Skipper 是一款开源 HTTP 路由器和反向代理,用于服务组合。正如其 GitHub 页面 所述,它旨在处理大量动态配置的 HTTP 路由定义(>600,000 条路由),具有详细的查找条件和通过过滤器对请求流进行灵活增强。它可以开箱即用,也可以通过自定义查找、过滤器逻辑和配置源进行扩展。
代理
当一些人想到代理时,他们会想象一个作为内网网关的网页,或者一个看起来可疑的网页,旨在解锁学校或工作网络上的社交媒体网站。前向代理是桌面基础设施运营商用来节省互联网带宽、实施家长控制或限制社交媒体访问的代理。另一种代理是个人用户导航到一个页面,提供凭据,然后被转发到受保护的内网资源。这种代理的反向代理是反向代理,它接受所有流量并将其转发到特定资源,例如服务器或容器。这就是 Skipper 为基础设施所做的工作。
当我阅读 Matt Klein 的文章,关于现代网络负载均衡和代理时,我意识到作为 Skipper 的维护者,我们应该解释更多关于为什么以及如何利用 HTTP 代理的功能和细节。在本文中,我将把术语“HTTP (反向) 代理”和“HTTP 路由器”视为相同。
HTTP 路由
根据 维基百科:“路由是在网络中为流量选择路径的过程。” 此定义指的是 OSI 第 3 层 的路由,最常见的是基于 IP 以及路由协议,如 BGP 或 OSPF。由于本文不是关于这些协议的,我将尝试解释 HTTP 路由器的作用。但首先,我想介绍 Skipper,一个用 Go 编写的 OSI 第 7 层 HTTP 路由器库,它是零售商 Zalando 的电子商务商店和 Kubernetes Ingress 基础设施的核心组件。
在 Zalando,我们使用 Skipper 作为 Kubernetes Ingress 控制器,为我们的用户提供可见性、可靠性、安全性以及卸载常用应用程序的附加功能。
任何运行 HTTP 服务的组织,通常在微服务架构中,都需要将 HTTP 请求路由到正确的应用程序。HTTP 路由器根据 HTTP 请求提供的信息进行路由。例如,以下显示了一个 HTTP/1.1 请求。
GET /details HTTP/1.1
Host: www.zalando.de
User-Agent: curl/7.49.0
Accept: */*
Authorization: Bearer <token>
...
我们可以根据方法 GET、路径 /details、Host 标头 www.zalando.de 或请求的任何任意部分进行路由。
应用程序所有者面临的一个常见问题是将 API 拆分为多个应用程序,因此您需要将组件的职责拆分为子组件。另一个常见的任务是支持重构;也许您已经重写了应用程序的某个部分,并且现在想要单独部署它。
例如,假设您有一个商店,其中包含产品列表及其详细信息,并且您需要将其拆分为商店和产品后端应用程序。在 /,您的商店显示产品列表,在 /details,它显示产品详细信息,例如颜色、尺寸、可持续性和价格。

图 1:商店
您需要将产品详细信息的职责拆分到其自己的应用程序中,以便 / 保留在商店应用程序中,而 /details 被重构到产品应用程序中。

图 2:两个后端应用程序,产品和商店
为了确保 HTTP 代理为传入请求找到正确的后端,它使用路由表来检查目标以确保其正确。
路由表
在 Skipper 中,路由表是通过从不同来源提取由 数据客户端 生成的信息来创建的。一个来源可以是 路由文件,类似于您在更流行的 HTTP 服务器(如 Apache 或 Nginx)中可能看到的。
根据您组织的大小——或者更好的是,后端应用程序的数量——路由表可能会变得非常大。Skipper 将路由表实现为一棵树,可以扩展到 600,000 条以上的路由(远远超过您想要在 Nginx 或 Apache 配置中管理的数量)。
继续上面的示例应用程序,表 1 显示了 图 2 中的路由表。商店 / 应路由到 shop,而 /detail 路由到 product 应用程序。
路径 | 应用 |
---|---|
/ | shop |
/detail | product |
表 1:路由表
Skipper 中可用的数据客户端从不同的来源获取路由以及路由的组成。
数据客户端
Skipper 的路由文件中的路由配置 数据客户端 类似于您可能从 Nginx 或 Apache 中的 HTTP 代理了解到的。在 Skipper 中,路由文件以 eskip 语法指定所有路由,如图 3 所示。
r1: P1() && P2() && .. && PN()
-> f1()
-> f2()
...
-> fN()
-> <backend>;
r2: ...
...
图 3:eskip 中的路由文件
在上面
- r1, r2, ... 是唯一的路由 ID。
- P1, P2,..,PN 是定义匹配的谓词。
- f1, f2,..,fN 是在选择路由后应用的过滤器。过滤器可以更改请求和响应。
- 最后,定义了 Skipper 后端。这可以是单个 URL、负载均衡的 URL 列表,以及其他用于特殊情况的 URL,例如 直接响应。
路由字符串 是另一个数据客户端,它对于测试非常方便。例如,如果您需要一个伪后端用于您的演示,该后端回复带有 HTML 的绿色背景,您可以使用
$ skipper -routes-string='*
-> inlineContent(
"<html><body style=\"background-color: green;\"></body></html>"
)'
到目前为止,Skipper 最流行的数据客户端是 Kubernetes 数据客户端,它用于从 Kubernetes API 服务器 获取信息,并从 Skipper Ingress 资源和 RouteGroup 自定义资源定义 (CRD) 创建路由表。
总结以上内容,数据客户端从不同的提供程序获取信息以构建 Skipper 的路由表。表 1 显示了商店/描述示例的路由表,Skipper 使用谓词来选择路由以处理请求。
谓词
在 Skipper 中,传入的请求与所有路由的 谓词 匹配,以找到与传入请求最佳匹配的路由。谓词是基于传入请求进行匹配的函数。在 图 2 和表 1 的示例中,Skipper 将具有类似于图 4 的路由表
shop: Path("/")
-> "https://shop.zalando";
product: Path("/detail")
-> "https://product.zalando";
图 4:Skipper 路由表
这意味着路径为 / 的 HTTP 请求将与 Path("/") 谓词匹配,这样 Skipper 将执行商店路由。路径为 /detail 的请求将与 Path("/detail") 匹配并路由到产品应用程序。
一般来说,路由行为可以通过谓词来改变。您可以选择很多谓词。例如,Method("POST") 仅在传递 POST 请求时才为真。具有更多谓词的路由被认为更具体。此外,具有更多谓词的路由在路由选择中比谓词较少的路由具有更高的权重。
特殊情况是 Path() 和 PathSubtree(),它们首先在树中匹配,并减少作为列表扫描的路由数量。例如,图 5 所示的树结构有助于将路由数量扩展到 Zalando 的生产设置之一中的 600,000 多个。

图 5:树结构
过滤器
选择路由后,将应用请求 过滤器。过滤器作用于请求或响应;它们可以更改发送到后端的传入请求,并且可以更改发送给客户端的响应。
例如,setRequestHeader("Foo", "bar") 将 HTTP 标头 "Foo" 设置为值 "bar",以便后端在请求中看到此标头。
响应过滤器 responseCookie("keks", "val", 3600) 在发送给调用者的响应中设置一个名为 "keks" 的 Cookie,在这种情况下调用者可能是浏览器。该 cookie 的值为 "val",有效期为一小时。
一个作用于请求和响应的过滤器是 enableAccessLog(40, 5)。这将为来自后端的、状态代码为 40x 或 5xx 的所有响应执行访问日志记录。
正如您从示例中看到的那样,过滤器可以更改请求或响应,或者只是基于它做一些工作。另一个过滤器示例是身份验证过滤器或速率限制。如果请求不应被允许通过,这些过滤器将阻止请求传递到后端。例如,要从名为 /var/www 的目录提供静态内容,您可以使用过滤器 static("/var/www")。
了解更多
本文提供了 Skipper 及其功能的基本概述。有关更多信息,请查阅 Skipper 的文档,请在评论中分享您的问题或反馈。
6 条评论