作者:潘梦源
前言
Kruise Rollout [ 1] 是 OpenKruise 社区开源的渐进式交付框架。Kruise Rollout 反对配合流量和实例灰度的金丝雀公布、蓝绿公布、A/B Testing 公布,以及公布过程可能基于 Prometheus Metrics 指标自动化分批与暂停,并提供旁路的无感对接、兼容已有的多种工作负载(Deployment、CloneSet、DaemonSet)。
目前 Kruise Rollout 新增了流量调度反对自定义资源的能力 ,从而更好的反对渐进式公布中的流量调度。 本文将对 Kruise Rollout 所提出的计划进行介绍。
什么是渐进式公布?
** 渐进式公布(Progressive Delivery)是一种软件部署和公布策略,旨在逐渐将新版本或性能引入生产环境,以升高危险并确保零碎的稳定性。一些常见的渐进式公布模式如下:
- 金丝雀公布: 在公布时会创立一个金丝雀版本的 Deployment 进行验证,当验证通过后,再进行全量的工作负载降级,并删除金丝雀版本的 Deployment。
- A/B 测试: 依照肯定的规定将用户流量切分成 A、B 两个不相交通路,并将导入不同版本的 Pod 实例进行解决,以此来更好地察看、比照或者灰度新版本能力。
金丝雀公布、A/B 测试和蓝绿公布都是逐渐测试和评估新性能或变更的策略,它们能够依据具体的需要和场景抉择适宜的部署和测试策略,并联合流量灰度等技术实现逐渐公布和测试新版本或性能。
为什么须要对网关资源提供反对?
Kruise Rollout 目前曾经对 Gateway API 提供了反对,那么为什么还须要对不同供应商的网关资源提供反对呢?在解释这个问题之前,咱们先来简略介绍一下 Gateway API。
以后社区中不同的供应商都有本人的网关资源,并提出了本人的规范,而 Kubernetes 为了提供一个对立的网关资源规范,构建标准化的,独立于供应商的 API,提出了 Gateway API。目前,只管 Gateway API 还处于开发阶段,但曾经有很多我的项目示意反对或打算反对 Gateway API。包含:
- Istio 是最风行的服务网格我的项目之一,Istio 1.9 版本打算引入实验性的 Gateway API 反对。用户能够通过 Gateway 和 HTTPRoute 资源来配置 Istio 的 Envoy 代理。
- Apache APISIX 是一个动静、实时、高性能的 API 网关,APISIX 目前反对 Gateway API 标准的 v1beta1 版本,用于其 Apache APISIX Ingress Controller。
- Kong 是一个为混合云和多云环境构建的开源 API 网关,Kong 在 Kong Kubernetes Ingress Controller (KIC) 以及 Kong Gateway Operator 中反对 Gateway API。
然而因为目前 Gateway API 并不能笼罩供应商所提出网关资源的所有性能,并且依然有大量用户应用供应商提供的网关资源,尽管用户能够通过开发 Gateway API 对网关资源进行适配,但这样的工作量较大,所以仅仅为 Gateway API 提供反对是远远不够的,只管随着 Gateway API 个性的不断丰富,在将来,应用 Gateway API 将成为一种更加举荐的形式。因而,尽管 Kruise Rollout 目前曾经提供了对 Gateway API 的反对,如何对现有供应商多种多样的网关资源提供反对依然是一个重要的问题。
如何兼容社区多样的网关计划?
以后社区中曾经存在许多宽泛应用的供应商提供的网关资源,比方:Istio、Kong、Apisix 等 ,然而正如前文所述,这些资源的配置并没有造成对立的规范, 因而无奈设计出一套通用的代码对资源进行解决, 这种状况给开发人员带来了一些不便和挑战。
argo-rollouts 与 flagger 兼容计划
为了可能兼容更多的社区网关资源,一些计划被提出,例如 flagger、argo-rollouts 为每一种网关资源都提供了代码实现。这些计划的实现绝对简略,但也存在一些问题:
- 面对大量的社区网关资源时,须要耗费大量精力进行实现
- 每次实现都须要从新进行公布,自定义能力较差
- 在某些环境下用户可能应用定制的网关资源,在这种状况下难以适配
- 每一种资源都有不同的配置规定,配置较为简单
- 每增加一个新的网关资源都须要为其实现新的接口,保护难度较大
argo-rollouts 不同资源配置
因而,须要一种反对用户定制,能够灵便插拔的实现计划,以适配社区以及用户定制的多种多样的网关资源,来满足社区不同的用户的需要,加强 Kruise Rollout 的兼容性和扩展性。
为此,咱们提出了一种 基于 Lua 脚本的网关资源可扩大流量调度计划。
Kruise Rollout:基于 Lua 脚本的可扩大流量调度
Kruise Rollout 应用基于 Lua 脚本的网关资源定制计划,本计划通过调用 Lua 脚本依据公布策略和网关资源原始状态来获取并更新资源的期待工作状态(状态蕴含 spec、labels 以及 annotations),能够使用户可能轻松地适配和集成不同类型的网关资源,而无需批改现有的代码和配置。
本计划对于网关资源的解决能够示意为上图,整个过程能够形容为:
- 用户定义了 Rollout 流量灰度规定、须要批改的资源等信息,开始金丝雀公布
- 依据 Rollout 配置获取指定资源
- 依据资源调用对应的 Lua 脚本
- 将资源以后状态转为字符串存入资源 annotation 中,并与公布策略一起输出 Lua 脚本
- 利用 Lua 脚本依据以后状态和公布策略解决失去新状态并更新资源
- 公布完结后,从 annotation 中获取资源的原始状态对资源进行复原
通过应用 Kruise Rollout,用户能够:
- 定制解决网关资源的 Lua 脚本,能够自在的实现对资源的解决逻辑,为更多资源提供反对
- 利用一套通用的 Rollout 配置模版对不同资源进行配置,升高配置的复杂性,不便用户配置
同时,Kruise Rollout 采纳的计划仅须要增加 5 个新接口即可实现对多种多样网关资源的反对。相比之下,其余计划例如 argo-rollouts 则为不同供应商的网关资源提供了不同的接口,对于 Istio 和 Apisix 来说,argo-rollouts 别离提供了 14 个和 4 个新的接口, 而且,该计划随着对更多网关资源的反对,接口数量还会持续增长。相比之下,Kruise Rollout 并不需要为新的网关资源提供新的接口,这使得 Kruise Rollout 成为一种更简洁、更易于保护的抉择,而不会减少过多的接口累赘。同时,编写 Lua 脚本绝对于开发 Gateway API 对网关资源进行适配,能够大大减小开发人员的工作量。
以下展现了一个利用 Lua 脚本对 Istio DestinationRule 进行解决的的示例。
- 首先定义 rollout 配置文件:
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
...
spec:
...
trafficRoutings:
- service: mocka
createCanaryService: false # 应用原有 service,不创立新的 canary service
networkRefs: # 须要管制的网关资源
- apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
name: ds-demo
patchPodTemplateMetadata:
labels:
version: canary # 为新版本 pod 打上 label
- 对 Istio DestinationRule 进行解决的 Lua 脚本为:
local spec = obj.data.spec -- 获取资源的 spec,obj.data 为资源的状态信息
local canary = {} -- 初始化一条指向新版本的 canary 路由规定
canary.labels = {} -- 初始化 canary 路由规定的 labels
canary.name = "canary" -- 定义 canary 路由规定名称
-- 循环解决 rollout 配置的新版本 pod label
for k, v in pairs(obj.patchPodMetadata.labels) do
canary.labels[k] = v -- 向 canary 规定中退出 pod label
end
table.insert(spec.subsets, canary) -- 向资源的 spec.subsets 中插入 canary 规定
return obj.data -- 返回资源状态
- 解决完的 DestinationRule 为:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
spec:
...
subsets:
- labels: # -+
version: canary # |- Lua 脚本解决后新插入的规定
name: canary # -+
- labels:
version: base
name: version-base
Kruise Rollout 进行 Istio 资源流量调度实际
接下来介绍一个利用咱们所提出计划对 Istio 进行反对的具体案例。
- 首先部署如下图所示的服务。该服务由以下几局部形成:
-
- 由 Ingress Gateway 作为内部流量网关
- 通过 VirtualService 和 DestinationRule 将流量调度至 nginx pod 中
- 利用 ConfigMap 作为主页 nginx pod 的主页
nginx 服务的 deployment 如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
version: base
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: html-volume
mountPath: /usr/share/nginx/html
volumes:
- name: html-volume
configMap:
name: nginx-configmap-base # 挂载 ConfigMap 作为 index
- 创立 rollout 资源,配置公布规定,该 rollout 分为两批公布:
-
- 第一批将 20% 的流量转发至新公布的 pod 中
- 第二批将带有 header version=canary 的流量转发至新版本 pod 中
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
annotations:
rollouts.kruise.io/rolling-style: canary
spec:
disabled: false
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-deployment
strategy:
canary:
steps:
- weight: 20 # 第一批转发 20% 的流量进入新版本 pod
- replicas: 1 # 第二批将蕴含 version=canary header 的流量转发入新版本 pod
matches:
- headers:
- type: Exact
name: version
value: canary
trafficRoutings:
- service: nginx-service # 旧版本 pod 应用的 service
createCanaryService: false # 不创立新的 canary service,新旧 pod 共用一个 service
networkRefs: # 须要批改的网关资源
- apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
name: nginx-vs
- apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
name: nginx-dr
patchPodTemplateMetadata: # 为新版本 pod 打上 version=canary 的 label
labels:
version: canary
- 批改 nginx 服务 deployment 中挂载的 ConfigMap 开始金丝雀公布。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
...
volumes:
- name: html-volume
configMap:
name: nginx-configmap-canary # 挂载新的 ConfigMap 作为 index
- 开始公布第一批,Kruise Rollout 主动调用定义的 Lua 脚本对 VirtualService 和 DestinationRule 资源进行批改,进行流量调度,将 20% 的流量转发至新版本 pod 中,此时整个服务的流量示意为下图所示:
- 执行命令 kubectl-kruise rollout approve rollout/rollouts-demo,开始公布第二批,Kruise Rollout 主动调用定义的 Lua 脚本对 VirtualService 和 DestinationRule 资源进行批改,进行流量调度,将蕴含 version=canary header 的流量转发至新版本 pod 中,此时整个服务的流量示意为下图所示:
- 执行命令 kubectl-kruise rollout approve rollout/rollouts-demo,公布完结,VirtualService 和 DestinationRule 资源复原至公布前状态,所有流量路由至新版本 pod。
如何利用 Lua 脚本疾速配置网关资源的流量调度
在调用 Lua 脚本获取资源状态新状态时,Kruise Rollout 反对两种 Lua 脚本调用形式,别离为:
- 自定义的 Lua 脚本:用户自定义的,以 ConfigMap 的模式定义并在 Rollout 中调用
- 已公布的 Lua 脚本:社区通用的、曾经稳固的 Lua 脚本,随 Kruise Rollout 打包公布
Kruise Rollout 默认首先查找本地是否存在已公布的 Lua 脚本,这些脚本通常须要设计测试案例进行单元测试验证其可用性,具备更好的稳定性。 测试案例的格局如下所示,Kruise Rollout 利用 Lua 脚本依据 rollout 中定义的公布策略对资源原始状态进行解决,失去公布过程中每一步的资源新状态,并与测试案例中 expected 中定义的期待状态进行比照,以验证 Lua 脚本是否依照预期工作。
rollout:
# rollout 配置
original:
# 资源的原始状态
expected:
# 公布过程中资源的期待状态
在资源的 Lua 脚本未公布的状况下,用户还能够疾速的通过 在 ConfigMap 中配置 Lua 脚本 的形式由 Kruise Rollout 调用从而对资源进行解决。
apiVersion: v1
kind: ConfigMap
metadata:
name: kruise-rollout-configuration
namespace: kruise-rollout
data:
# 键以 lua.traffic.routing.Kind.CRDGroup 的模式命名
"lua.traffic.routing.DestinationRule.networking.istio.io": |
--- 定义 Lua 脚本
local spec = obj.data.spec
local canary = {}
canary.labels = {}
canary.name = "canary"
for k, v in pairs(obj.patchPodMetadata.labels) do
canary.labels[k] = v
end
table.insert(spec.subsets, canary)
return obj.data
具体的 Lua 脚本配置阐明参见 Kruise Rollout 官网 [ 2]。
将来布局
- 更多网关协定反对: Kruise Rollout 目前是以 Lua 脚本插件化的形式反对多类型的网关协定,咱们后续会重点加大这方面的投入,但面对百花齐放的协定类型,单靠社区 Maintainer 的薄弱力量还远远不够,心愿更多的社区小伙伴退出咱们,一起来不断完善这方面的内容。
- 全链路灰度反对: 全链路灰度是具备更加细粒度和全面的灰度公布模式,它涵盖了应用程序的所有服务,而不止对繁多的服务进行灰度,能够更好的对新服务进行模仿和测试。目前能够通过社区的网关资源如 Istio 进行配置来实现,但人工配置往往须要耗费较大的精力。咱们将对这一部分进行摸索,从而实现对全链路灰度的反对。
社区参加
十分欢送你通过 Github/Slack/ 钉钉 / 微信 等形式退出咱们来参加 OpenKruise 开源社区。
你是否曾经有一些心愿与咱们社区交换的内容呢?能够在咱们的社区双周会 [ 3] 上分享你的声音,或通过以下渠道参加探讨:
- 退出社区 Slack channel [ 4] (English)
- 退出社区钉钉群:搜寻群号 23330762 (Chinese)
- 退出社区微信群(新):增加用户 openkruise 并让机器人拉你入群 (Chinese)
相干链接:
[1] Kruise Rollout
https://github.com/openkruise/rollouts
[2] Kruise Rollout 官网
https://openkruise.io/rollouts/introduction
[3] 社区双周会https://shimo.im/docs/gXqmeQOYBehZ4vqo
[4] Slack channel
https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise