deployment部署策略

Recreate
重建更新策略,先删除当前副本控制集(RS)管理的所有Pods,而后创建基于新模板的副本控制集(RS),使用新版本的副本控制集(RS)创建新版本的Pods。通常,只应该在应用程序的新旧本不兼容(如依赖的后端数据库的schema不同且无法兼容)时,运行才会使用recreate策略,因为他会导致应用更新期间暂时不可用,好处在于不存在中间状态,用户要么是应用的新版本,要么是旧版本。
RollingUpdate
滚动更新(RollingUpdate)一次仅更新一批Pod,当更新的Pod就绪(可用)后,再更新另一批,直到全部更新完成为止,该策略实现了不间断服务的目标,在更新过程中可能会出现不同的应用版本且并存,同时提供服务的情况,Rolling Update升级策略分为三个步骤:
- 创建新的RS,然后根据新的镜像运行新的Pod。
- 删除旧的Pod,启动新的Pod,当新Pod就绪后,继续删除旧Pod,启动新Pod。
- 持续第二步过程,直到所有Pod都被更新成功。
注意 1:注意滚动升级步骤第2步,存在先增新后减旧、先减旧后增新、同时增减(少减多增)多种情况,具体与滚动升级策略属性spec.strategy.rollingUpdate.maxSurge和spec.strategy.rollingUpdate.maxUnavailable配置相关,此部分会在下文进行详细解释。
注意 2:在Deployment的滚动升级期间,可以通过以下方式判断Pod是否处于可用状态:Pod配置就绪探针(Readiness Probe)的情况下:如果就绪探针的探测结果为成功,则表示容器已经准备好接收流量,该Pod被认为是就绪状态。Pod没有就绪探针的情况下:判断Pod是否就绪的依据主要是基于Pod的运行状态,一般Pod的状态为Running则认为Pod是就绪状态。
注意 3:修改 Deployment 控制器的 minReadySeconds、replicas 和 strategy 等字段的值并不会触发 Pod 资源的更新操作,因为它们不属于 template 的内嵌字段,对现存的 Pod 对象不产生任何影响。
相关字段

实战
echo "apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-demo
namespace: default
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # 向上取整
maxUnavailable: 25% # 向下取整
replicas: 10
selector:
matchLabels:
app: nginx-deployment
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- name: nginx
image: nginx:1.23
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80" | kubectl apply -f -
kubectl annotate deployments.apps deployment-demo kubernetes.io/change-cause="set image nginx:1.23"
触发更新
kubectl set image deploy deployment-demo nginx=nginx:1.24
kubectl annotate deployments.apps deployment-demo kubernetes.io/change-cause="set image nginx:1.24"

版本回退


金丝雀(灰度发布)
金丝雀发布是一种软件发布策略,其中应用的新版本被发布到一小部分用户客户端进行测试,这些用户就像 “金丝雀” 一样,可以提供对新版本功能和性能的真实反馈。 新版本如果没有问题,可以逐步扩大到更多的用户客户端,如果出现问题时,可以迅速回滚或修复。金丝雀发布核心目的在于减少对已有系统的潜在影响,提高发布过程的可控性。
可以利用 Kubernetes 中 Deployment + Ingress 两者的结合,简单平滑地将金丝雀发布流程集成到 Kubernetes 架构体系中。
具体的流程如下:
- 创建应用的金丝雀版本 (新版本) 镜像和 Deployment (资源请求和限制,副本数量,HPA 等)
- 配置服务发现,为金丝雀版本应用 Pod 配置专属的 Service
- 流量切分,将生产环境的一小部分流量切分到金丝雀版本中
- 持续监控 (业务稳定三板斧: 日志, 监控指标, 链路追踪)
- 渐进式更新 (逐步加大金丝雀版本的流量比例) 或及时回滚 (只需要将金丝雀版本流量比例重置为 0 即可)
- 删除旧的生产 Service 和 Deployment

