在上面这张架构图中,我展示了蓝绿部署的基本架构。我们对同一个应用部署了两个版本的环境,称之为蓝绿环境,流量通过 Ingress-Nginx 进入到 Service,然后再由 Service 将流量转发至相应的 Pod。在没有切换流量之前,“蓝色”环境负责接收外部请求流量。
需要进行流量切换时,我只需调整 Ingress 策略就可以将外部流量重定向到”绿色”环境,如下图所示。
蓝绿发布实战
接下来,我会通过一个实例来演示如何使用 Kubernetes 原生的 Deployment 和 Service 来实现蓝绿发布。整个过程主要包含以下几个步骤:
- 创建蓝色环境的 Deployment 和 Service
- 创建 Ingress 策略,并指向蓝色环境的 Service
- 访问蓝色环境
- 创建绿色环境的 Deployment 和 Service
- 更新 Ingress 策略,并指向绿色环境
- 访问绿色环境
简单蓝绿发布
如标题所示,这就是最简单的蓝绿发布。
创建蓝色环境
我将分两步创建蓝色环境:首先部署蓝色应用及服务,然后创建对应的 Ingress 规则。
创建蓝色资源(应用 + 服务)
首先,我需要创建蓝色环境,将下面的内容保存为 blue_deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blue
labels:
app: blue
spec:
replicas: 3
selector:
matchLabels:
app: blue
template:
metadata:
labels:
app: blue
spec:
containers:
- name: demo
image: argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: blue-service
labels:
app: blue
spec:
ports:
- protocol: TCP
port: 80
targetPort: 8080
selector:
app: blue
type: ClusterIP
在这段 Manifest 中,我使用了 argoproj/rollouts-demo:blue 镜像创建蓝色环境的 Deployment 工作负载,并创建了名为 blue-service 的 Service 对象。通过 Service 的选择器,我将 Service 和具有 app: blue 标签的 Pod 进行关联。
cheverjohn:quartz/ git:(docs/about-argocd-blue-green-deploy*)$ kb apply -f content/CloudNative/ArgoCD/blue_deployment.yaml
kbdeployment.apps/blue created
service/blue-service created
实际命令为:
kubectl apply -f content/CloudNative/ArgoCD/blue_deployment.yaml
部署完成后,我等待工作负载达到 Ready 状态:
$ kubectl wait pods -l app=blue --for condition=Ready --timeout=90s
pod/blue-79c9fb755d-9b6xx condition met
创建蓝色环境 ingress 策略
接下来,我们再创建蓝色环境的 Ingress 策略。将下面的内容保存为 blue_ingress.yaml 文件。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bluegreen-demo-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: "bluegreen-demo-in-k8s.cheverjohn.me"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: blue-service
port:
number: 80
tls:
- hosts:
- bluegreen-demo-in-k8s.cheverjohn.me
secretName: bluegreen-demo-in-k8s-tls
ingressClassName: "nginx"
访问域名,可以看到下面的效果。
创建绿色环境
分为部署 green 应用和切换 ingress 两部分。
创建绿色资源(应用 + 服务)
假设我们需要发布新版本,即部署绿色环境。我将下面的内容保存为 green_deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: green
labels:
app: green
spec:
replicas: 3
selector:
matchLabels:
app: green
template:
metadata:
labels:
app: green
spec:
containers:
- name: demo
image: argoproj/rollouts-demo:green
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: green-service
labels:
app: green
spec:
ports:
- protocol: TCP
port: 80
targetPort: 8080
selector:
app: green
type: ClusterIP
在这段 Manifest 中,我使用 argoproj/rollouts-demo:green 镜像创建绿色环境的 Deployment,并创建了名为 green-service 的 Service 对象。
接下来,使用 kubectl apply 来将它应用到集群内。
切换 ingress 实现绿色环境切换
green_ingress.yaml 如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bluegreen-demo-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: "bluegreen-demo-in-k8s.cheverjohn.me"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: green-service
port:
number: 80
tls:
- hosts:
- bluegreen-demo-in-k8s.cheverjohn.me
secretName: bluegreen-demo-in-k8s-tls
ingressClassName: "nginx"
蓝色发布自动化
到目前为止,我演示的是通过创建 Kubernetes 原生对象并修改 Ingress 策略来完成蓝绿发布。
这种方式存在一些明显的缺点:
- 在更新过程中,我们通常只关注镜像版本的变化,而不希望手动操作 Ingress 策略;
- 这种方式不利于将蓝绿发布与 GitOps 流水线进行集成;
- 缺乏自动化的验证和回滚机制。
安装 Argo Rollout
Argo Rollout 是一款专门提供 Kubernetes 高级部署能力的自动化工具,它可以独立运行,同时也可以和 ArgoCD 协同在 GitOps 流水线中来使用。
在使用之前,我们需要先安装它,你可以通过下面的命令进行安装。我已经将官方的拷贝了一份。
kubectl create namespace argo-rollouts # 创建命名空间
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
安装完成后,等待 Argo Rollout 工作负载就绪。
$ kubectl wait --for=condition=Ready pods --all -n argo-rollouts --timeout=300s
pod/argo-rollouts-7f75b9fb76-wh4l5 condition met
创建 Rollout 对象
和手动实施蓝绿发布的过程不同的是,为了实现自动化,Argo Rollout 采用了自定义资源(CRD)的方式来管理工作负载。
首先,我需要先创建 Rollout 对象。将下面的内容保存为 blue-green-rollout.yaml 文件。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: bluegreen-demo
labels:
app: bluegreen-demo
spec:
replicas: 3
revisionHistoryLimit: 1
selector:
matchLabels:
app: bluegreen-demo
template:
metadata:
labels:
app: bluegreen-demo
spec:
containers:
- name: bluegreen-demo
image: harbor.cheverjohn.me/quay.io/argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
strategy:
blueGreen:
autoPromotionEnabled: true
activeService: bluegreen-demo
仔细观察可以发现,这个 Rollout 对象的大部分字段定义与 Kubernetes 原生的 Deployment 工作负载非常相似 。主要区别在于:
- apiVersion 从 apps/v1 修改为 argoproj.io/v1alpha1
- kind 字段从 Deployment 修改为 Rollout
- 增加了 strategy 字段来定义部署策略
在 strategy 字段中,autoPromotionEnabled: true 表示自动实施蓝绿发布,activeService 用于关联蓝绿发布的 Service 名称,即我们接下来要创建的 Service 。
当我将这段 Rollout 对象应用到集群后,Argo Rollout 会创建 Kubernetes 原生对象 ReplicaSet,然后 ReplicaSet 会创建对应的 Pod。
创建 Service 和 Ingress
创建好 Rollout 对象之后,我们还需要创建 Service 和 Ingress 策略,这和之前手动实施蓝绿发布的过程是一致的。
首先,创建 Service。将下面的内容保存为 blue-green-service.yaml 文件。
apiVersion: v1
kind: Service
metadata:
name: bluegreen-demo
labels:
app: bluegreen-demo
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: bluegreen-demo
最后,创建 Ingress 对象。将下面的内容保存为 blue-green-ingress.yaml 文件。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bluegreen-demo
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: "bluegreen-auto-in-k8s.cheverjohn.me"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: bluegreen-demo
port:
number: 80
tls:
- hosts:
- bluegreen-auto-in-k8s.cheverjohn.me
secretName: bluegreen-auto-in-k8s-tls
ingressClassName: "nginx"
和之前创建的 Ingress 对象不同的是,这里我们使用了 bluegreen-auto-in-k8s.cheverjohn.me 域名,以便和之前创建的 Ingress 域名区分开。
请访问域名。
蓝绿发布自动化
现在,假设我们需要更新到绿色环境,在 Argo Rollout 的帮助下,你只需要修改 Rollout 对象中的镜像版本就可以了,流量切换过程将由 Argo Rollout 自动控制。
要更新到绿色环境,你需要编辑 blue-green-rollout.yaml 文件的 image 字段,将 blue 修改为 green 版本。
containers:
- name: bluegreen-demo
image: argoproj/rollouts-demo:green
然后,使用 kubectl apply 将这段 Rollout 对象重新应用到集群内。
$ kubectl apply -f blue-green-rollout.yaml
rollout.argoproj.io/bluegreen-demo configured
几秒钟后,所有请求都变成了绿色方格,这表示蓝绿发布的自动化过程已经完成。相比手动方式,使用 Argo Rollout 进行蓝绿发布时,我不再需要手动切换流量,只需关注镜像版本的更新。
访问 ArgoRollout Dashboard
要访问 Argo Rollout Dashboard,首先你需要安装 Argo Rollout 的 kubectl 插件,以 MacOS 为例,你可以通过下面的命令来安装。
brew install argoproj/tap/kubectl-argo-rollouts
插件安装完成后,你可以通过下面的命令来检查安装是否成功。
$ kubectl argo rollouts version
kubectl-argo-rollouts: v1.3.0+93ed7a4
BuildDate: 2022-09-19T02:51:42Z
GitCommit: 93ed7a497b021051bf6845da90907d67c231e703
GitTreeState: clean
GoVersion: go1.18.6
Compiler: gc
Platform: darwin/amd64
当看到上面的输出结果后,说明插件安装成功了。接下来,我们可以使用 kubectl argo rollouts dashboard 来启用 Dashboard。
$ kubectl argo rollouts dashboard
INFO[0000] Argo Rollouts Dashboard is now available at http://localhost:3100/rollouts
点击进入 Rollout 的详情界面,在这里,你能够以图形化的方式来查看 Rollout 的信息或进行回滚操作。
自动化原理
在刚开始创建蓝色环境时,Ingress、Service 和 Rollout 的关系是下图这样。
在这个例子中,当 Rollout 对象创建后,Argo Rollout 将会随之创建 ReplicaSet 对象,名称为 blue-green-fbc7b7f55,这个 ReplicaSet 会在创建 Pod 时额外为 Pod 打上 rollouts-pod-template-hash=fbc7b7f55 的标签,同时为 Service 添加 rollouts-pod-template-hash=fbc7b7f55 选择器,这样,就打通了从 Ingress 到 Pod 的请求链路。
当我们修改 Rollout 对象的镜像版本后,Argo Rollout 将会重新创建一个新的 ReplicaSet 对象,名称为 bluegreen-demo-7d6459646d,新的 ReplicaSet 也会在创建 Pod 时额外为 Pod 打上 rollouts-pod-template-hash=7d6459646d 标签。这时候蓝绿环境的 ReplicaSet 同时存在。
当绿色环境的 Pod 全部就绪之后,Argo Rollout 会将 Service 原来的选择器删除,并添加 rollouts-pod-template-hash=7d6459646d 的选择器,这样就将 Service 指向了绿色环境的 Pod,从而达到了切换流量的目的。同时,Argo Rollout 还会将蓝色环境的 ReplicaSet 副本数缩容为 0,但并不删除它,而是把它作为灾备。如下图所示。
这样,当我们需要重新回滚到蓝色环境时,Argo Rollout 只需调整蓝色环境的 ReplicaSet 副本数并且修改 Service 的选择器,就可以达到快速回滚的目的。
总结
在本文中,我首先通过手动方式实践了蓝绿发布的过程。这一过程的核心是部署两套 Deployment 和 Service,通过修改 Ingress 策略来实现流量切换。
然而,手动方式不适合与 GitOps 流水线集成,因此我介绍了使用 Argo Rollout 将蓝绿发布自动化的方法。将手动过程转化为自动化过程非常简单:安装 Argo Rollout,修改 Deployment 对象的 apiVersion 和 Kind 字段,增加 strategy 字段配置蓝绿发布策略即可 。
Argo Rollout 实现自动化蓝绿发布的原理与手动修改 Ingress 策略不同,它主要通过自动修改 Service 的选择器来切换流量。这种方式将蓝绿发布简化为更新镜像的操作,大大降低了蓝绿发布的门槛。
需要注意的是,在微服务架构下实施蓝绿发布时,情况会更为复杂。我们需要关注整个微服务链路的蓝绿流量切换过程,并且在数据库层面也需要考虑对蓝绿发布的支持,使数据库在升级过程中能够同时兼容蓝绿(新旧)应用。