SMI 与 Gateway API 的 GAMMA 倡议意味着什么?

SMI 与 Gateway API 的 GAMMA 倡议意味着什么?

就在上周 Gateway API 发布版本 0.5.0,其中几个最重要的 CRD GatewayGatewayClass 以及 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 进行实现。

SMI Spec

Kubernetes Gateway API

Kubernetes Gateway API是由SIG-NETWORK 社区管理的开源项目,是一种规范,不提供实现。Gateway API 被认为是 Kubernetes Ingress API 的继任者,在 2019 年的Ingress 革命中首次被提出,并在 2020 年 11 月发布了第一个版本。

Gateway API Spec

Gateway API 是一系列资源的集合,相比 Ingress API,Gateway API 最大的进步就是将流量规范独立成 API,大大提升了扩展性和灵活性。目前提供了 HTTPRouteTCPRouteUDPRouteTLSRoute 规范来匹配特定协议的规则。

流量规范独立之后,网关的实现、基础设施的管理以及流量路由的定义得到了解耦合,这得益于 Gateway API 面向角色的 API 设计:

  • 厂商实现了Gateway API 并定义了自己 GatewayClass 类型,一系列的实现可供选择。
  • 集群管理员安装 Gateway API 的实现,部署跨命名空间的共享网关实例,或者命名空间独享的网关实例。如下图,集群管理员部署了跨 storesite 命名空间的网关 foo
  • 开发人员创建 HTTPRoute 资源将流量路由到指定的后端服务。

Role Oriented

现有的流量规范

这里主要讨论的是服务网格中所面对的东西向流量,部分代码片段来自探索使用 Gateway API 处理东西向流量

从代码片段不难看出,几乎各家都有各自特别的实现。

SMI TrafficSplit

SMI 的 TrafficSplit 定义了流量与后端服务(service)的映射关系(路由)。通过 serviceClusterIP 进行流量的匹配,然后进一步使用 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

从配置上可以看出流量的匹配是通过 serviceClusterIP 实现的。

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 一样,通过 serviceClusterIP 进行流量匹配。但不同的是,提供了跨命名空间的流量路由。

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 协议可以使用 methodpath、参数、请求头进行匹配,完成 7 层的路由。

东西向流量

在东西向流量的场景,需要实现:

  • 网格实现的映射,将流量匹配规则写入到某种网格的 sidecar 中。
  • 服务的映射,如何将进入到网格 sidecar 流量与目标服务(网格内的服务,或者网格外的服务)进行匹配。

因此需要一种设计或者 CRD 来承载如上的关系,截止发稿时目前在探索使用 Gateway API 处理东西向流量中已经有 5 种方式来实现,有兴趣的同学可以了解一下。

总结

还是那句话,最终会使用何种方式,都为时尚早。

但已知的是,目前社区也意识到服务网格是实现上的分裂,而未来服务网格的标准有可能全部或者部分以 Kubernetes 原生 API 的方式存在。如果是后者,我认为 SMI 会有走出来的可能。正如在开放服务网格 Open Service Mesh 如何开放中,谈及较多的标准开放关乎着生态的发展、创新、扩展性和灵活性。

comments powered by Disqus