服务网格接口(Service Mesh Interface,SMI)是针对在 Kubernetes 上运行的服务网格的规范。它定义了可以由各种提供商实施的通用标准。这样既可以实现最终用户的标准化,又可以实现服务网格技术提供商的创新。SMI 实现了灵活性和互操作性,并涵盖了最常见的服务网格功能。SMI 是服务网格规范的统称,实际上由四个组件组成:Traffic Access Control、Traffic Metrics、Traffic Specs 和 Traffic Split 。
SMI 在 2020 年 4 月以 sandbox 方式加入 CNCF,截至本文发出时其最新版本是 v0.6.0。
组件 | 版本 |
Traffic Access Control | v1alpha3 |
Traffic Metrics | v1alpha1 |
Traffic Specs | v1alpha4 |
Traffic Split | v1alpha4 |
目标
SMI 的目标是提供一套通用的、可移植的 Service Mesh API 集,Kubernetes 用户可以以与提供商无关的方式使用它们。这样,用户可以定义使用 Service Mesh 技术的应用程序,而不必与任何特定实现紧密绑定。将概念与实现隔离开来,像软件开发过去一直做的那样,将复杂的系统分层、抽象。
对于 SMI 项目来说,实现服务网格并不是其目标。它仅仅定义通用规范。同样,定义服务网格的含义范围不是目标,而是通常有用的子集。如果 SMI 提供程序想要添加超出 SMI 规范的提供程序特定的扩展和 API,并受到欢迎。可见,随着时间的推移,随着越来越多的功能作为服务网格的一部分而被普遍接受,这些定义将迁移到 SMI 规范中。
组件
下面结合osm[1] 项目中的 Bookstore Demo[2] 来介绍 SMI 的各个组件。
流量规格
API Group: specs.smi-spec.io API Version: v1alpha4
流量规格(Traffic Specs)定义了一组资源,用来描述流量的表现形式,与访问控制和其他策略结合来定义特定流量在服务网格中的行为。
在服务网格场景下,用户会使用多种不同的网络协议。目前主要是 HTTP,但是也会有其他协议。该规范中的每个资源都各自对应着一种协议。这就允许用户以特定协议的方式来定义流量。
流量规格的定义与任何应用程序或者其他资源无关,只是描述了经过网格的流量类型。通过被更高级的策略引用,比如访问控制、限流等。
值得注意的是,目前并没有关于 gRPC 的规格定义。
HTTPRouteGroup
这个资源用于描述 HTTP/1 和 HTTP/2 流量。它列出了应用程序可以服务的路由。
apiVersion: specs.smi-spec.io/v1alpha4
kind: HTTPRouteGroup
metadata:
name: bookstore-service-routes
namespace: bookstore
spec:
matches:
- name: books-bought
pathRegex: /books-bought
methods:
- GET
headers:
- "user-agent": ".*-http-client/*.*"
- "client-app": "bookbuyer"
- name: buy-a-book
pathRegex: ".*a-book.*new"
methods:
- GET
- name: update-books-bought
pathRegex: /update-books-bought
methods:
- POST
在上面这个示例中,我们为服务 bookstore
定义了名为 bookstore-service-routes
的 HTTPRouteGRoup
,其中有 3 个路由规则(match
):
•books-bought
:匹配请求头 client-app
为 bookbuyer 、user-agent
符合正则 .*-http-client/*.*
,使用 GET
方法访问 /books-bought
路径的流量。•buy-a-book
:匹配使用 GET
方法访问符合正则 .*a-book.*new
路径的流量。在 bookstore 服务中,只有一个路径符合:/buy-a-book/new
。•update-books-bought
:匹配使用 POST
方法访问 bookstore 服务的 /update-books-bought
路径。
HTTP 请求头过滤器
这里的 3 个路由规则(条件),实际上都是通过 HTTP 协议的请求头信息来进行判断。这就像是一个 HTTP 请求头的过滤器,过滤出符合条件的请求。再通过与其他 SMI 组件的组合使用,来进行流量的管理。比如与后面要提到 TrafficSplit
,来实现特定流量的转移。
请求头过滤器是一个键值对,键是 HTTP 请求头的名称,值便是该请求头匹配条件的正则表达式。
每个路由规则可以有多个请求头过滤器,通过 与 的方式组合;而多个路由规则之间则是 或 的方式。
Bookstore Demo 中的服务交互全都是基于 HTTP 协议的,而 SMI 的流量规范中还覆盖了其他协议。
TCPRoute
该资源用于描述一组4 层 TCP 端口的流量。
kind: TCPRoute
metadata:
name: the-routes
spec:
matches:
ports:
- 3306
- 6446
上面的例子中,匹配了端口 3306
和 6446
的流量。假如没有指定任何端口,则意味着匹配 Kubernetes service 的所有端口。
kind: TCPRoute
metadata:
name: the-routes
spec:
matches:
ports: []
UDPRoute
这个资源用于描述一组端口的 4层 UDP 流量,比如下面的定义中描述的是 989
、990
端口的 UDP 流量:
kind: UDPRoute
metadata:
name: the-routes
spec:
matches:
ports:
- 989
- 990
同样,假如不指定任何端口时会匹配所有 Kubernetes service 中的端口。
访问控制
Traffic Access Control 提供了用于定义应用访问控制策略的一组资源,在认证授权中属于授权的部分。而身份验证由底层实现处理并表示。
访问控制在 SMI 规范中使用加法的策略,默认情况下所有的流量都是被拒绝的。
API Group: access.smi-spec.io API Version: v1alpha3 Compatible with: specs.smi-spec.io/v1alpha4
TrafficTarget
TrafficTarget
将流量定义(规则)和用于定位 Pod 的身份标识关联起来。访问控制是通过引用的流量规格和源服务身份标识列表来实现的。
如果 Pod 的身份标识在列表中,该 Pod 向目标服务的某一个已定义的路由发起的请求是被允许的。假如 Pod 的身份标识不在列表中,或者访问的路由不在引用的流量规格的路由列表中,请求会被拒绝。
服务的身份标识,是通过 Kubernetes 的 service account 体现的。运行 Pod 时会为其指定 service account,这个 service acount 便是运行在 Pod 中的进程的身份标识,也是 Pod 的身份标识。
一个有效的 TrafficTarget
必须有一个目标、至少一个规则和至少一个源。
** 7 层的访问控制:**
kind: TrafficTarget
apiVersion: access.smi-spec.io/v1alpha3
metadata:
name: bookbuyer-access-bookstore-v1
namespace: bookstore
spec:
destination:
kind: ServiceAccount
name: bookstore-v1
namespace: bookstore
rules:
- kind: HTTPRouteGroup
name: bookstore-service-routes
matches:
- buy-a-book
- books-bought
sources:
- kind: ServiceAccount
name: bookbuyer
namespace: bookbuyer
在这个示例,定义了这样的访问策略:bookbuyer 命名空间下使用 service account bookbuyer 运行的 Pod(源)可以访问 bookstore
命名空间下使用 service account bookstore-v1 运行的 Pod (目标)的部分路由(路由规则),这些路由就是 bookstore-service-routes
HTTPRouteGroup
下的 buy-a-book 和 books-bought。而 bookstore 的其他路由,包括 bookstore-service-routes
中定义的路由 update-books-bought 的访问都会被拒绝。
用表格直观的表示就是:
源 | 目标 | 路径 | 请求头 | 方法 |
bookbuyer/bookbuyer | bookstore/bookstore | .*a-book.*new | * | GET |
bookbuyer/bookbuyer | bookstore/bookstore | /books-bought | user-agent: .-http-client/.*; client-app: bookbuyer | GET |
** 4 层的访问控制:**
kind: TCPRoute
metadata:
name: tcp-ports
spec:
matches:
ports:
- 8301
- 8302
- 8300
---
kind: UDPRoute
metadata:
name: udp-ports
spec:
matches:
ports:
- 8301
- 8302
---
kind: TrafficTarget
metadata:
name: protocal-specific
spec:
destination:
kind: ServiceAccount
name: server
namespace: default
rules:
- kind: TCPRoute
name: tcp-ports
- kind: UDPRoute
name: udp-ports
sources:
- kind: ServiceAccount
name: client
namespace: default
同样也可以做 4 层的访问控制,比如上面的示例中展示了:在 default 命名空间下,使用 service account client 运行的 Pod 允许访问使用 service account server 的8301
、8302
和 8300
端口。8301
、8302
允许 TCP、UDP 的流量;而 8300
只允许 TCP 的流量,UDP 的流量都会被拦截。
流量分流
API Group: split.smi-spec.io API Version: v1alpha4 Compatible with: specs.smi-spec.io/v1alpha4
这个规范中定义了资源 TrafficSplit
,用来在多个服务间调整流量的百分比。其属于客户端的策略,将出站流量发送给不同的目标。通过 TrafficSplit
可以实现应用新版本的金丝雀发布,也是服务网格中最重要的功能。
这个规范将一个根服务与多个后端服务关联起来,。这个根服务 spec.service
是用来进行交互的 FQDN(完全限定域名),假如没有 sidecar 代理,客户端同样可以通过其访问目标服务。
TrafficSplit
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: bookstore-split
namespace: bookstore
spec:
service: bookstore
backends:
- service: bookstore-v1
weight: 50
- service: bookstore-v2
weight: 50
在 Bookstore demo 中,有 1 个名为 bookstore 的 Kubernetes service 作为 TrafficSplit
的根服务,其他的服务(如 bookbuyer)通过 bookstore.bookstore.svc.cluster.local:14001
来访问 bookstore 的路由。
同时还部署了 bookstore-v1 和 bookstore-v2 两个 Deployment,以及同名的 service。这两个 service 便作为根服务的后端。
从 上面 TrafficSplit
来看访问 bookstore 的流量,按照 1:1 的比例分发给 bookstore-v1 和 bookstore-v2 两个后端(实际每个后端都可以有多个副本)。请求的分发首先是在 service 之间按照权重进行分流,具体到特定的 service 之后便是在该 service 的多个副本之间均衡(Deployment 的多个 Pod 之间权重相同)。
这里示例将所有访问 bookstore 的请求无差别地进行分流,即所有的路由都是按照权重进行分流。TrafficSplit
支持更高级的分流,可以为不同路由指定分流的方式。我们可以通过 matches
字段指定需要进行分流的路由,使用新增的 canary-test HTTPRouteGroup
:
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: bookstore-canary
namespace: bookstore
spec:
service: bookstore
backends:
- service: bookstore-v2
weight: 1
matches:
- kind: HTTPRouteGroup
name: canary-test
---
kind: HTTPRouteGroup
metadata:
name: canary-test
matches:
- name: buy-a-book
pathRegex: ".*a-book.*new"
上面我们增加了一个新的 TrafficSplit
定义 bookstore-canary,将所有访问 bookstore 的 /buy-a-book/new
请求全都分流到 bookstore-v2。
金丝雀发布流程
假如现在 bookstore 运行的是 v1 的版本,系统中有如下资源:
•有着app: bookstore
、version: v1
标签的 Deployment bookstore-v1•名为 bookstore 的 service,使用选择器 app: bookstore
•名为 bookstore-v1 的 service,使用选择器 app: bookstore
和 version: v1
•客户端(如 bookbuyer)通过 FQDN bookstore
来访问 bookstore 服务
即将要发布的版本 v2,为了降低发布的风险我们需要逐步地将流量发送给新的版本来验证功能。
•发布 v2 之前,需要先创建一个名为 bookstore-rollout 的 TrafficSplit
:
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: bookstore-rollout
namespace: bookstore
spec:
service: bookstore
backends:
- service: bookstore-v1
weight: 100
- service: bookstore-v2
weight: 0
•发布 v2 版本的 bookstore,创建 Deployment bookstore-v2,使用标签 app: bookstore
、version: v2
•创建名为 bookstore-v2 的 service,使用选择器 app: bookstore
和 version: v2
此时 bookstore-v2 还不会获得任何流量。
•待 bookstore-v2 成功启动之后,调整 bookstore-rollout 中 bookstore-v2 的权重
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: bookstore-rollout
namespace: bookstore
spec:
service: bookstore
backends:
- service: bookstore-v1
weight: 90
- service: bookstore-v2
weight: 10
•验证 v2 版本的功能,并逐步增加 bookstore-v2 的权重,减少 bookstore-v1 的权重。•直至所有的流量都被发送 v2
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: bookstore-rollout
namespace: bookstore
spec:
service: bookstore
backends:
- service: bookstore-v1
weight: 0
- service: bookstore-v2
weight: 100
•功能验收完成,删除 bookstore-v1 的 Deployment 和 Service•删除 bookstore-rollout TrafficSplit
这样我们便完成了 bookstore 的版本升级,这是最简单的金丝雀发布用来展示如何使用 TrafficSplit
来实现流量可控的滚动升级。现实场景下,还需要结合路由来针对特定的流量进行分流,这种方式更加高级且高效。
可能会有同学提出疑问,为什么在发布 v2 之前要先创建 TrafficSplit
?这是因为 service bookstore
的标签选择器,可以选择 v1 和 v2 的 Pod。如果不先配置分流,Kubernetes service 会将流量发送给 v2 的 Pod。
流量指标
API Group: metrics.smi-spec.io API Version: v1alpha1
该规范描述了一种资源,该资源为消费 HTTP 流量相关指标的工具提供了一个通用的集成点。在 CLI 工具、HPA 缩放、自动金丝雀更新所使用的即时指标上,它遵循 metrics.k8s.io
模式。很多实现都是使用 Prometheus 做存储,这里仅标准化了指标/标签命名。
指标是与资源关联的,这种资源可以是 pod、namespace、deployment 或者 service。所有指标都与生成或服务于测量流量的 Kubernetes 资源相关联。Pod 是其中最小粒度,通常都会将 pod 的指标聚合后作为整个应用的指标。比如,在金丝雀发布时将 deployment 的成功率指标进行聚合。
除了资源以外,指标还包括了 edges,其表示流量的源头和目的地。edge 限制了指标是 resource 和 edge.resource 间的流量。
通过 API 来获取指标的方法有如下几种:
•支持的资源(pod、namespace、deployment 等)作为 APIResourceList
的一部分,支持 list 和 get 操作。•对于支持的资源,可以使用标签选择器来过滤。•子资源允许查询与特定资源关联的所有 edges。
TrafficMetrics
kind: TrafficMetrics
# See ObjectReference v1 core for full spec
resource:
name: foo-775b9cbd88-ntxsl
namespace: foobar
kind: Pod
edge:
direction: to
side: client
resource:
name: baz-577db7d977-lsk2q
namespace: foobar
kind: Pod
timestamp: 2019-04-08T22:25:55Z
window: 30s
metrics:
- name: p99_response_latency
unit: seconds
value: 10m
- name: p90_response_latency
unit: seconds
value: 10m
- name: p50_response_latency
unit: seconds
value: 10m
- name: success_count
value: 100
- name: failure_count
value: 100
在上面的示例中,展示了从 pod foo-775b9cbd88-ntxsl 到 pod baz-577db7d977-lsk2q 的延迟百分比分布、成功/失败数指标。
注意:延迟的默认单位是秒,值10m
表示0.1
,即 0.1 秒。
TrafficMetricsList
TrafficMetricsList
展示的是与指定资源相关的一组 TrafficMetrics
。比如,通过类型来查询所有 Pod 相关的指标:
kind: TrafficMetricsList
resource:
kind: Pod
items:
...
或者在类型之上,通过标签选择器对 Pod 进行过滤:
kind: TrafficMetricsList
resource:
kind: Pod
selector:
matchLabels:
app: foo
items:
...
Kubernetes API
通过 APIService
暴露 metrics.smi-spec.io
API。
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1alpha1.metrics.smi-spec.io
spec:
group: metrics.smi-spec.io/v1alpha1
service:
name: mesh-metrics
namespace: default
version: v1alpha1
使用场景:自动金丝雀发布
创建一个金丝雀发布的控制器,可以执行前面提到的“金丝雀发布流程”。
唯一不同的是,控制器会通过 metrics.smi-spec.io
API 监控新版本的成功率指标,并自动增加新版本的权重占比。此外,新版本 deployment 的部署和旧版本 deployment 的下线,都是由控制器自动完成的。
实现示例
与很多 metrics 指标监控一样,Prometheus 作为指标的存储,会周期性地从网格 sidecar 拉取指标。途中唯一需要实现的是 Traffic Metrics Shim,它的作用是将 Kubernetes 原生 API 映射到 Prometheus 的查询(PromSQL)。这是服务网格的实现细节。
1.发起 metrics.smi-spec.io
API 的查询请求:
kubectl get --raw /apis/metrics.smi-spec.io/v1alpha1/namespaces/default/deployments/
1.Kubernetes API Server 将请求转发给 Traffic Metrics Shim
。2.Shim 会将请求映射成 Prometheus 的查询,这里可能会转换成多个 Prometheus 请求:
sum(requests_total{namespace='default',kind='deployment'}) by (name, success)
1.收到 Prometheus 的查询结果后,shim 会将内容转换成 TrafficMetrics 对象。
总结
SMI 的出现,为“混乱的”服务网格生态提供了规范性的指引。用户可以在不同实现间低成本地切换,避免厂商绑定。
接下来,在下一篇中我们会介绍 SMI 的实现之一:开放服务网格(Open Service Mesh, OSM)。
OSM 是一个轻量级可扩展的云原生服务网格,是简单、完整且独立的服务网格解决方案,它允许用户统一管理、保护并获得针对高度动态微服务环境的开箱即用的可观察性功能。OSM 运行在 Kubernetes 之上控制平面实现了 xDS API 并配置了 SMI API,为应用程序注入 Envoy sidecar 容器作为代理,通过 SMI Spec 来引用网格中的服务。
虽然默认情况下使用 Envoy 作为数据平面,但是设计上提供接口的抽象,支持兼容 xDS 的代理,甚至其他的代理。
目前 OSM 最新版本对 SMI 的支持如下:
类型 | SMI 资源 | 支持的版本 | 备注 |
TrafficTarget | traffictargets.access.smi-spec.io | v1alpha3[3] | |
HTTPRouteGroup | httproutegroups.specs.smi-spec.io | v1alpha4[4] | |
TCPRoute | tcproutes.specs.smi-spec.io | v1alpha4[5] | |
UDPRoute | udproutes.specs.smi-spec.io | not supported | |
TrafficSplit | trafficsplits.split.smi-spec.io | v1alpha2[6] | |
TrafficMetrics | *.metrics.smi-spec.io | v1alpha1[7] | 🚧 In Progress 🚧 |
引用链接
[1]
osm: https://github.com/openservicemesh/osm[2]
Bookstore Demo: https://github.com/openservicemesh/osm/tree/main/demo/cmd[3]
v1alpha3: https://github.com/servicemeshinterface/smi-spec/blob/v0.6.0/apis/traffic-access/v1alpha3/traffic-access.md[4]
v1alpha4: https://github.com/servicemeshinterface/smi-spec/blob/v0.6.0/apis/traffic-specs/v1alpha4/traffic-specs.md#httproutegroup[5]
v1alpha4: https://github.com/servicemeshinterface/smi-spec/blob/v0.6.0/apis/traffic-specs/v1alpha4/traffic-specs.md#tcproute[6]
v1alpha2: https://github.com/servicemeshinterface/smi-spec/blob/v0.6.0/apis/traffic-split/v1alpha2/traffic-split.md[7]
v1alpha1: https://github.com/servicemeshinterface/smi-spec/blob/v0.6.0/apis/traffic-metrics/v1alpha1/traffic-metrics.md
原文始发于微信公众号(Flomesh):服务网格接口 SMI 规范解读
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/59596.html