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 拆分为多个应用程序,因此您需要将组件的职责拆分为子组件。另一个常见的任务是支持重构;也许您已经重写了应用程序的一部分,并且您现在希望单独部署它。
例如,假设您有一个商店,其中包含产品列表及其详细信息,并且您需要将其拆分为 shop 和 product 后端应用程序。在 /,您的商店显示产品列表,在 /details,它显示产品详细信息,例如颜色、尺寸、可持续性和价格。

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

图 2:两个后端应用程序,product 和 shop
为了确保 HTTP 代理为传入请求找到正确的后端,它使用路由表来检查目标以确保其正确。
路由表
在 Skipper 中,路由表是通过从不同来源提取由 dataclients 生成的信息来创建的。一个来源可以是 路由文件,类似于您在更流行的 HTTP 服务器(如 Apache 或 Nginx)中可能看到的。
根据您组织的规模——或者更好的是,后端应用程序的数量——路由表可能会变得非常大。Skipper 将路由表实现为树,可以扩展到超过 600,000 条路由(远远超出您希望在 Nginx 或 Apache 配置中管理的数量)。
继续上面的示例应用程序,表 1 显示了来自 图 2 的路由表。商店 / 应该路由到 shop,,而 /detail 路由到 product 应用程序。
路径 | 应用 |
---|---|
/ | / |
/detail | product |
表 1:路由表
Skipper 中可用的 dataclients 从不同的来源获取路由以及路由的组成。
Dataclient
Skipper 的路由文件 dataclient 中的路由配置类似于您可能从 Nginx 或 Apache 中的 HTTP 代理中了解到的。在 Skipper 中,路由文件以 eskip 语法指定所有路由,如图 3 所示。
r1: P1() && P2() && .. && PN()
-> f1()
-> f2()
...
-> fN()
-> <backend>;
r2: ...
...
图 3:eskip 中的路由文件
在上面
- r1, r2, ... 是唯一的 routeID。
- P1, P2,..,PN 是定义匹配的谓词。
- f1, f2,..,fN 是在选择路由后应用的过滤器。过滤器可以更改请求和响应。
- 最后,定义了 Skipper 后端。这可以是单个 URL、负载均衡的 URL 列表,以及其他用于特殊情况的 URL,例如 直接响应。
路由字符串 是另一个 dataclient,它对于测试非常方便。例如,如果您需要一个伪后端来用于您的演示,该演示使用 HTML 回复绿色背景,您可以使用
$ skipper -routes-string='*
-> inlineContent(
"<html><body style=\"background-color: green;\"></body></html>"
)'
到目前为止,Skipper 最受欢迎的 dataclient 是 Kubernetes dataclient,它用于从 Kubernetes API 服务器 获取信息,并从 Skipper Ingress 资源和 RouteGroup 自定义资源定义 (CRD) 创建路由表。
总结以上内容,dataclients 从不同的提供程序获取信息以构建 Skipper 的路由表。表 1 显示了 shop/description 示例的路由表,而 Skipper 使用谓词来选择要处理请求的路由。
谓词
在 Skipper 中,传入的请求与所有路由的 谓词 匹配,以找到与传入请求最佳匹配的路由。谓词是基于传入请求进行匹配的函数。在 图 2 和表 1 的示例中,Skipper 将具有类似于图 4 的路由表
shop: Path("/")
-> "https://shop.zalando";
product: Path("/detail")
-> "https://product.zalando";
图 4:Skipper 路由表
这意味着路径为 / 的 HTTP 请求将与 Path("/") 谓词匹配,这样 Skipper 将执行 shop 路由。路径为 /detail 的请求将与 Path("/detail") 匹配并路由到 product 应用程序。
通常,路由行为可以通过谓词更改。您可以选择许多谓词。例如,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 条评论