共计 9175 个字符,预计需要花费 23 分钟才能阅读完成。
作者:明昼
前言
Kruise Rollout 是 OpenKruise 社区开源提出的一个渐进式交付框架。其设计理念是提供一组可能将流量公布与实例灰度相结合,反对金丝雀、蓝绿、A/B Testing 等多样化公布模式,以及反对基于 Prometheus Metrics 等自定义 Metrics 实现公布过程自动化,无感对接、易扩大的旁路式规范 Kubernetes 公布组件。
https://github.com/openkruise/rollouts
在最新公布的 Kruise Rollout 0.3.0 版本中,咱们为大家带来了几个十分乏味的新个性:一是针对 Kubernetes 社区利用最为宽泛的 Deployment 工作负载的公布能力进行了重磅加强;二是对流量灰度能力进行了进一步扩大;三是反对以插入 Lua 脚本的形式来反对更多网关协定的扩大:
- Deployment 分批公布:Deployment 可能像 StatefulSet 或 CloneSet 一样具备分批公布 Pod 的能力。
- 基于 Header&Cookie 南北向流量灰度:容许用户在公布时对七层流量依照 Header&Cookie 匹配规定进行划分,并将不同流量群体导入不同版本实例,以便对新个性进行 A/B Testing 或进行更细粒度的流量调度。
- 基于 Lua 脚本的 Ingress 流量扩大:容许用户以配置 Lua 脚本的形式,为更多类型的流量组件制订 Kruise Rollout 插件,反对更多类型的 Ingress 扩大协定。
概念阐明
在介绍新个性之前,让咱们先一起梳理一下目前 Kubernetes 工作负载支流的公布模式:
- 滚动降级:原生 Deployment 自带的支流公布模式,流式滚动降级,无奈设置卡点。
<!—->
-
- 劣势:公布效率高;
- 劣势:爆炸半径大,容易呈现大规模公布故障。
<!—->
- 金丝雀公布:Flagger 和 Kruise Rollout 等组件都反对的一种针对 Deployment 的公布模式,在公布时会创立一个金丝雀版本的 Deployment 进行验证,当验证通过后,再进行全量的工作负载降级,并删除金丝雀版本的 Deployment。
<!—->
-
- 劣势:回滚无需重建或从新公布 Pod,所以回滚十分疾速和不便;
- 劣势:公布时须要额定的资源耗费,并且须要反复公布新版本 Pod,公布时不能齐全兼容 HPA。
图 1:金丝雀公布形式
- 规范分批公布:借助相似 StatefulSet 或 CloneSet 提供的 Partition 能力实现的规范模式的分批公布,公布时始终保持原工作负载名称等元属性不变,并且不会裂变出其余工作负载。
<!—->
-
- 劣势:公布不浪费资源,可管制爆炸半径,可齐全兼容 HPA 等须要 Ref 工作负载的其余组件;
- 劣势:Deployment 很难反对此类型公布(目前仅知 Kruise Rollout 反对 Deployment 进行此类型公布)。
图 2:规范分批公布形式
- 非标准分批公布:因为 Deployment 原生逻辑无奈反对分批能力,所以像 KubeVela 等社区提出的 Rollout 计划,应用的是两个 Deployment 滚动的模式进行公布。每次公布时都会创立新的 Deployment,并且对 Deployment 扩容的同时,缩容旧的 Deployment,相当于每次公布实现后 Deployment 都会被替换。
-
- 劣势:公布时不须要额定资源,可管制爆炸半径;
- 劣势:公布时会裂变多个工作负载导致短少对立管制立体,容易造成公布和扩缩动作相冲突,难以兼容 HPA 等场景,容易造成公布卡单。
图 3:非标准分批公布形式
- A/B Testing:依照肯定的规定将用户流量切分成 A、B 两个不相交通路,并将导入不同版本的 Pod 实例进行解决,以此来更好地察看、比照或者灰度新版本能力。一般来说,A/B Testing 须要联合金丝雀公布或分批公布进行。
图 4:A/B Testing
计划比照
对于上述公布模式,除了 Deployment 自带的滚动降级“一把梭”的形式不须要依附其余三方组件之外,其余公布形式或多或少都须要依附其余组件或下层 PaaS 平台的能力反对。那么 Kruise Rollout 作为其中的一种解决方案,与其余计划相比,又有何优缺点?上面咱们比拟了开源社区目前绝对较为风行的两种解决方案:Flux 社区提出的 Flagger [ 1] ,以及 Argo 社区提出的 Argo-Rollout [ 2]:
总的来说,Kruise-Rollout 的劣势能够总结为以下几点:
- 灵活性:具备旁路式可插拔能力,即,当用户下发 Kruise Rollout 配置后,对应的 Deployment 会立即具备规范分批公布的能力;当用户不在须要该能力时,可随时删除 Kruise Rollout 配置(甚至在公布过程中也能够删除),Deployment 立即会复原至原生滚动公布行为。
- 兼容性:完满兼容 HPA 或其余须要 Ref Workload 的三方组件;
- 接入简便 :因为 Kruise Rollout 极具灵活性,用户只须要下发配置即可失效, 用户无需做任何 Pod 或 Workload 的迁徙工作,对存量运行时容器无影响,不影响扩缩容链路,故接入绝对非常简便。
个性介绍
在介绍新个性之前,再啰嗦一下为什么 OpenKruise 社区要执着于做 Rollout 这件事件。
- 咱们晓得在 Kubernetes 中,容器生命周期与流量生命周期异步治理的设计使得 Deployment 自身无奈感知流量的挂载与卸载,咱们曾遇到某客户在一次 Deployment 流式滚动降级过程中,流量组件出现异常,导致流量全副挂空的事变,尽管只有短短十几分钟,但却也造成了十分大的损失。
- 业务逻辑导致的 Bug 在 Deployment 流式滚动更新的公布阶段无奈感知,一旦全量上线后,可能会造成重大故障,很难管制故障的爆炸半径(因为 Deployment 滚动降级只有 Pod 可用就会全量公布)。
- 咱们也常常遇到在测试环境中跑的好好的,为什么到了生产却不行了之类的问题。其实只靠环境隔离解决不了所有问题,生产公布环境最好还是不要降级“一把梭哈”,循序渐进能力“一步一个脚印”。
上述场景如果应用分批的公布模式,其实是能够尽可能地将问题的爆炸半径管制在灰度范畴之内,并且能够留下短缺的灰度和察看的工夫。然而,Deployment 原生逻辑并不反对分批操作,但如果应用 Argo-Rollout,还须要把所有工作负载和 Pod 进行迁徙,危险太高,而且适配也太麻烦;如果应用 Flagger,依然要迁徙 Pod,并且公布时候还须要双倍资源,代价也太高。
这时候,你须要的可能是 Kruise-Rollout!仅需两步,就能够让你的存量 Deployment 立即具备规范分批公布能力!
新个性一:教你玩转 Deployment 规范分批公布
前置步骤
存量或新建 Kubernetes 集群并要求:
- Kubernetes version >= 1.19
注:该版本要求次要是 Ingress API 在 1.19 有较大变动所引起,如果你不须要简单的流量灰度的能力(即不须要配置 TrafficRouting 字段),能够自行拉取和批改 charts,来躲避该版本要求。
步骤一:一键装置 Kruise-Rollout
$ helm install kruise-rollout openkruise/kruise-rollout –version 0.3.0
步骤二:为你的 Deployment 绑定并下发分批公布规定
cat <<EOF | kubectl apply -f -
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
namespace: default
annotations:
rollouts.kruise.io/rolling-style: partition
spec:
objectRef: # 绑定你的 Deployment
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: echoserver
strategy: # 制订你的分批公布规定
canary:
steps:
- replicas: 1 #第一批发一个 Pod,公布完后暂停,手动确认后进入下一批
- replicas: 60% #第二批发 60% Pod,公布完后暂停,手动确认后进入下一批
- replicas: 100% #第三批发全量 Pod,最初一批公布完后默认主动实现
EOF
步骤三:玩转 Deployment 的分批公布
如此一来,当你后续进行公布时,Deployment 的流式滚动降级将会间接变为分批公布。上面咱们以一个名为 echoserver 的 Deployment 为例,形容一次分批公布过程。
1. 公布前
检查一下 Deployment 正本数为 5,以后版本为 789b88f977:
2. 开始公布第一批
此时,咱们批改容器的某个环境变量来触发公布,能够看到第一批只公布了一个 Pod,版本号为 d8db56c5b:
3. 持续公布第二批
第一批 Pod 公布结束后,此时假如咱们曾经实现第一批的验证,想要持续发第二批 Pod,咱们能够借助 kubectl-kruise 这个命令行工具来进行批次实现的确认操作。该工具是基于 kubectl 的拓展,目前也是由 OpenKruise 社区保护。
注:确认发下一批的命令为 kubectl-kruise rollout approve rollout/rollouts-demo
从上述过程能够看出,在该批次公布过程中且未实现时,Rollout 会进入 StepUpgrade 状态,而当该批次公布实现,会转变成 StepPaused 状态。
4. 公布最初一批
当第二批公布确认实现后,发最初一批后,Rollout 会进入 Completed 状态,示意公布实现:
特地要阐明的是,在分批公布的 单个公布批次内,咱们依然会遵循流式滚动公布的规定,也就是说你依然能够通过调整 Deployment 的 MaxUnavailable 和 MaxSurge 来兼顾你公布时的稳定性和效率,例如在以下场景,你仍然能够遵循 Deployment 的如下配置:
- 单个批次内必须 先扩后缩 ,最大水平保障 公布稳固:
kind: Deployment
spec:
strategy:
rollingUpdate:
maxUnavailble: 0
maxSurge: 20%
- 单个批次内必须 先缩后扩 ,最大水平 节俭 资源占用:
kind: Deployment
spec:
strategy:
rollingUpdate:
maxUnavailble: 20%
maxSurge: 0
- 单个批次内 边扩遍缩 ,最大水平 进步公布效率:
kind: Deployment
spec:
strategy:
rollingUpdate:
maxUnavailble: 25%
maxSurge: 25%
此外,该计划还充分考虑了各种公布场景,最大水平地进步计划的灵活性:
- 间断公布场景:v1 到 v2 的公布过程中(v2 未公布实现),又公布了 v3,此时 v3 依然会从第一批开始走规范分批公布流程;
- 疾速回滚场景:v1 到 v2 公布到中途,回滚回 v1,则会进行疾速回滚,默认不再进行分批公布。
- 公布策略删除:无论是在公布实现后,甚至是在公布过程中,失常删除 Rollout 资源后,相应的 Deployment 都会无缝回退至流式滚动公布场景,不便某些非凡状况下疾速进行变更。
新个性二:基于 Header&Cookie 的流量灰度
在 Kruise-Rollout 0.3.0 之前的版本中,咱们提供了基于调整流量权重(Weight)的流量灰度计划,然而思考到在理论大多数场景中,各类 Ingress 等自身曾经具备的载平衡能力就能满足日常流量灰度的需要,例如 10% 的 canary 正本自身就会默认打入 10% 的流量,如果不是非凡的精细化流量调整场景(例如 10% 的 canary 正本只导入 1% 流量),个别不须要独自配置该能力。
然而,对于一些公布敏感性业务,是可能须要 A/B Test 等这类非凡的公布模式:即在公布时,需先将特定的一批带有标记的流量,定向导入新版本 Pod,将新旧版本的流量进行隔离,比方如下场景:
- 业务新个性只对白名单用户凋谢,能够很大水平缩小业务新个性的不确定性带来的危险;
- 将新旧两个版本进行流量隔离,不便进行对照试验,更好地察看新版本个性的有效性;
对于 Kruise-Rollout 的用户来说,能够通过以下配置来开启该能力:
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
namespace: default
annotations:
rollouts.kruise.io/rolling-style: partition
spec:
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: echoserver
strategy:
canary:
steps:
- matches: #设置 header&cookie 匹配规定
- headers:
- name: UserAgent
type: Exact
value: iOS
pause: {}
replicas: 1
- replicas: 50%
- replicas: 100%
trafficRoutings:
- ingress:
classType: nginx
name: echoserver
service: echoserver
上述相较于单纯的分批公布配置,多了 Header & Cookie 匹配规定的形容,以及 TrafficRouting 的援用,这里是以 Ingress-Nginx 为例进行的配置,也就是说,想要应用该能力,相应的 Ingress 控制器必须要具备该根底能力(能够了解为 Nginx 提供数据面的能力,Kruise-Rollout 提供管控面的能力)。
在该配置下,假如有正本数量为 10 的 Deployment,则其将被划分为三批进行公布,具体行为如下:
- 第一批共计有 1 个新版本 Pod,9 个旧版本 Pod,并且指定满足 UserAgent=iOS 这一匹配规定的用户流量才会打入新版本 Pod,其余流量会平均打入残余 9 个旧版本 Pod;
- 第二批共计有 5 个新版本 Pod,5 个旧版本 Pod,并且勾销流量匹配规定,流量间接全副走负载平衡策略;
- 第三批共计有 10 个新版本 Pod,0 个旧版本 Pod,并且勾销流量匹配规定,流量间接全副走负载平衡策略;
新个性三:基于 Lua 脚本的 Ingress 流量扩大计划
云原生技术倒退到明天,云原生网关也呈现出百花齐放的状态,除了 Kubernetes 原生提供的 Nginx Ingress 以及 Gateway API 之外,也存在着十分多的 Network Provider 计划,比方阿里云 ALB、MSE、ASM;社区的 Istio、Kong、Apisix,甚至是许多公司是自研网关计划和协定等等。Kruise Rollout 设计之初就思考过百花齐放的云原生网关应该如何反对,惯例的硬编码的形式既费时费力,也不不便不同公司的同学对接应用和保护。
最终,Kruise Rollout 抉择基于 Lua 脚本的形式,让用户以插件化的模式反对更多类型的网关协定(此版本只反对基于 Ingress 的扩大协定,其它自定义资源协定将在下个版本反对),Kruise Rollout 实现一些通用局部的能力,而不同 NetWork Provider 的具体实现则由 Lua 脚本来解决,这样针对不同的实现,只须要编写对应的 Lua 脚本即可,可参考:Nginx 与 Alb Lua 脚本示例 [ 3] 。为了不便大家依据本人的需要编写本人的 Lua 脚本,上面针对 Nginx Ingress 解读一下 lua 脚本(对应的 Rollout 配置能够参考 新个性二),该脚本能够搁置于特定目录或特定 ConfigMap:
-- 因为 Ingress 灰度公布协定都是基于 Annotations 来实现的,所以此脚本的所有操作
-- 都是批改 Annotations 到指标状态,kruise rollout 会将此 annotations patch 到
-- ingress canary 资源当中
annotations = {}
-- obj.annotations 是 Ingress.Annotations 此句不须要变动,固定即可
if (obj.annotations)
then
annotations = obj.annotations
end
-- 这是 nginx 灰度公布协定的规范,其它的实现也能够依据本人的理论状况调整
annotations["nginx.ingress.kubernetes.io/canary"] = "true"
-- nginx 的灰度公布协定变动次要是上面这些变动,为了简化多个批次间来回切换的复杂度,每次
-- 都先将这些 annotations 置空
annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = nil
annotations["nginx.ingress.kubernetes.io/canary-by-header"] = nil
annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = nil
annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = nil
annotations["nginx.ingress.kubernetes.io/canary-weight"] = nil
-- obj.weight 是 rollout.spec.strategy.canary.steps[x].weight
-- 代表以后批次的灰度百分比,当不设置时为‘-1’(lua 脚本不反对 nil,所以用‘-1’示意),-- 所以如果不是‘-1’,须要将 obj.weight 设置到 annotations 中
if (obj.weight ~= "-1")
then
annotations["nginx.ingress.kubernetes.io/canary-weight"] = obj.weight
end
-- obj.matches 是 rollout.spec.strategy.canary.steps[x].matches(数据结构一样),-- 当没有设置时表明此 step 不须要进行 A/B Testing 公布,间接返回即可
if (not obj.matches)
then
return annotations
end
-- A/B Testing 公布,遍历 matches,将 matches 设置到 annotations 中
-- 留神:nginx 并不反对多个 header,所以这里并不需要真正的遍历,默认只取第一个数组
for _,match in ipairs(obj.matches) do
-- 留神 lua 脚本当中数组是从下标‘1’开始
local header = match.headers[1]
-- cookie
if (header.name == "canary-by-cookie")
then
annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = header.value
-- header
else
annotations["nginx.ingress.kubernetes.io/canary-by-header"] = header.name
-- 是否是“正则”if (header.type == "RegularExpression")
then
annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = header.value
else
annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = header.value
end
end
end
-- must be return annotations
return annotations
注 :此版本只针对 Ingress 资源来实现的,面对 Apisix、Kong 等其它自定义资源(CRD)将在下一个版本反对。 相干 PR [ 4] 曾经提交 Github,欢送大家一起探讨。
将来布局
- 更多网关协定反对:Kruise Rollout 目前是以 Lua 脚本插件化的形式反对多类型的网关协定,咱们后续会重点加大这方面的投入,但面对百花齐放的协定类型,单靠社区 Maintainer 的薄弱力量还远远不够,心愿更多的社区小伙伴退出咱们,一起来不断完善这方面的内容。
- 更欠缺的公布体系:为撑持包含灰度、告警、可观测、主动回滚、无人值守等在内的较为残缺的公布体系,须要持续建设一些公布公布时的 Hook 调用与 Prometheus Metrics Analysis 等相干能力,这块咱们目前正在与 KubeVela 社区严密单干,通过 KubeVela 现有的 Workflow 体系集成来补救目前这些能力的缺失,至于后续是否须要将这些能力做到 Kruise Rollout 之中,咱们也心愿凝听更多的社区意见,欢送大家一块探讨沟通。
社区参加
十分欢送你通过 Github/Slack/ 钉钉 / 微信 等形式退出咱们来参加 OpenKruise 开源社区。
你是否曾经有一些心愿与咱们社区交换的内容呢?
能够在咱们的 社区双周会 [ 5] 上分享你的声音,或通过以下渠道参加探讨:
- 退出社区 Slack channel [ 6] (English)
- 退出社区钉钉群:搜寻群号 23330762 (Chinese)
- 退出社区微信群(新):增加用户 openkruise 并让机器人拉你入群 (Chinese)
相干链接
[1] flagger
https://github.com/fluxcd/flagger
[2] Argo-Rollout
https://github.com/argoproj/argo-rollouts
[3] Nginx 与 Alb Lua 脚本示例https://github.com/openkruise/rollouts/tree/master/lua_configuration/trafficrouting_ingress
[4] 相干 PR
https://github.com/openkruise/rollouts/pull/111
[5] 社区双周会
https://shimo.im/docs/gXqmeQOYBehZ4vqo
[6] Slack channel
https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise
戳这里,查看 OpenKruise 我的项目官方主页与文档