6.1 Deployment 简介
Deployment 资源对象为 Pod 和 ReplicaSet(ReplicationController 的升级版)提供了声明式定义,在 Deployment YAML 文件中描述期望达到的目标状态,Deployment Controller 会创建符合目标状态的 ReplicaSet 和 Pod,如果更新 Deployment,对应的 ReplicaSet 和 Pod 的状态也会改变以符合新的目标状态。
Deployment 的典型应用场景包括:
- 定义 Deployment 来创建 ReplicaSet 和 Pod
使用 Deployment 来创建 ReplicaSet,ReplicaSet 又会在后台创建 Pod。可以检查 Pod 的启动状态,观察是成功还是失败。
也可以根据 Deployment 的状态判断上线是否 hang 住了。 - 滚动升级和回滚应用
通过更新 Deployment 的 PodTemplateSpec 字段来声明 Pod 的新状态,更新 Deployment 后会创建一个新的 ReplicaSet,Deployment 会按照控制的速率删除由旧的 ReplicaSet 创建的 Pod、并使用新的 ReplicaSet 创建新的 Pod,当新生成的 Pod 达到要求的目标状态后,会清除旧的 ReplicaSet。
如果当前状态不稳定,可以回滚到之前的 Deployment revision,每次回滚都会更新 Deployment 的 revision。
- 扩容和缩容
根据系统负载进行 Deployment 的扩容和缩容。 - 暂停和继续 Deployment
暂停 Deployment 并修改 PodTemplateSpec 字段,然后重新恢复上线。
运行示例
定义一个 Deployment YAML 文件,它可以创建一个 Replica Set 来启动 3 个 nginx Pod,新建 nginx-deployment.yaml 文件,并向其中写入如下内容:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
ports:
- containerPort: 80
执行创建:
$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
查看 Deployment 的状态、RS 的状态、以及运行的 Pod 副本数量:
# 可以看到已经创建了 3 个符合要求的 Pod,这 3 个 Pod 都是最新的、都处于可用状态
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 34s
# 该 RS 需要 3 个 Pod,当前符合要求的有 3 个 Pod 并且已经创建好,RS 名字的组成:<Deployment 的名字>-<Pod template 的 hash 值>
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-56f8998dbc 3 3 3 92s
# 使用 nginx-deployment-56f8998dbc RS 创建的 3 个 Pod
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-56f8998dbc-6w9ll 1/1 Running 0 2m14s
nginx-deployment-56f8998dbc-nwv2w 1/1 Running 0 2m14s
nginx-deployment-56f8998dbc-spc6s 1/1 Running 0 2m14s
查看 nginx-deployment 的详细信息:
$ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: user-test
CreationTimestamp: Thu, 12 Nov 2020 21:21:39 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-56f8998dbc (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m57s deployment-controller Scaled up replica set nginx-deployment-56f8998dbc to 3
在 Annotations 记录了版本 revision: 1
默认更新策略使用的是 RollingUpdate
max unavailable 和 max surge 的默认值都为 25%
ReplicaSet 为 nginx-deployment-56f8998dbc
6.2 Deployment 的升级
设想一个场景,服务需要升级,传统的做法是:暂停与该服务相关的所有 Pod,下载新镜像并创建新的 Pod,这种方式不好的地方是升级的过程中,会导致服务不可用,进而影响整体的产品体验。因此 Kubernetes 提供了滚动升级功能来解决这个问题。
通常使用 Deployment 来创建 Pod 资源对象,当 Pod 在运行中,可以修改 Deployment 的 Pod 定义或是镜像名称,并应用到 Deployment 对象上,这样就完成了 Deployment 的自动更新操作。在更新过程中发生了错误,可以通过回滚操作恢复 Pod 的版本。
这里我们将 Pod 的镜像更新为 nginx:1.19:
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.19
deployment.apps/nginx-deployment image updated
也可以使用 kubectl edit 命令进行更新,把 image 字段下的 nginx:1.9.1 更改为 nginx:1.19
$ kubectl edit deployment/nginx-deployment
deployment.extensions/nginx-deployment edited
上面修改了镜像名,就会触发系统完成 Deployment 所有运行 Pod 的滚动升级操作,查看 rollout 的状态:
$ kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
需要注意的是:当且仅当 Deployment 的 .spec.template 字段下的 labels 或是 image 更新时才会触发 rollout,其它的更新并不会触发 rollout。
查看更新后的 Deployment、RS、Pods:
# 可以看到 UP-TO-DATE 的数量已经变成了 3,说明已经全部更新完成
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 43m
# 查看两个 RS 的最终状态,可以看到旧的 RS 已经没有 Pod 在运行了,新的 RS 创建好了 3 个新的 Pod
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-56f8998dbc 0 0 0 43m
nginx-deployment-687548bb8c 3 3 3 4m58s
# 由新 nginx-deployment-687548bb8c RS 创建出来的 3 个 Pod
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-687548bb8c-89268 1/1 Running 0 5m22s
nginx-deployment-687548bb8c-fff9j 1/1 Running 0 5m29s
nginx-deployment-687548bb8c-vzxbp 1/1 Running 0 5m37s
查看现在 nginx-deployment 的详细信息:
$ kubectl describe deployments/nginx-deployment
...
NewReplicaSet: nginx-deployment-687548bb8c (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 45m deployment-controller Scaled up replica set nginx-deployment-56f8998dbc to 3
Normal ScalingReplicaSet 6m48s deployment-controller Scaled up replica set nginx-deployment-687548bb8c to 1
Normal ScalingReplicaSet 6m40s deployment-controller Scaled down replica set nginx-deployment-56f8998dbc to 2
Normal ScalingReplicaSet 6m40s deployment-controller Scaled up replica set nginx-deployment-687548bb8c to 2
Normal ScalingReplicaSet 6m33s deployment-controller Scaled down replica set nginx-deployment-56f8998dbc to 1
Normal ScalingReplicaSet 6m33s deployment-controller Scaled up replica set nginx-deployment-687548bb8c to 3
Normal ScalingReplicaSet 6m32s deployment-controller Scaled down replica set nginx-deployment-56f8998dbc to 0
从 Events 事件中我们可以看到 Pod 的更新过程:
- 系统创建了一个新的 RS(nginx-deployment-687548bb8c),将其副本数量扩展到 1;将旧的 RS(nginx-deployment-56f8998dbc)副本数量缩减为 2
- 新的 RS 副本数量扩展到了 2,旧的 RS 副本数量缩减为 1
- 新的 RS 副本数量扩展到了 3,旧的 RS 副本数量缩减为 0
就是通过这样逐步替换的过程完成了整个 Pod 的更新,这个过程中保证了整体服务不中断,并且副本数量始终维持为用户指定的数量。
Deployment 通过字段 spec.strategy 设置 Pod 更新策略,上面创建的 Deployment 的相关默认设置如下:
spec:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
Deployment 的更新策略有两种:
- Recreat(重建): 先 kill 掉所有正在运行的 Pod,然后创建新的 Pod。
- RollingUpdate(滚动更新): 逐个更新 Pod。可以在字段 spec.strategy.rollingUpdate 下设置两个参数 maxSurge 和 maxUnavailable 来控制滚动更新的过程。
关于两个参数的详细说明:
- maxUnavailable:指定更新过程中不可用状态的 Pod 数量上限。默认值 25% 指的是:Pod 期望副本数的 25%,然后系统会以向下取整的方式计算出绝对值(整数)。对于这里而言,当开始滚动更新时,旧的 RS 立即将副本数缩小到所需副本总数的 75%(3 * 75% = 2.25,然后向下取整,绝对值为 2),一旦新的 Pod 创建并准备好,旧的 RS 会进一步缩容,新的 RS 会进一步扩容,整个过程中系统在任意时刻都可以确保可用状态的 Pod 总数至少占 Pod 期望副本总数的 75%。
- maxSurge:指定更新过程中 Pod 总数超过 Pod 期望副本数部分的最大值。默认值 25% 指的是:Pod 期望副本数的 25%,然后系统会以向上取整的方式计算出绝对值(整数)。对于这里而言,新的 RS 可以在滚动更新开始时立即进行副本数扩容,只要保证新旧 RS 的 Pod 副本总数之和不超过期望副本数的 125% 即可。一旦旧的 Pod 被 kill 掉,新的 RS 会进一步扩容。在整个过程中系统在任意时刻都能确保新旧 RS 的 Pod 副本总数之和不超过所需副本数的 125%。
6.3 Deployment 的回滚
有的时候由于升级后的 Deployment 不稳定,我们需要将 Deployment 回滚到旧的版本。默认情况下,kubernetes 会在系统中保存所有 Deployment 的 rollout 历史记录,方便随时回退。
这里需要注意的是,只有更新 Deployment template 中的 label 和 image,才会创建一个新的 revision,而扩缩容不会创建 revision,所以回退历史 revision 时,只有 Deployment 中的 template 部分才会回退。
比如先进行一次升级,要修改容器的镜像,但是不小心手误写成了 nginx:1.91,这是一个不存在的版本镜像,所以 Deployment 不会更新成功:
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment.apps/nginx-deployment image updated
查看 Deployment 的部署过程以及 RS 的状态:
# 可以看到部署过程卡住了,按 Ctrl+C 终止查看
$ kubectl rollout status deployments nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
^C
# 新创建的 nginx-deployment-8456d5d8c5 RS 也卡在了第一个 Pod 的创建过程中,主要是镜像拉取不成功
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-546bbd4947 3 3 3 96s
nginx-deployment-8456d5d8c5 1 1 0 71s
使用 kubectl rollout history 命令查看部署的历史记录,由于我们在创建 Deployment 的时候没有使用 --record 参数,CHANGE-CAUSE 中没有记录下更新每个版本使用的命令,在创建的时候可以把这个参数加上:
$ kubectl rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
使用 --revision=<N> 参数可以查看特定版本的详细信息:
$ kubectl rollout history deployment nginx-deployment --revision=2
deployment.apps/nginx-deployment with revision #2
Pod Template:
Labels: app=nginx
k8s=nginx-pod
pod-template-hash=8456d5d8c5
Containers:
nginx:
Image: nginx:1.91
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
撤销本次发布并回滚到上一个部署版本:
$ kubectl rollout undo deployment/nginx-deployment
deployment.extensions/nginx-deployment rolled back
# 也可以使用参数 --to-revision 指定回滚到的部署版本
$ kubectl rollout undo deployment/nginx-deployment --to-revision=1
现在检查 Deployment 是否回滚到了上一个版本:
$ kubectl describe deployments.apps nginx-deployment
...
NewReplicaSet: nginx-deployment-546bbd4947 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m52s deployment-controller Scaled up replica set nginx-deployment-546bbd4947 to 3
Normal ScalingReplicaSet 5m27s deployment-controller Scaled up replica set nginx-deployment-8456d5d8c5 to 1
Normal ScalingReplicaSet 25s deployment-controller Scaled down replica set nginx-deployment-8456d5d8c5 to 0
从 Events 事件中可以看到 Deployment 回滚的过程是:先创建一个新 RS(nginx-deployment-546bbd4947)扩容到 3 个 Pod,然后旧的 RS(nginx-deployment-8456d5d8c5)的 Pod 从 1 缩减为 0。
6.4 Deployment 的暂停与恢复
有的时候需要频繁的对 Deployment 的配置进行修改,如果每修改一次就触发一次更新的话会显得比较麻烦,这个时候可以暂停 Deployment 的更新操作,多次修改配置,然后再恢复 Deployment,一次性触发完整的更新操作。
这里先删除掉前面创建的 nginx-deployment,然后重新使用文件进行创建(注意创建的时候可以加上 --record 参数):
$ kubectl create -f nginx-deployment.yaml --record
deployment.apps/nginx-deployment created
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 22s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-546bbd4947 3 3 3 32s
使用命令 kubectl rollout pause 暂停 Deployment 的更新操作:
$ kubectl rollout pause deployment nginx-deployment
deployment.apps/nginx-deployment paused
修改 Deployment 的镜像信息为 nginx:1.9.1:
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated
查看更新历史记录,可以看到并没有触发对应的更新操作:
$ kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 kubectl create --namespace=user-test --filename=nginx-deployment.yaml --record=true
这里再一次更新 Deployment 配置,限制容器的资源使用:
$ kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-deployment resource requirements updated
恢复 Deployment 的部署操作:
$ kubectl rollout resume deploy nginx-deployment
deployment.apps/nginx-deployment resumed
查看 RS 资源和 Deployment 的详细信息:
# 可以看到新创建的 RS(nginx-deployment-5bd45c7d95)
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-546bbd4947 0 0 0 5m55s
nginx-deployment-5bd45c7d95 3 3 3 46s
# 观察 nginx-deployment 的配置信息是否修改
$ kubectl describe deployment nginx-deployment
...
Annotations: deployment.kubernetes.io/revision: 2
kubernetes.io/change-cause: kubectl create --namespace=user-test --filename=nginx-deployment.yaml --record=true
...
Pod Template:
Labels: app=nginx
k8s=nginx-pod
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
Limits:
cpu: 200m
memory: 512Mi
...
评论区