本文主要从使用背景、使用前提和实现原理等方面介绍了ACK Nginx Ingress 灰度发布,以及如何快速配置灰度发布流水线。
使用背景
灰度及蓝绿发布:为新版本创建一个与老版本完全一致的生产环境,在不影响老版本的前提下,按照一定的规则把部分流量切换到新版本,当新版本试运行一段时间没有问题后,将用户的全量流量从老版本迁移至新版本。其中A/B测试就是一种灰度发布方式,一部分用户继续使用老版本的服务,将一部分用户的流量切换到新版本,如果新版本运行稳定,则逐步将所有用户迁移到新版本。
Ingress灰度发布使用背景:控制新版本流量分配权重,以小部分线上流量对服务进行验证通过cookie或者header使得部分受控用户在线上对发布进行验证。当发布验证失败后,可以快速回滚到旧版本。
使用前提
使用由阿里云容器服务Kubernetes版本提供的Kubernetes集群。
Deployment的labels内包含有Service的全部selector labels,如:
--- apiVersion: apps/v1 kind: Deployment metadata: labels: run: spring-boot-sample name: spring-boot-sample spec: replicas: 2 selector: matchLabels: run: spring-boot-sample template: metadata: labels: run: spring-boot-sample spec: containers: - image: ${IMAGE} name: app --- apiVersion: v1 kind: Service metadata: name: spring-boot-service labels: test: test labele2: label spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080 - name: https port: 443 protocol: TCP targetPort: 443 selector: run: spring-boot-sample sessionAffinity: None type: ClusterIP
实现原理
如下图所示,当采用ACK Nginx Ingress灰度发布时,假定当前运行版为primary,将要发布版本为canary。
发布过程:
发布前检查:预检查当前Ingress是否有且只关联了唯一的Service实例,且Service实例下有且只有唯一版本的Deployment。
生成Canary版本:克隆primary版本的Service以及Deployment生成canary版本的Service和Deployment,同时修改canary版本Deployment的镜像到新版本。
修改Ingress流量规则:根据发布配置调整Ingress配置,开始执行灰度。
人工验证:通过cookie或者header对灰度版本进行验证,根据结果选择完成发布或者回滚。
完成灰度:修改Ingress配置以及流量规则,下线Primary版本的Service以及Deployment实例。
回滚发布:修改Ingress配置以及流量规则,下线Canary版本的Service以及Deployment。
快速开始
初始化应用部署
通过以下配置快速初始化应用部署:
apiVersion: apps/v1 kind: Deployment metadata: name: old-nginx labels: run: old-nginx spec: replicas: 2 selector: matchLabels: run: old-nginx template: metadata: labels: run: old-nginx spec: containers: - image: nginx imagePullPolicy: Always name: nginx # 容器名称 ports: - containerPort: 80 protocol: TCP restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: old-nginx spec: ports: - port: 80 #服务端口 protocol: TCP targetPort: 80 #应用端口 selector: run: old-nginx sessionAffinity: None type: NodePort --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: gray-release spec: rules: - host: www.example.com http: paths: # 老版本服务 - path: / backend: serviceName: old-nginx servicePort: 80
保存以上内容到,gray-release.yaml,使用kubectl或者控制台完成应用初始化:
kubectl apply -f gray-release.yaml
配置流水线
如下所示,配置如下信息:
配置说明:
配置项
说明
命名空间
当前服务所在Kubernetes集群命名空间, 示例中为default
Ingress名称
发布的目标Ingress实例名称,示例中为gray-release
服务端口
Ingress后端Service 实例对外暴露的端口,示例中为80
应用端口
镜像对外暴露的端口,示例中为80
容器名称
应用发布时需要更新镜像的容器名,示例中为nginx
镜像
通过前序任务镜像构建产生的镜像,或者是特定的镜像名称
灰度方式
选择使用header或者cookie方式进行灰度验证,并设置匹配的Key/Value
灰度初始化流量权重
默认灰度版本上线后的流量权重
启动等待时间
灰度版本发布后等待该时间后再修改Ingress配置
下线等待时间
Ingress配置调整后等待该时长后再移除应用实例
运行流水线
保存并运行流水线,当新版本发布后,流水线的任务将处于灰度发布中,等待人工验证以确定后续操作。
灰度中查看Kubernetes资源状态如下所示:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"nginx.ingress.kubernetes.io/service-match":"gray-release-v20201231112858: cookie(\"foo\", /^bar$/)","nginx.ingress.kubernetes.io/service-weight":"gray-release-v20201231112858: 0, old-nginx: 100"},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"old-nginx","servicePort":80},"path":"/","pathType":"ImplementationSpecific"},{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}} nginx.ingress.kubernetes.io/service-match: 'gray-release-v20201231112858: cookie("foo", /^bar$/)' nginx.ingress.kubernetes.io/service-weight: 'gray-release-v20201231112858: 0, old-nginx: 100' name: gray-release namespace: default spec: rules: - host: www.example.com http: paths: - backend: serviceName: old-nginx servicePort: 80 path: / pathType: ImplementationSpecific - backend: serviceName: gray-release-v20201231112858 servicePort: 80 path: / pathType: ImplementationSpecific
在灰度中云效自动创建了灰度版本的服务,命名规则为:
<ingressname>-<version>
,查看灰度版本的Service以及Deployment详情:apiVersion: v1 kind: Service metadata: labels: run: old-nginx version: v20201231112858 name: gray-release-v20201231112858 namespace: default spec: clusterIP: 10.0.6.88 ports: - port: 80 protocol: TCP targetPort: 80 selector: run: old-nginx version: v20201231112858 type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: generation: 1 labels: run: old-nginx version: v20201231112858 name: gray-release-v20201231112858 namespace: default spec: progressDeadlineSeconds: 600 replicas: 2 revisionHistoryLimit: 10 selector: matchLabels: run: old-nginx version: v20201231112858 strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: creationTimestamp: null labels: run: old-nginx version: v20201231112858 spec: containers: - image: nginx:latest imagePullPolicy: Always name: nginx ports: - containerPort: 80 protocol: TCP
用户可以通过在浏览器设置cookie的后来访问新版本的应用:
document.cookie="foo=bar"
确认发布完成
在人工验证成功后,在卡片上单击完成按钮后,流水线将会自动完成ingress的配置调整,以及老版本的应用下线操作:
此时查看线上应用Ingress信息,如下所示:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}} name: gray-release namespace: default spec: rules: - host: www.example.com http: paths: - backend: serviceName: gray-release-v20201231112858 servicePort: 80 path: / pathType: ImplementationSpecific ---
常见发布失败的问题
当Ingress关联了多个Service实例时发布失败。
当Service关联了多个Deployment实例时发布失败,Service的LabelSelector需要确保与Deployment的labels保持匹配。
线上版本镜像和当前发布镜像未变化时,发布失败。
而发布配置中的容器名称无法匹配到容器定义时发布失败。