业务流程
上面谈到了 Kubernetes 中金丝雀发布的技术方案,下面简单说一下业务方面需要哪些配合工作。
- 后台发放金丝雀版本的客户端 (用户) 名额,可以根据用户画像发放,也可以随机发放
- 客户端收到金丝雀版本更新后,提示当前用户是否更新到金丝雀版本
- 用户下载更新金丝雀版本应用 (例如 Android 绕过应用商城直接下载安装,IOS 通过官方 TestFlight 方案)
- 金丝雀版本应用访问时,流量被切分到金丝雀版本的 Pod, 产生对应的日志, 监控指标, 链路追踪数据
流量切分
上文中谈到的将生产环境的一小部分流量切分到金丝雀版本中,下面来介绍两种常见的业务金丝雀部署场景。 限于篇幅,本文仅讨论使用 Kubernetes 原生功能作为流量切分方案,不考虑 Istio, Linkerd 等 Service Mesh 方案。
REST接口不同
这种实现方案是最简单的,我们可以直接使用 Ingress + Service 来实现,通过将不同版本的接口路由到不同的服务即可,例如:
- 当前运行版本的路由
/api/v1/users/:id/profile流量切分到生产应用版本的 Service - 金丝雀版本的路由
/api/v2/users/:id/profile流量切分到金丝雀版本的 Service
其中,Deployment 对应的 yaml 声明式代码大致如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-app
labels:
version: prod # 标签 = prod
spec:
# 定义生产版本的 Deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-app
labels:
version: canary # 标签 = canary
spec:
# 定义金丝雀版本的 Deployment
Service 对应的 yaml 声明式代码大致如下:
---
apiVersion: v1
kind: Service
metadata:
name: app-prod-service
spec:
selector:
version: prod # 将流量路由到标签为 prod 的 Pod
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app-canary-service
spec:
selector:
version: canary # 将一部分流量路由到标签为 canary 的 Pod
ports:
- protocol: TCP
port: 80
targetPort: 80
Ingress 对应的 yaml 声明式代码大致如下 (这里以 Nginx Ingress Controller 为例):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/service-upstream: "true"
spec:
ingressClassName: nginx
rules:
- host: www.lab.example.com
http:
paths:
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: app-prod-service
port:
number: 80
weight: 10 # 权重 1:10
- path: /api/v2/users
pathType: Prefix
backend:
service:
name: app-canary-service
port:
number: 80
weight: 1 # 权重 1:10
REST 接口版本相同
现实中更常见的场景是: 应用的生产版本和金丝雀版本使用相同的 REST 接口,但是需要将流量切分到不同版本中,同样可以使用 Ingress 来实现。
Nginx Ingress Controller 下列 canary-* Annotation 来支持金丝雀发布机制,下面是几个常用字段的描述。
| 字段 | 说明 |
|---|---|
| nginx.ingress.kubernetes.io/canary | true:启用 canary false:不启用 canary |
| nginx.ingress.kubernetes.io/canary-by-header | 基于请求头的名称进行金丝雀发布 |
| nginx.ingress.kubernetes.io/canary-by-header-value | 基于请求头的值进行金丝雀发布 |
| nginx.ingress.kubernetes.io/canary-by-header-value | 基于请求头的值进行金丝雀发布 |
| nginx.ingress.kubernetes.io/canary-weight | 基于权重进行金丝雀发布 (0 - 100) |
Service 对应的 yaml 声明式代码直接复用上文中的即可,这里不再赘述,Ingress 对应的 yaml 声明式代码大致如下:
---
# 生产版本 Ingress 配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prod
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: www.lab.example.com
http:
paths:
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: app-prod-service
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: canary
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# 启用金丝雀发布
# nginx.ingress.kubernetes.io/canary: "true"
# 这里以 Request.Header 中的 app_release_version 作为金丝雀流量标识名称
#nginx.ingress.kubernetes.io/canary-by-header: "app_release_version"
# 这里以 v1.2.3 版本号作为金丝雀流量标识值
# nginx.ingress.kubernetes.io/canary-by-header-value: "v1.2.3"
# 给金丝雀版本切分 10% 的流量
# nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: www.lab.example.com
http:
paths:
- path: /api/v2/users
pathType: Prefix
backend:
service:
name: app-canary-service
port:
number: 80
触发更新的方式
kubectl apply -f | kubectl replace -f | kubectl patch
kubectl edit
kubectl set | kubectl scale | kubectl autoscale
kubectl set env子命令说明

kubectl set image子命令说明

kubectl set resource子命令说明

kubectl set serviceaccount子命令说明

回滚
