image-20250417012614401

在上面这张架构图中,我展示了蓝绿部署的基本架构。我们对同一个应用部署了两个版本的环境,称之为蓝绿环境,流量通过 Ingress-Nginx 进入到 Service,然后再由 Service 将流量转发至相应的 Pod。在没有切换流量之前,“蓝色”环境负责接收外部请求流量。

需要进行流量切换时,我只需调整 Ingress 策略就可以将外部流量重定向到”绿色”环境,如下图所示。

image-20250417012614401

蓝绿发布实战

接下来,我会通过一个实例来演示如何使用 Kubernetes 原生的 Deployment 和 Service 来实现蓝绿发布。整个过程主要包含以下几个步骤:

  1. 创建蓝色环境的 Deployment 和 Service
  2. 创建 Ingress 策略,并指向蓝色环境的 Service
  3. 访问蓝色环境
  4. 创建绿色环境的 Deployment 和 Service
  5. 更新 Ingress 策略,并指向绿色环境
  6. 访问绿色环境

简单蓝绿发布

如标题所示,这就是最简单的蓝绿发布。

创建蓝色环境

我将分两步创建蓝色环境:首先部署蓝色应用及服务,然后创建对应的 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 策略来完成蓝绿发布。

这种方式存在一些明显的缺点:

  1. 在更新过程中,我们通常只关注镜像版本的变化,而不希望手动操作 Ingress 策略;
  2. 这种方式不利于将蓝绿发布与 GitOps 流水线进行集成;
  3. 缺乏自动化的验证和回滚机制。

安装 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

image-20250417021050525

几秒钟后,所有请求都变成了绿色方格,这表示蓝绿发布的自动化过程已经完成。相比手动方式,使用 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

image-20250417021425779

点击进入 Rollout 的详情界面,在这里,你能够以图形化的方式来查看 Rollout 的信息或进行回滚操作。

image-20250417021544709

自动化原理

在刚开始创建蓝色环境时,Ingress、Service 和 Rollout 的关系是下图这样。

image-20250417022109993

在这个例子中,当 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,但并不删除它,而是把它作为灾备。如下图所示。

image-20250417022458904

这样,当我们需要重新回滚到蓝色环境时,Argo Rollout 只需调整蓝色环境的 ReplicaSet 副本数并且修改 Service 的选择器,就可以达到快速回滚的目的。

总结

在本文中,我首先通过手动方式实践了蓝绿发布的过程。这一过程的核心是部署两套 Deployment 和 Service,通过修改 Ingress 策略来实现流量切换。

然而,手动方式不适合与 GitOps 流水线集成,因此我介绍了使用 Argo Rollout 将蓝绿发布自动化的方法。将手动过程转化为自动化过程非常简单:安装 Argo Rollout,修改 Deployment 对象的 apiVersion 和 Kind 字段,增加 strategy 字段配置蓝绿发布策略即可 。

Argo Rollout 实现自动化蓝绿发布的原理与手动修改 Ingress 策略不同,它主要通过自动修改 Service 的选择器来切换流量。这种方式将蓝绿发布简化为更新镜像的操作,大大降低了蓝绿发布的门槛。

需要注意的是,在微服务架构下实施蓝绿发布时,情况会更为复杂。我们需要关注整个微服务链路的蓝绿流量切换过程,并且在数据库层面也需要考虑对蓝绿发布的支持,使数据库在升级过程中能够同时兼容蓝绿(新旧)应用。