k8s (八) Deployment: 声明式地升级应用
一、手动更新运行在 pod 内的应用程序
- 删除旧版本 pod,使用新版本 pod 替换
- 先新建新 pod 再删除旧版本 pod
- 执行滚动升级操作
二、使用 ReplicationController 实现自动的滚动升级
使用 kubectl rolling-update 虽然可以实现自动滚动升级,但是已经过时:
- 过程中会直接修改创建德对象
- 伸缩请求是由 kubectl 客户端执行而不是 Kubernetes master 执行的,如果在 kubectl 执行升级时失去了网络连接,升级过程将会中断,pod 和 ReplicationController 最终会处于中间状态
- Kubernetes 的原则是直接使用期望副本数来伸缩 pod 而不是通过手动地删除一个 pod 或者增加一个 pod
三、使用 Deployment 声明式地升级应用
3.1. 创建 Deployment
创建服务:如果没有需要创建,和之前章节的一样:
# kubia-svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
type: LoadBalancer
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
kubectl create -f kubia-svc-loadbalancer.yaml
创建 Deployment:
# kubia-deployment-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubia # Deployment 的名称中不再需要包含版本号
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
selector:
matchLabels:
app: kubia
kubectl create -f kubia-deployment-v1.yaml --record ## record 选项会记录历史版本号
可以使用 kubectl describe 查看 Deployment 的详细信息,也可以使用下面的命令查看部署状态:
kubectl rollout status deployment kubia
测试:
while true; do curl http://10.97.183.107; done # 10.97.183.107 是服务的 IP
之前当使用 ReplicationController 创建 pod 时,它们的名称是由 Controller 的名称加上一个运行时生成的随机字符串(如 kubia-m33mv),由 Deployment 创建的三个 pod 名称中均包含一个额外的数字(如 kubia-74967b5695-bpnrl)。这个数字实际上对应 Deployment 和 ReplicaSet 中的 pod 模板的哈希值。如前所述,Deployment 不能直接管理 pod,相反,它创建了 ReplicaSet 来管理 pod。ReplicaSet 的名称中也包含了其 pod 模板的哈希值。Deployment 会创建多个 ReplicaSet,用来对应和管理一个版本的 pod 模板。像这样使用 pod 模板的哈希值,可以让 Deployment 始终对给定版本的 pod 模板创建相同的(或使用已有的) ReplicaSet。
3.2. 升级 Deployment
升级 Deployment 只需修改 Deployment 资源中定义的 pod 模板,Kubernetes 会自动将实际的系统状态收敛为资源中定义的状态。类似于将 ReplicationController 或 ReplicaSet 扩容或者缩容,升级需要做的就是在部署的 pod 模板中修改镜像的 tag, Kubernetes 会收敛系统,匹配期望的状态。
实际上,如何达到新的系统状态的过程是由 Deployment 的升级策略决定的,默认策略是执行滚动更新(策略名为 RollingUpdate) 。另一种策略为Recreate,它会一次性删除所有旧版本的 pod,然后创建新的 pod,整个行为类似于修改 ReplicationController 的 pod 模板,然后删除所有的 pod。
为便于观察,先设置减慢滚动升级速度:
kubectl patch deployment kubia -p '{"spec": {"minReadySeconds": 10}}'
触发滚动升级:
kubectl set image deployment kubia nodejs=luksa/kubia:v2
注:在 Kubernetes 中修改资源:
方法 | 作用 |
---|---|
kubectl edit | 使用默认编辑器打开资源配置。修改保存并退出编辑器,资源对象会被更新。 |
kubectl patch | 修改单个资源属性 |
kubectl apply | 通过一个完整的 YAML 或 JSON 文件,应用其中新的值来修改对象。如果 YAML/JSON 中指定的对象不存在,则会被创建。该文件需要包含资源的完整定义。 |
kubectl replace | 将原有对象替换为 YAM/JSON 文件中定义的新对象。与 apply 命令相反,运行这个命令前要求对象必须存在,否则会打印错误。 |
kubectl set image | 修改 Pod、ReplicationController、Deployment、DemonSet、Job 或 ReplicaSet 内的镜像 |
这些方式在操作 Deployment 资源时效果是一样的,修改后都会触发滚动升级过程。
3.3. 回滚 Deployment
回滚到上一个版本:
kubectl rollout undo deployment kubia
undo 命令也可以在滚动升级过程中运行,会直接停止滚动升级,在升级过程中已创建的 pod 会被删除并被老版本的 pod 替代。
显示升级历史:
kubectl rollout history deployment kubia
回滚到特定版本:
kubectl rollout undo deployment kubia --to-revision=1
3.4. 控制滚动升级速率(maxSurge、maxUnavailable)
maxSurge: 决定了 Deployment 配置中期望的副本数之外,最多允许超出的 pod 实例的数量。默认值为 25%,当把百分数转换成绝对值时会将数字四舍五入;值也可以不是百分数而是绝对值。
maxUnavailable: 决定了在滚动升级期间,相对于期望副本数能够允许有多少 pod 实例处于不可用状态。默认值也是 25%,当把百分数转换成绝对值时会将数字四舍五入;值也可以不是百分数而是绝对值。
3.5. 暂停滚动升级
暂停:
kubectl rollout pause deployment kubia
恢复:
kubectl rollout resume deployment kubia
3.6. 阻止出错版本的滚动升级
minReadySeconds 的主要功能是避免部署出错版本的应用,而不只是单纯地减慢部署的速度。minReadySeconds 属性指定新创建的 pod至少要成功运行多久之后,才能将其视为可用。在 pod 可用之前,滚动升级的过程不会继续,当所有容器的就绪探针返回成功时, pod 就被标记为就绪状态。如果一个新的 pod 运行出错,就绪探针返回失败,如果一个新的 pod 运行出错,并且在 minReadySeconds 时间内它的就绪探针出现了失败,那么新版本的滚动升级将被阻止。
配置就绪探针来阻止全部版本的滚动部署:
# kubia-deployment-v3-with-readinesscheck.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubia
spec:
replicas: 3
minReadySeconds: 10 # minReadySeconds
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 设置为 0 来确保升级过程中 pod 被挨个替换
type: RollingUpdate
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v3
name: nodejs
readinessProbe:
periodSeconds: 1 # 定义一个就绪探针并每隔 1s 执行一次
httpGet:
path: / # 就绪探针会执行发送 HTTP GET 请求到容器
port: 8080
selector:
matchLabels:
app: kubia
kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
可以看到升级没有成功,因为新的 pod 启动时,就绪探针会每隔 1s 发起请求,而 luksa/kubia:v3 在第 5 个请求开始一直返回 HTTP 状态码 500,因此 pod 会从 Service 的 endpoint 中移除。
默认情况下 10 分钟内不能完成滚动升级的话,将被视为失败,可以手动设定超时时间。
取消出错版本的滚动升级:
因为滚动升级过程不再继续,所以取消滚动升级:
kubectl rollout undo deployment kubia