Argo 家简直是在 Kubernetes 上实现 GitOps 的大杀器,本来 ArgoCD 搭配 Kubernetes 原生的 Rollout 就够用了,但不行,还要实践蓝绿部署,金丝雀部署。
什么是 Rollouts
其实和 Deployment 类似,也是控制 Pod 的,看具体的 yaml 也会发现差不多,但是却多了一个渐进性部署的功能。
什么是金丝雀部署
“金丝雀” 一词是指 “煤矿中的金丝雀” 的做法,即把金丝雀带入煤矿以保证矿工的安全。 如果出现无味的有害气体,鸟就会死亡,而矿工们知道他们必须迅速撤离。 同样,如果更新后的代码出了问题,新版本就会被 “疏散” 回原来的版本。
金丝雀部署是一种部署策略,开始时有两个环境:一个有实时流量,另一个包含没有实时流量的更新代码。 流量逐渐从应用程序的原始版本转移到更新版本。 它可以从移动 1% 的实时流量开始,然后是 10%,25%,以此类推,直到所有流量都通过更新的版本运行。 企业可以在生产中测试新版本的软件,获得反馈,诊断错误,并在必要时快速回滚到稳定版本。
安装
先按照官网,安装 Argo Rollouts,其实就是 CRD、SA 和 Controller 那一套。
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
创建 Demo
创建 demo Rollout。第一次部署不会有什么惊喜,但是它会像创建 Deployment 一样,拉起 Pod。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
replicas: 2
strategy:
canary:
steps:
- setWeight: 20
- pause: {}
- setWeight: 40
- pause: { duration: 10 }
- setWeight: 60
- pause: { duration: 10 }
- setWeight: 80
- pause: { duration: 10 }
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollouts-demo
template:
metadata:
labels:
app: rollouts-demo
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
---
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
升级版本
用命令更新 image 的 tag,触发 Rollout 更新。
kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:yellow
watch 一下,发现拉起了一个新的 pod。根据之前的设置,会有 20%的流量打到金丝雀部署里面了。
Name: rollouts-demo
Namespace: default
Status: ॥ Paused
Message: CanaryPauseStep
Strategy: Canary
Step: 1/2
SetWeight: 0
ActualWeight: 0
Images: argoproj/rollouts-demo:blue (stable)
argoproj/rollouts-demo:yellow (canary)
Replicas:
Desired: 2
Current: 3
Updated: 1
Ready: 3
Available: 3
NAME KIND STATUS AGE INFO
⟳ rollouts-demo Rollout ॥ Paused 12h
├──# revision:22
│ └──⧉ rollouts-demo-6cf78c66c5 ReplicaSet ✔ Healthy 12h canary
│ └──□ rollouts-demo-6cf78c66c5-lrp7n Pod ✔ Running 66m ready:1/1
├──# revision:21
│ └──⧉ rollouts-demo-5747959bdb ReplicaSet ✔ Healthy 6h57m stable
│ ├──□ rollouts-demo-5747959bdb-drwf5 Pod ✔ Running 6h56m ready:1/1
│ └──□ rollouts-demo-5747959bdb-tmdrn Pod ✔ Running 6h56m ready:1/1
└──# revision:9
└──⧉ rollouts-demo-687d76d795 ReplicaSet • ScaledDown 12h
查看 ArgoCD
搭配 ArgoCD,效果更好。可以看到,新的 ReplicaSet 被创建,同时有了新的 pod。
创建一个新的 kustomize.yml 文件,并创建 ArgoCD application,就可以看到下面的图。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
metadata:
name: rollouts-demo
namespace: ics
resources:
- rollouts-demo.yml
- service-demo.yml
images:
- name: argoproj/rollouts-demo:blue
newTag: yellow
手动部署或者回滚
kubectl argo rollouts promote rollouts-demo
kubectl argo rollouts abort rollouts-demo
自动部署或者回滚
可以在 steps 中加入 Analysis 步骤,触发测试脚本,确认一切 ok 之后,自动部署。
如果测试失败,版本会被回滚。相应的代码也要回滚,否则 ArgoCD 上会一直显示 Degraded 的状态。
流量控制
一般来说,金丝雀就是要把一丢丢流量丢给新版本进行测试,但是我不想这样做,我更希望等我测试完毕之后再切流量过去。这点也可以按照文档的配置实现。
按照下面的配置,新版本来了之后,会有一个新的 pod 拉起来,并且通过 Header/Cookie 来切流量到新的 Pod。
spec:
replicas: 2
strategy:
canary:
canaryService: rollouts-demo-canary
stableService: rollouts-demo-stable
steps:
# scale up to 1 pod to work with trafficeRouting
# so that requests with specifc header will be
# routed to canary service
- setCanaryScale:
replicas: 1
- pause: {}
trafficRouting:
nginx:
stableIngress: user-profile-ingress
additionalIngressAnnotations:
canary-by-header: X-Canary
canary-by-header-value: iwantsit
canary-by-cookie: Canary
背后的原因
其实 Kubernetes 的 Ingress-Nginx controller 是支持金丝雀部署的。这里会创建和原来 Ingress 一样的 Ingress,只是多了几个 Canary 相关的注解,从而在 Ingress-Nginx Controller 上实现分流。
需要注意的是,在上面的例子中,我们不要设置 weight,否则会导致按照 weight 拆分的流量才会打到新版本。按照文档的解释,weight 和 http header 是有优先级关系,需要多测试一下。我自己测试的结果是 weight 的优先级更高,会直接把一部分流量分给新版本。如果 weight 设置为 0,则会验证 http header 进行分流。