pod 相关知识点-2
Init Container(初始化容器)
在很多应用场景中,应用在启动之前都需要进行如下初始化操作。
- 等待其他关联组件正确运行(例如数据库或某个后台服务)
- 基于环境变量或配置模板生成配置文件。
- 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中
- 下载相关依赖包,或者对系统进行一些预配置操作。
init container(初始化容器)用于在启动应用容器(app container) 之前启动一个或多个初始化容器,完成应用容器所需的预置条件,如图:

init container与应用容器在本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功执行完成后,系统才能继续执行下一个容器,根据Pod的重启策略(RestartPolcy),当Init container 执行失败,而且设置了 RestartPolicy=Never 时,Pod将会启动失败;而设置RestartPolicy=Always时,Pod将会被系统自动重启。
下面以Nginx 应用为例,在启动Nginx之前 ,通过初始化容器busybox 为Nginx创建一个index.html 主页文件,这里为 init container 和Nginx 设置一个共享的 Volume ,以供 Nginx 访问init container 设置的 index.html 文件;
nginx-init-containers.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
annotations:
spec:
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://kubernetes.io
volumeMounts:
- name: workdir
mountPath: "/work-dir"
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
创建这个 Pod:
kubectl create -f nginx-init-container.yaml
在运行init container的过程中,查看Pod的状态,可见init 过程还未完成
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:0/1 0 9s
在 init container 成功执行完成后,系统继续启动 Nginx容器,再次查看Pod 的状态:
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3h38m
查看Pod的事件,可以看到系统首先创建并运行了 init container 容器(名为install )成功后,继续创建和运行Nginx容器
[root@master ~]# kubectl describe pod nginx
…………
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 27s default-scheduler Successfully assigned default/nginx to node1
Normal Pulling 27s kubelet Pulling image "busybox"
Normal Pulled 12s kubelet Successfully pulled image "busybox" in 15.413337033s
Normal Created 12s kubelet Created container install
Normal Started 12s kubelet Started container install
Normal Pulling 11s kubelet Pulling image "nginx"
Normal Pulled 10s kubelet Successfully pulled image "nginx" in 652.777329ms
Normal Created 10s kubelet Created container nginx
Normal Started 10s kubelet Started container nginx
成功后,登录进Nginx容器,可以看到 /usr/share/ngixn/html 目录下的 index.html 文件为 init container 所生成。
init container与应用容器的区别如下:
(1)、init container 的运行方式与应用容器不同,它们必须先于应用容器执行完成,当设置了多个 Init container 时,将按顺序逐个运行,并且只有前一个 init container 运行成功后才能运行后一个init container 。当所有 init container 都成功运行后,Kubernetes 才会初始化 Pod的各种信息,并开始创建和运行应用容器。
(2)、在 init container 的定义中也可以设置资源限制,Volume的使用和安全策略,等等。但资源限制的设置与应用容器的策略不同。
- 如果多个 init container 都定义了资源请求/资源限制,则取最大值作为所有 init container 的资源请求值/资源限制值。
- Pod的有效(effective)资源请求值/资源限制值取以下二者中的较大值。
a) 所有应用容器的资源请求值/资源限制值之和
b) init container 的有效资源请求值/资源限制值 - 调度算法将基于Pod的有效资源请求值/资源限制值进行计算,也就是说init container 可以为初始化操作预留系统资源,即使后续应用容器无须使用这些资源。
- Pod的有效Qos等级适用于 init container 和应用容器。
- 资源配额和限制将根据Pod的有效资源请求值/限制值计算生效。
- Pod 级别的将基于Pod的有效请求/限制,与调度机制一致。
(3)、init container 不能设置readinessProbe的探针,因为必须在它们成功运行后才能继续运行在Pod中定义的普通容器。
在Pod重新启动时,init container 将会重新运行,常见的Pod重启场景如下 :
- init container 的镜像被更新时,init container 将会重新运行,导致Pod重启。仅更新应用容器的镜像只会使得应用容器被重启。
- Pod 的 infrastructure(基础设施) 容器更新时,Pod将会被重启。infrastructure 我理解为pause容器
- 若Pod中的所有应用容器都终止了,并且 RestartPolicy=Always,则Pod会重启。
Pod 的升级与回滚
当集群中的某个服务需要升级时,我们需要停止目前与服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模比较大,则这个工作变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。Kubernetes 提供了滚动升级功能来解决上面的问题。
如果Pod是通过Deployment创建,则用户可以在运行时修改Deployment 的Pod 定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则还可以通过回滚操作恢复Pod的版本。
Deployment的升级
以Deployment nginx为例:
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.7.9
ports:
- containerPort: 80
已运行的Pod 副本数量有3个:
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d59d67564-7khp8 1/1 Running 0 67s
nginx-deployment-5d59d67564-skhbt 1/1 Running 0 67s
nginx-deployment-5d59d67564-x8gx6 1/1 Running 0 67s
现在Pod镜像需要被更新为 Nginx1.9.1 ,我们可通过kubectl set image
命令,为Deployment设置新的镜像名称:
[root@master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated
另一种更新的方法是使用Kubectl edit 命令修改Deployment 的配置,将spec.template.spec.containers[0].image 从Nginx1.7.9 更改为 1.9.1:
kubectl edit deployment/nginx-deployment
一旦镜像名(或Pod定义)发生了修改,则将触发系统完成Deployment 所有运行Pod的滚动升级操作。可以使用kubectl rollout status 命令查看Deployment的更新过程:
[root@master ~]# kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
查看Pod使用的镜像,已经更新为Nginx1.9.1 了
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 107m
nginx-deployment-69c44dfb78-drltm 1/1 Running 0 64s
nginx-deployment-69c44dfb78-jkl2c 1/1 Running 0 63s
nginx-deployment-69c44dfb78-ks25g 1/1 Running 0 61s
[root@master ~]# kubectl describe pod nginx-deployment-69c44dfb78-drltm
Name: nginx-deployment-69c44dfb78-drltm
Namespace: default
Priority: 0
Node: node1/192.168.200.2
Start Time: Wed, 02 Mar 2022 09:04:49 -0500
Labels: app=nginx
pod-template-hash=69c44dfb78
…………
Containers:
nginx:
Container ID: docker://4d2136c54bbb96df85baef5b316e565e216a03193f09b57951049d12e799fbf5
Image: nginx:1.9.1
Image ID: docker-pullable://nginx@sha256:2f68b99bc0d6d25d0c56876b924ec20418544ff28e1fb89a4c27679a40da811b
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Wed, 02 Mar 2022 09:04:50 -0500
我们可以使用 kubectl describe deployments/nginx-deployment
,命令仔细观察Deployment 的更新过程。初始创建Deployment时,系统创建了一个 ReplicaSet(nginx-deployment-5d59d67564),并按用户的需求创建了3个Pod副本,当更新Deployment 时,系统创建了一个新的ReplicaSet(nginx-deployment-69c44dfb78),并将其副本数量扩展到1,然后将旧的ReplicaSet缩减为2,之后 ,系统继续按照相同的更新策略对新旧两个ReplicaSet 进行逐个调整。最后,新的ReplicaSet 运行3个新版本Pod副本,旧的ReplicaSet缩减为0。如图 :

PS: 使用 kubectl describe deployment/nginx-deployment
的详细信息。
运行kubectl get rs
命令,查看两个ReplicaSet的最终状态。
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5d59d67564 0 0 0 24h
nginx-deployment-69c44dfb78 3 3 3 24h
在默认情况下,Deployment确保可用的Pod总数至少为所需的副本数量(DESIRED 期望值)减 1 ,也就是最多 1个不可用(maxUnavailable=1) .Deployment 还需要确保在整个更新过程中Pod的总数最不会超过所需的副本数量太多。在默认情况下,Deployment 确保Pod的总数最多比所需的Pod数多1个,也就是最多1个(maxSurge=1)。Kubernetes 从 1.6版本开始,maxUnavailable 和maxSurge 的默认值将从1、1 更新为所需副本数量的 25%、25%.这样,在升级过程中,Deployment 就能保证服务不中断,并且副本数量始终维持为用户指定的数量(DESIRED).
对更新策略说明如下 :
在Deployment 的定义中,可以通过spec.strategy 指定Pod更新的策略,目前支持两种策略:Recreate(重建)和RollingUpdate(滚动更新),默认值为 RollingUpdate。
- Recreate:设置spec.strategy.type=Recreate,表示Deployment 在更新Pod时,会先杀掉所有正在运行的Pod,然后创建新的Pod。
- RollingUpdate:设置 spec.strategy.type=RollingUpdate,表示Deployment 会以滚动更新的方式逐个更新Pod。同时,可以通过设置spec.strategy.rollingUpdate 下面的两个参数(maxSurge和maxUnavailable)来控制滚动更新的过程。
下面对滚动更新时两个主要参数的说明如下: - spec.strategy.rollingUpdate.maxUnavailable: 用于指定Deployment在更新过程中不可用状态的Pod数量的上限。该maxUnavailable 的数值可以是绝对值(例如5)或Pod期望的副本数的百分比(例如10%),如果被设置为百分比,那么系统会先以向下取整的方式计算出绝对值(整数)。而当另一个参数maxSurge 被设置为0时,maxUnavailable 则必须被设置为绝对数值大于 0 。例如maxUnavailable=30%,则整个更新过程中,只需要保证可用Pod的总数至少点Pod期望副本总数的70% 。
- spec.strategy.rollingUpdate.maxSurge : 用于指定在Deployment 更新Pod的过程中Pod总数超过Pod期望副本数部分的最大值。该maxSurge 的数值可以是绝对值(例如 5) 或Pod期望期望副本数的百分比(例如 10%)。如果设置为百分比,那么系统会先按照向上取整的计算方式计算出绝对值(开整数)。如果maxSurge=30%,则整个更新过程,需要保证新旧Pod副本数之和不超过期望副本数130% 即可。
这里需要注意多重更新(Rollover的情况)。如果Deployment的上一次更新正在进行,此时用户再次发起Deployment的更新操作,那么Deployment会为每一次更新都创建一个ReplicaSet,而每次在新的ReplicaSet创建成功后,会逐个增加Pod副本数,同时将之前正在扩容 的ReplicaSet停止扩容 (更新),并将其加入旧版本ReplicaSet 列表中,然后开始缩容至0的操作。
相关信息
假设我们创建一个Deployment,这个Deployment开始创建5个Nginx:1.7.9 的Pod副本。在这个创建Pod动作尚未完成时,我们又将Deployment进行更新,在副本数不变的情况下将Pod模板中的镜像修改为Nginx:1.9.1,又假设此时Deployment已经创3个Nginx:1.7.9 的Pod副本,则Deployment会立即杀掉已经创建的3个Nginx1.7.9 Pod,并开始创建Nginx:1.9.1 Pod。Deployment 不会在等Nginx:1.7.9 的Pod创建到5个之后再进行更新操作。
还需要注意更新Deployment的标签选择器(Label Selector)的情况。通常来说,不鼓励更新Deployment的标签选择器,因为这样会导致Deployment选择的Pod列表发生变化,也可能与其他控制器产生冲突。如果一定要更新,需要谨慎操作。关于Deployment更新标签选择器的注意事项如下:
(1)、添加选择器标签时,必须同步修改Deployment配置的Pod的标签,为Pod添加新的标签,否则Deployment的更新会报验证错误而失败:

当我们修改了Deployment的标签选择器,而没有修改Pod的时就会出现如下报错:

添加标签选择器是无法向后兼容的,这意味着亲的标签选择器不会匹配和使用旧选择器创建的ReplicaSets 和Pod,因此添加选择器将会导致所有旧版本的ReplicaSets 和由旧版本ReplicaSet创建的Pod处于孤立状态(不会被系统自动删除,也不受新的ReplicaSet控制)。
为标签选择器和Pod模板添加新的标签(使用 kubectl edit deployment 命令)后,效果如下 :(实验未完成)
说明
在 API 版本 apps/v1
中,Deployment 标签选择算符在创建后是不可变的。
(2)、更新标签选择器,即更改选择器中标签的键或值,也会产生与添加选择器标签类似的效果。
(3)、删除标签选择器,即从Deployment的标签器中删除一个或多个标签,该Deployment的ReplicaSet和Pod不会受到任何影响。但需要注意的是,被删除的标签仍会存在于现有的Pod和ReplicaSet上。
Deployment 的回滚
有时新的Deployment不稳定时,我们可能需要将Deployment 回滚到旧版本。在默认情况下,所有Deployment 的发布历史记录都被保留在系统中,以便于我们随时进行回滚(可以配置历史记录数量)
假设在更新Deployment镜像时,将容器名误设置成 Nginx:1.91 (一个不存在的镜像)
[root@master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment.apps/nginx-deployment image updated
则这时,Deployment的过程会卡住
[root@master ~]# kubectl rollout status deployments nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
由于执行过程卡住,所有需要执行Ctrl+C命令来终止这个查看命令。查看ReplicaSet,可以看到新建的ReplicaSet(nginx-deployment-d645d84b6)
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5d59d67564 3 3 3 23m
nginx-deployment-d645d84b6 1 1 0 22m
再查看创建的Pod,会发现亲的ReplicaSet创建的一个Pod被卡在镜像拉取过程中。
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 47h
nginx-deployment-5d59d67564-7slwk 1/1 Running 0 25m
nginx-deployment-5d59d67564-rxlwj 1/1 Running 0 25m
nginx-deployment-5d59d67564-w8q7z 1/1 Running 0 25m
nginx-deployment-d645d84b6-hbx8t 0/1 ImagePullBackOff 0 24m
为了解决上面这个问题,我们需要回滚到之前稳定版本的Deployment。
首先用 kubectl rollout history
命令检查这个 Deployment 部署的历史记录
[root@master ~]# kubectl rollout history deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
3 kubectl create --filename=nginx-deployment.yaml --record=true
4 kubectl create --filename=nginx-deployment.yaml --record=true
5 kubectl set image deployment/nginx-deployment nginx=nginx:1.91 --record=true
注意,在创建Deployment时使用 --record 参数,就可以在 CHANGE-CAUSE列看到每个版本使用的命令了。另外 ,Deployment的更新操作是在Deployment 进行部署(rollout)时被触发的,这意味着当且仅当Deployment的Pod模板(即spec.template) 被更改时才会创建新的修订版本,例如更新模板标签或者容器镜像。其他更新操作(如扩展副本数)将不会触发Deployment 的更新操作,这也意味着我们将Deployment回滚到之前的版本时,只有Deployment的Pod模板部分会被修改。
小记
在使用 --record 时,不同的场景产生的效果不一样
在不使用 --record 时,kubectl rollout history deployment 时,显示如下
[root@master ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
在create时使用 --record 时,在kubectl set 时不使用,kubectl rollout history deployment 时,显示如下
[root@master ~]# kubectl rollout history deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
3 kubectl create --filename=nginx-deployment.yaml --record=true
4 kubectl create --filename=nginx-deployment.yaml --record=true
在create和set时者使用 --record 时,kubectl rollout history deployment 时,显示如下
[root@master ~]# kubectl rollout history deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
4 kubectl create --filename=nginx-deployment.yaml --record=true
5 kubectl set image deployment/nginx-deployment nginx=nginx:1.91 --record=true
如果需要查看特定版本的详细信息,是可以加上 --revision=<N>
参数
[root@master ~]# kubectl rollout history deployment/nginx-deployment --revision=5
deployment.apps/nginx-deployment with revision #5
Pod Template:
Labels: app=nginx
pod-template-hash=d645d84b6
Annotations: kubernetes.io/change-cause: kubectl set image deployment/nginx-deployment nginx=nginx:1.91 --record=true
Containers:
nginx:
Image: nginx:1.91
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
现在撤销本次发布,回滚到上一个部署版本
[root@master ~]# kubectl rollout undo deployment nginx-deployment
deployment.apps/nginx-deployment rolled back
当然也可以使用 --to-revision 参数指定回滚到哪个部署版本号:
[root@master ~]# kubectl rollout undo deployment/nginx-deployment --to-revision=3
deployment.apps/nginx-deployment rolled back
这样,该Deployment就回滚到之前的稳定版本了,可以从Deployment的事件信息中查看到回滚到版本3的操作过程: