SMI 与 Gateway API 的 GAMMA 倡议意味着什么?
就在上周 Gateway API 发布版本 0.5.0,其中几个最重要的 CRD Gateway
、GatewayClass
以及 HTTPRoute
第一次升级到了 beta 版本。升级的详细内容这里不做详谈,我也想说说的是与版本一同发布的,也是很容易被忽略的社区合作倡议 GAMMA。
GAMMA 是 Gateway API Mesh Management and Administration 的简写,这个计划是 Gateway API 子项目中的一个专用工作流。这个小组的目标是调研、设计和跟踪 Gateway API 资源、语义和其他与服务网格技术和用例相关的组件。此外,小组还努力倡导服务网格项目的 Gateway API 实现之间的一致性,无论使用何种技术栈或者代理。
可能你会有疑问如此简单一个倡议,有什么特别?有两点:
- 规范的参与:这个倡议由 SIG Network(Gateway API 所在的 Kubernetes SIG)与服务网格社区共同发起,服务网格社区有来自 Cilium Service Mesh、Consul、Istio、Kuma、Linkerd、SMI、NGINX Service Mesh 和 Open Service Mesh 的代表。SMI 与其他几个不同,它是服务网格的规范 API,并非是实现,Gateway API 也一样。
- 面向东西向流量:小组的首个工作探索使用 Gateway API 处理东西向流量已经开始。东西向流量,也就是服务网格中服务到服务的流量。
可见,服务网格部分 API 的统一将迈进一步,且成为 Kubernetes 原生 API 的一员。当然现在还言之过早,还需看各个厂商的跟进程度。
接下来,我们分别从规范,以及当前的实现两个角度进行分析。
规范
SMI 与 Gateway API 一样都是规范,前者用于服务网格,而后者用于网关。但二者在流量规范的部分存在明显重叠(见下图中红色的部分),流量规范可以可以理解为流量标识(traffic identify)。流量标识作为流量管理的关键,将流量标识之后才能进行流量路由、分流、限流等操作。
在 SMI issue 249 发起了讨论,关于 SMI 能否使用 Gateway API 的流量规范,将精力集中在服务网格的规范中。
只能说 Gateway API 作为 Kubernetes 的原生 API,有着更强的说服力,其他服务网格厂商更容易接纳。
已经对 SMI 和 Gateway API 有了解的同学,可以直接跳过规范的介绍。
服务网格的规范 SMI
SMI(Service Mesh Interface) 首次登场在 2019 年,并于 2020 年加入 CNCF。
在开放服务网格 Open Service Mesh 如何开放一文中曾介绍过 SMI,有兴趣的可以浏览各个 API 的说明及示例。
SMI 是服务网格的规范,重点关注在 Kubernetes 上运行的服务网格。它定义了一个可以由各种供应商使用的通用标准。允许最终用户的标准化和服务网格技术提供商的创新。SMI 实现了灵活性和互操作性,并涵盖了最常见的服务网格功能。
SMI 提供了 Kubernetes 服务网格的标准接口、常见服务网格场景的基本功能集、支持服务网格新功能的灵活性,以及服务网格技术生态的创新空间。
SMI 的目标是将概念与实现隔离开来,像软件开发过去一直做的那样,将复杂的东西分层抽象。
SMI 规范的最新版本是 0.6.0,覆盖了常见的流量访问控制、遥测、管理等基础服务网格能力。与规范对应的是 API,各个网格供应商基于该 API 进行实现。
Kubernetes Gateway API
Kubernetes Gateway API是由SIG-NETWORK 社区管理的开源项目,是一种规范,不提供实现。Gateway API 被认为是 Kubernetes Ingress API 的继任者,在 2019 年的Ingress 革命中首次被提出,并在 2020 年 11 月发布了第一个版本。
Gateway API 是一系列资源的集合,相比 Ingress API,Gateway API 最大的进步就是将流量规范独立成 API,大大提升了扩展性和灵活性。目前提供了 HTTPRoute
、TCPRoute
、UDPRoute
和 TLSRoute
规范来匹配特定协议的规则。
流量规范独立之后,网关的实现、基础设施的管理以及流量路由的定义得到了解耦合,这得益于 Gateway API 面向角色的 API 设计:
- 厂商实现了Gateway API 并定义了自己
GatewayClass
类型,一系列的实现可供选择。 - 集群管理员安装 Gateway API 的实现,部署跨命名空间的共享网关实例,或者命名空间独享的网关实例。如下图,集群管理员部署了跨
store
和site
命名空间的网关foo
。 - 开发人员创建
HTTPRoute
资源将流量路由到指定的后端服务。
现有的流量规范
这里主要讨论的是服务网格中所面对的东西向流量,部分代码片段来自探索使用 Gateway API 处理东西向流量。
从代码片段不难看出,几乎各家都有各自特别的实现。
SMI TrafficSplit
SMI 的 TrafficSplit
定义了流量与后端服务(service)的映射关系(路由)。通过 service
的 ClusterIP
进行流量的匹配,然后进一步使用 HTTPRouteGroup
对 HTTP 流量进行匹配。并且,服务的映射限制在同一个命名空间下。
比如下面示例将访问 service
website
的全部 Firefox 流量路由到 website-v2
服务。
SMI 的实现 osm 会穷举出 spec.service
上指定的 service
的所有 FQDN。
kind: TrafficSplit
metadata:
name: ab-test
spec:
service: website
matches:
- kind: HTTPRouteGroup
name: ab-test
backends:
- service: website-v1
weight: 0
- service: website-v2
weight: 100
---
kind: HTTPRouteGroup
metadata:
name: ab-test
matches:
- name: firefox-users
headers:
user-agent: ".*Firefox.*"
Istio VirtualService
VirtualService
中的流量匹配使用了 hosts
,进一步通过一些基于协议的规则进行匹配。不同的是,协议规则的匹配不是通过独立的 CRD 来维护的,而是耦合在 VirtualService
中。
spec:
hosts:
- reviews.prod.svc.cluster.local # could also be just "reviews"
http:
- match:
- uri:
prefix: "/frontpage"
route:
- destination:
host: frontpage.prod.svc.cluster.local
Kuma TrafficRoute
从配置上可以看出流量的匹配是通过 service
的 ClusterIP
实现的。
spec:
sources:
- match:
kuma.io/service: backend_default_svc_80
destinations:
- match:
kuma.io/service: redis_default_svc_6379
conf:
http:
- match: …
Cilium CiliumEnvoyConfig
与 Kuma 一样,通过 service
的 ClusterIP
进行流量匹配。但不同的是,提供了跨命名空间的流量路由。
spec:
services:
- name: httpbin
namespace: default
listener: envoy-lb-listener
backendServices:
- name: echo
namespace: default
resources: …
Consul ServiceRouter
使用 ServiceRouter
资源名(web
)作为 service
名,然后通过其 ClusterIP
进行流量匹配。
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceRouter
metadata:
name: web
spec:
routes:
- match:
http:
pathPrefix: /admin
destination:
service: admin
Linkerd ServiceProfile
ServiceProfile
将名字与请求的 Host
头进行匹配。然后 routes
下挂不同的 endpoint 定义(如 /admin
、/store
),在 endpoint 的粒度上进行 HTTP 协议属性的匹配、超时、重试等策略的配置。
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: webapp.my-service-namespace.svc.cluster.local
spec:
routes: …
未来的设计
在看未来的可能设计之前,先看下 Kubernetes Gateway API 如何完成流量匹配的:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: my-route
namespace: gateway-api-example-ns2
spec:
parentRefs:
- kind: Gateway
name: foo-gateway
namespace: gateway-api-example-ns1
hostnames:
- foo.example.com
rules:
- backendRefs:
- name: foo-svc
port: 8080
从 HTTPRoute
的定义来看,存在着两个映射(这里的映射,也可以看做附着点):
- 与网关实现(
foo-gateway.gateway-api-example-ns1
)的映射(通过parentRefs
实现),也就是说将流量匹配规则写入到哪个网关实例。 - 与服务(
foo-svc:8080
)的映射(通过hostnames
实现)。
通过 HTTPRoute
上的附着点,将通过网关实现进入的流量,与后端服务进行关联。先通过 spec.hostnames
进行流量的匹配,然后再由 spec.rules
中的匹配规则完成协议属性的匹配。HTTP 协议可以使用 method
、path
、参数、请求头进行匹配,完成 7 层的路由。
东西向流量
在东西向流量的场景,需要实现:
- 与网格实现的映射,将流量匹配规则写入到某种网格的 sidecar 中。
- 与服务的映射,如何将进入到网格 sidecar 流量与目标服务(网格内的服务,或者网格外的服务)进行匹配。
因此需要一种设计或者 CRD 来承载如上的关系,截止发稿时目前在探索使用 Gateway API 处理东西向流量中已经有 5 种方式来实现,有兴趣的同学可以了解一下。
总结
还是那句话,最终会使用何种方式,都为时尚早。
但已知的是,目前社区也意识到服务网格是实现上的分裂,而未来服务网格的标准有可能全部或者部分以 Kubernetes 原生 API 的方式存在。如果是后者,我认为 SMI 会有走出来的可能。正如在开放服务网格 Open Service Mesh 如何开放中,谈及较多的标准与开放关乎着生态的发展、创新、扩展性和灵活性。