什么是Kubernetes部署策略?
部署策略定义了如何创建、升级或降级不同版本的应用程序。在传统软件环境中,应用程序的部署或升级会导致停机和服务中断。Kubernetes可以帮助避免这种情况,它提供了多种部署策略,让您避免或最大限度地减少停机时间。
Kubernetes部署策略是一种声明性语句,它定义应用程序生命周期以及如何应用应用程序更新。它通常在YAML文件中配置为Kubernetes部署对象的一部分。还可以使用命令式方法通过显式命令创建部署。
在本文中,将介绍以下Kubernetes部署策略:
- Rolling Update Deployment(滚动部署):默认策略,允许在不停机的情况下更新一组Pod。它会逐个用新版本替换运行旧版本应用程序的Pod
- Recreate Deployment(重新创建部署):一种全有或全无的部署策略,可让您立即更新应用程序,但需要停机一段时间。它会终止所有Pod并将其替换为新版本
- Ramped slow rollout(渐进式推出):渐进式推出新版本的副本,同时关闭旧副本
- Best-effort controlled rollout(尽最大努力控制推出):指定"最大不可用"参数,该参数指示升级期间现有Pod中有多少百分比不可用,从而使推出速度更快。
- Blue/Green Deployment(蓝绿部署):一种部署策略,在其中创建两个独立但相同的环境,然后转移到新环境。
- Canary Deployment(金丝雀部署):采用渐进式交付方法,一个版本的应用程序服务于大多数用户,另一个较新的版本服务于一小部分测试用户。如果测试部署成功,则会向更多用户推出。
- Shadow Deployment(影子部署):应用程序的新版本("影子"版本)与当前版本一起接收真实世界的流量,但不会影响最终用户。
- A/B testing(A/B测试):同时向一部分用户推出应用程序功能的两个或多个版本,以查看哪个版本在用户参与度、错误率或其他KPI方面表现更好。
注意:虽然Kubernetes支持开箱即用的滚动部署和重新创建部署,但不支持其他类型,并且可能需要定制或专门的工具才能在Kubernetes集群中实现。
Kubernetes部署策略
1. 滚动部署
滚动部署是Kubernetes中的默认部署策略。它会用新版本替换现有版本的Pod,并逐个缓慢更新Pod,且不会造成集群停机。
滚动更新使用就绪探测(Readiness)来检查新Pod是否已准备就绪,然后再开始缩减旧版本的Pod。如果出现问题,您可以停止更新并将其回滚,而无需停止整个集群。
要执行滚动更新,只需使用kubectl set image更新Pod的镜像即可。这将自动触发滚动更新。
要优化部署策略,请更改清单文件的spec:strategy部分中的参数。有两个可选参数maxSurge和maxUnavailable :
- MaxSurge:指定Deployment一次允许创建的最大Pod数量。可以将其指定为整数(例如5),也可以将其指定为所需Pod总数的百分比(例如10%,始终向上舍入到下一个整数)。如果未设置MaxSurge,则隐式默认值为25%。
- MaxUnavailable:指定在部署期间允许的最大不可用Pod数量。与MaxSurge 一样,可以将其定义为绝对数字或百分比。
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
2. 重新创建部署
一种全有或全无的部署策略,可以通过将清单的spec :strategy:type部分设置为Recreate来定义它,如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
strategy:
type: Recreate
replicas: 4
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
startupProbe:
tcpSocket:
port: 80
initialDelaySeconds: 20
periodSeconds: 5
重新创建策略可能会导致停机,因为在确保新Pod与新版本的应用程序一起推出之前,旧的Pod就被删除了。
3. 缓慢推出
渐进式推出会通过创建新副本并删除旧副本来逐步更新Pod。可以选择每次推出的副本数量。还需要确保没有任何Pod变得不可用。
此策略与常规滚动部署的区别在于,可以控制新副本的推出速度。例如,可以定义每次只更新1个或2个节点,以降低更新风险。要定义此行为,请将maxSurge设置为1,将maxUnvailable设置为0。这意味着Deployment将一次滚动一个Pod,同时确保没有Pod不可用。例如,如果有10个Pod,Deployment将确保至少10个Pod一次可用。
以下部署YAML文件执行渐进式部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
4. 尽最大努力控制推出
分阶段推出的缺点是,推出应用程序需要时间,尤其是大规模推出时。另一种选择是“尽最大努力控制推出”。这可以加快推出速度,但需要权衡更高的风险,因为需要容忍节点之间一定比例的停机时间。
这涉及:
- 将maxUnavailable设置为一定百分比,这意味着更新可以容忍一定数量的Pod停机。
- 将maxSurge设置为0,以确保Deployment中始终有相同数量的Pod。这可在更新期间提供最佳的资源利用率。
这样做的目的是尽快更换Pod,同时确保在任意给定时间内停机的Pod数量是有限的。
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 20%
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
5. 蓝绿部署
在Kubernetes中的蓝绿部署中,可以维护部署的两个版本:“蓝”表示当前版本,“绿”表示新版本。
可以使用Kubernetes服务来管理两者之间的流量:
- 最初,服务将所有流量路由到“蓝色”版本。
- 在集群中将“绿色”版本与“蓝色”版本一起部署。
- 一旦“绿色”版本准备就绪并经过测试,请更新服务以将流量路由到“绿色”版本。
- 如果出现任何问题,可以将服务切换回指向“蓝色”版本。
- 通过使用Kubernetes中的标签和选择器,可以轻松控制服务指向哪个版本的部署。
1). blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: blue-myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
startupProbe:
tcpSocket:
port: 80
initialDelaySeconds: 20
periodSeconds: 5
2). green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: green-myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
startupProbe:
tcpSocket:
port: 80
initialDelaySeconds: 20
periodSeconds: 5
3). 切换到版本绿色版本
service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: green
ports:
- protocol: TCP
port: 80
targetPort: http
4). 回滚到蓝色版本(如果需要)
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: blue
ports:
- protocol: TCP
port: 80
targetPort: http
6. 金丝雀部署
金丝雀部署通常用于在应用程序的后端测试一些新功能。同时部署两个或多个应用程序服务或版本,一个运行现有版本,另一个运行新功能。用户逐渐转向新版本,通过将新版本展示给真实用户来验证新版本。如果没有报告错误,则可以将其中一个新版本逐步部署给所有用户。
以下借助Istio来实现Kubernetes Canary Deployment的示例:
1). 创建版本v1的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
namespace: istio-canary
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
2). 创建版本v2的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
namespace: istio-canary
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v2
ports:
- containerPort: 80
3). 创建Service
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: istio-canary
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 80
4). 配置Istio路由
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-app
namespace: istio-canary
spec:
host: my-app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-app
namespace: istio-canary
spec:
hosts:
- my-app
http:
- route:
- destination:
host: my-app
subset: v2
weight: 10
- destination:
host: my-app
subset: v1
weight: 90
此时,Kubernetes集群中应该运行着三个v1版本的实例和一个v2 版本的实例。通过访问my-app服务,您可以验证10%的流量会被路由到v2版本,其余的流量会被路由到v1版本。
5). 调整流量
如果v2版本运行正常,可以逐步增加v2的权重,减少v1的权重,直到完全替代v1版本。
http:
- route:
- destination:
host: my-app
subset: v2
weight: 50
- destination:
host: my-app
subset: v1
weight: 50
7. 影子部署
影子部署是一种方法,其中应用程序的新版本("影子"版本)与当前版本一起接收真实流量,但不会影响最终用户。这种部署策略允许团队实时测试系统在真实负载和数据下的表现。
在Kubernetes中,实现影子部署需要高级流量路由,通常使用Istio或Linkerd等服务网格完成:
- 与当前版本一起部署影子版本。
- 配置服务网格以复制传入流量,将一份副本发送到当前版本,将另一份副本发送到影子版本。
- 监控影子版本的性能和错误,确保其运行符合预期。
- 由于影子版本的结果不会返回给用户,因此它们不受影响。
1). 创建v1版本的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
namespace: istio-shadow
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
2). 影子版本v2的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
namespace: istio-shadow
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v2
ports:
- containerPort: 80
3). 创建Service
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: istio-shadow
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 80
4). 配置Istio路由
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-app
namespace: istio-shadow
spec:
hosts:
- my-app
http:
- route:
- destination:
host: my-app
subset: v1
- mirror:
host: my-app
subset: v2
mirror_percent: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-app
namespace: istio-shadow
spec:
host: my-app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
此时,Kubernetes集群中应该运行着三个v1版本的实例和一个v2版本的实例。通过访问my-app服务,实际生产流量会被路由到v1版本,同时流量的副本会被发送到v2版本。
8. A/B测试
在部署策略中,A/B测试是指同时向一部分用户推出应用程序功能的两个或多个版本,以查看哪个版本在用户参与度、错误率或其他KPI方面表现更好。
Kubernetes中的A/B测试通常需要部署和服务的组合,并可能需要入口控制器或服务网格的帮助来实现更细粒度的流量路由:
- 并行部署功能的两个版本(A和B)。
- 使用Kubernetes服务或入口控制器在两个版本之间拆分流量,例如50%到版本A,50%到版本B。
- 监控两个版本的性能指标和用户反馈。
- 一旦收集到足够的数据,就决定选择性能更好的版本并相应地调整流量,最终逐步淘汰不太优化的版本。
1). 版本A的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
namespace: istio-abtest
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
2). 版本B的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
namespace: istio-abtest
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: ikubernetes/myapp:v2
ports:
- containerPort: 80
3). 创建Service
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: istio-abtest
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 80
4). 配置Istio路由
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-app
namespace: istio-abtest
spec:
host: my-app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-app
namespace: istio-abtest
spec:
hosts:
- my-app
http:
- match:
- headers:
end-user:
exact: user1
route:
- destination:
host: my-app
subset: v2
- route:
- destination:
host: my-app
subset: v1
weight: 50
- destination:
host: my-app
subset: v2
weight: 50
此时,Kubernetes集群中应该运行着两个版本的应用(v1和v2),并且通过访问my-app服务,50%的流量会被路由到v1版本,50%的流量会被路由到v2版本。特定用户(如user1)会被固定路由到v2版本。
选择哪种Kubernetes部署策略?
- 如果停机不是问题,那么重新创建是最容易实施的部署策略。
- 如果停机是一个问题,那么渐进式部署或蓝绿部署通常是一个不错的选择,但蓝绿部署成本更高且设置起来稍微复杂一些。
- 如果对发布的质量没有信心,则应使用Canary和A/B测试。这些策略以及影子将需要额外的集群组件(例如服务网格),从而增加成本和复杂性。