1、概述
在Pod的spec中有一个restartPolicy字段,如下:
apiVersion: v1 kind: Pod metadata: name: xxx spec: restartPolicy: Always ...
restartPolicy的值有三个:Always、OnFailure、Never;默认值为Always。
三种策略的区别在于:
- Always:只要Container退出就重启,即使它的退出码为0(即成功退出)
- OnFailure:如果Container的退出码不是0(即失败退出),就重启
- Never:Container退出后永不重启
所谓Container的退出码,就是Container中进程号为1的进程的退出码。每个进程退出时都有一个退出码,我们常见的提示exit 0表示退出码为0(即成功退出)。举个例子:shell命令cat /tmp/file,如果文件/tmp/file存在,则该命令(进程)的退出码为0。
2、Pod中的Container重启规则
2.1 Pod中Container重启命名规则
上面谈到当Container退出后,Container可能会被重启,那么Container是如何被重启的呢?是Kubelet调用类似于docker start的API拉起?还是重新创建一个docker容器(docker create && docker start)?
答案是,Kubelet会重新创建一个docker容器。
我们给一个例子,创建一个如下的Pod,这个Pod中有两个Container,其中demo1这个Container在启动后60秒就会退出(退出码为0),demo2这个Container则会一直运行。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: restartPolicy: Always containers: - name: demo1 image: busybox:1.31.1 command: - /bin/sh - -c - sleep 60 - name: demo2 image: tomcat:9.0.37
接下来我们创建test-restartpolicy这个命名空间和Pod。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 0 44s
然后观察demo1这个Container的docker名字与ID,发现名字(docker name字段)为k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_0,ID为f09dd4d59d76。
[root@node1 test]# docker ps|grep demo1 f09dd4d59d76 1c35c4412082 "/bin/sh -c 'sleep 6…" 32 seconds ago Up 30 seconds k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_0
再过一分钟左右,我们再看这个Container的名字和ID,发现名字为k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_1,ID为7ba3cc9685eb。这说明,重启后已经不是同一个docker容器了。
[root@node1 test]# docker ps|grep demo1 7ba3cc9685eb 1c35c4412082 "/bin/sh -c 'sleep 6…" 14 seconds ago Up 13 seconds k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_1
同理,再过一分钟左右,我们再看这个Container的名字和ID,发现名字为k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_2,ID为d71f73027239。这说明,重启后已经不是同一个docker容器了。
d71f73027239 1c35c4412082 "/bin/sh -c 'sleep 6…" 7 seconds ago Up 5 seconds k8s_demo1_test-restartpolicy_test-restartpolicy_88d4575b-5d59-4e25-8b61-ea0e0bfef035_2
如果我们细心的会发现,docker容器的名字其实是有规则的,为
k8s_<ContainerName_In_Pod>_<PodName>_<Namespace>_<PodID>_<Index>
其中最后一个<Index>一开始为0,每重启一次就会递增1。
2.2 查看Pod状态规则
在2.1查看Pod中的容器重启命名规则的时候,同时开了一个Shell窗口,一直观察Pod的状态。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 0 1s
可以看到在demo1容器第一次重启后,Pod容器状态从NotReady变成了Running。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 0 64s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 NotReady 0 65s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 1 67s
可以看到在demo1容器第一次重启后,Pod容器状态从NotReady先变成了CrashLoopBackOff再变成Running。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 1 2m5s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 NotReady 1 2m6s ........ [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 NotReady 1 2m20s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 CrashLoopBackOff 1 2m21s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 CrashLoopBackOff 1 2m22s [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 2 2m23s
过一段时间后,可以看到Pod容器状态大部分时间为CrashLoopBackOff,这是因为容器频繁重启的话会有退避延迟时间,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由Kubelet延迟一段时间后进行,且反复的重启操作的延迟时长为10s,20s,40s,80s,160s,300s,300s是最大延迟时长,过一段时间后demo1容器的重启延迟时间变成了300s,此时Pod状态为CrashLoopBackOff,重启延迟时间过后demo1容器状态会变成running,再过60s,demo1容器退出后,demo1的状态又会从NotReady变成CrashLoopBackOff,之后周而复始。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 CrashLoopBackOff 7 22m [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 CrashLoopBackOff 7 22m
如果我们细心的会发现,对于Always重启策略的Pod来说,Pod中容器重启时,查看Pod 状态字段值是有规则的,Pod中容器整个重启过程Pod状态会从Running -> NotReady -> CrashLoopBackOff -> Running,周而复始。
2.3 查看Pod RESTARTS字段值规则
实例一:
过一段查看2.1中创建Pod,可以看到RESTARTS字段的值为8。
[root@node1 ~]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 2/2 Running 8 24m
此时我们查看Pod的yaml文件,会发现demo1这个Container的restartCount为8,demo2这个Container的restartCount为0。
kubectl get pods -n=test-restartpolicy -o yaml ...... status: containerStatuses: - containerID: docker://75a85ae72574d5edb42486dc58d3526b3c7ed5d29c9959c898a4689f12fef7f5 ...... name: demo1 ready: true restartCount: 8 started: true ...... - containerID: docker://1d7fe0cb75d5a6aa9074f2ceb0dc7515df0aa6082b1e4bb9e77931b5724445ca ...... name: demo2 ready: true restartCount: 0 started: true ......
示例2:
此时删除test-restartpolicy这个Pod,修改Pod规格配置文件如下,让demo2这个Container在启动30秒后也退出(退出码为0)。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: restartPolicy: Always containers: - name: demo1 image: busybox:1.31.1 command: - /bin/sh - -c - sleep 60 - name: demo2 image: busybox:1.31.1 command: - /bin/sh - -c - sleep 30 nodeSelector: kubernetes.io/hostname: node1
接下来我们创建test-restartpolicy这个Pod,过几分钟查看Pod。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 CrashLoopBackOff 7 4m27s
此时我们查看Pod的yaml文件,会发现demo1这个Container的restartCount为3,demo2这个Container的restartCount为4。
[root@node1 test]# kubectl get pods -n=test-restartpolicy -o yaml ...... status: ...... containerStatuses: - containerID: docker://a34abd3d662c0d5c309f7967ccb2e3a70e25bda520a8b55b63eee8cff7892762 ...... name: demo1 ready: true restartCount: 3 started: true ...... - containerID: docker://4f97cccf338fff56f0d894cf4f89bda48b6108046d3ff6cfd8097bd2e6efe86b ...... name: demo2 ready: false restartCount: 4 started: false .......
如果我们细心的会发现,查看Pod RESTARTS字段值是有规则的,RESTARTS
字段表示整个Pod中所有容器的累计重启次数。如果任何一个容器在Pod中重启,RESTARTS
字段的值会增加。这意味着即使只有一个容器重启了,整个Pod的RESTARTS
字段值也会增加。
2.4 进入Pod容器规则
以sh方式登陆到Pod中的某个容器里:
kubectl exec -it <pod-name> -c <container-name> /bin/sh
还是2.1创建的Pod实例,进入demo1容器内部
[root@node1 ~]# kubectl exec -it -n=test-restartpolicy test-restartpolicy -c demo1 /bin/sh kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. / # top Mem: 194720396K used, 1279444K free, 611808K shrd, 647372K buff, 109342248K cached CPU: 10.5% usr 6.5% sys 0.0% nic 76.6% idle 5.8% io 0.0% irq 0.3% sirq Load average: 16.27 15.37 15.43 6/20460 19 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND 13 0 root S 1296 0.0 0 0.0 /bin/sh 19 13 root R 1292 0.0 3 0.0 top 1 0 root S 1280 0.0 8 0.0 sleep 60
进入demo2容器内部
[root@node1 test]# kubectl exec -it -n=test-restartpolicy test-restartpolicy -c demo2 bash kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. root@test-restartpolicy:/usr/local/tomcat# top top - 00:16:39 up 55 days, 19:23, 0 users, load average: 16.37, 17.25, 16.98 Tasks: 3 total, 1 running, 2 sleeping, 0 stopped, 0 zombie %Cpu(s): 7.4 us, 4.7 sy, 0.1 ni, 81.8 id, 5.9 wa, 0.0 hi, 0.1 si, 0.0 st MiB Mem : 191406.1 total, 3684.6 free, 78569.1 used, 109152.4 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 111506.1 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1 root 20 0 33.3g 98848 16712 S 0.3 0.1 0:03.01 java 38 root 20 0 5740 2172 1676 S 0.0 0.0 0:00.01 bash 46 root 20 0 9752 1804 1348 R 0.0 0.0 0:00.01 top
可以看到,同一个Pod中每个容器都有自己的进程空间, 当同一个Pod中的一个容器宕掉时,它的故障通常不会直接影响其他容器的运行。其他容器仍然可以继续运行,除非它们与宕掉容器之间存在依赖关系。
3、Pod中Container重启策略实战
由于在第2章节已经演示了Pod默认的重启策略, 在这一节,将实战演示下Never、OnFailure重启策略。
3.1 Never重启策略
示例一:
创建一个如下的Pod,重启策略为Nerver。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: containers: - name: demo1 image: nginx:1.14-alpine ports: - name: nginx-port containerPort: 80 livenessProbe: httpGet: scheme: HTTP port: 80 path: /hello restartPolicy: Never
查看Pod详情,关注下存活探针Liveness配置和状态码。
其中对于存活探针配置,容器启动后立即进行探测,如果1s内容器没有给出回应则记作探测失败。每次间隔10s进行一次探测,在探测连续失败3次后重启容器。
- delay:延迟,delay=0s,表示在容器启动后立即开始探测,没有延迟时间
- timeout:超时,timeout=1s,表示容器必须在1s内进行响应,否则这次探测记作失败
- period:周期,period=10s,表示每10s探测一次容器
- success:成功,#success=1,表示连续1次成功后记作成功
- failure:失败,#failure=3,表示连续3次失败后会重启容器
对于状态码,Pod中容器退出状态码为0,这说明存活探针是正常的使容器停止。
[root@node1 test]# kubectl describe pod -n=test-restartpolicy test-restartpolicy Containers: demo1: Container ID: docker://183a5fb4ad7556ba74460dc2af1afe2821f60100fadb0d2882cc0db929348ecc ...... State: Terminated Reason: Completed Exit Code: 0 Started: Wed, 14 Jun 2023 16:32:57 +0800 Finished: Wed, 14 Jun 2023 16:33:24 +0800 Ready: False Restart Count: 0 Liveness: http-get http://:80/hello delay=0s timeout=1s period=10s #success=1 #failure=3 ...... node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 11s default-scheduler Successfully assigned test-restartpolicy/test-restartpolicy to node1 Normal Pulled 9s kubelet Container image "nginx:1.14-alpine" already present on machine Normal Created 8s kubelet Created container demo1 Normal Started 8s kubelet Started container demo1 Warning Unhealthy 1s kubelet Liveness probe failed: HTTP probe failed with statuscode: 404 [root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/1 Running 0
Pod创建30秒后再查看Pod状态,Pod中容器退出状态码为0的话,Pod状态为Completed。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 0/1 Completed 0 4m13s
查看Pod日志,虽然Pod状态是Completed,但是是能查看Pod日志的,那么Never重启策略的Pod在某些场景下是非常有用的,比如Pod中的容器频繁重启,捕捉停止容器日志比较麻烦,这时如果Pod是默认的Always,可以通过临时将Pod的重启策略改成Never,这样可以通过describe Pod和查看Pod日志来分析Pod重启原因。
[root@node1 test]# kubectl logs -f -n=test-restartpolicy test-restartpolicy 2023/06/14 07:57:11 [error] 7#7: *1 open() "/usr/share/nginx/html/hello" failed (2: No such file or directory), client: 10.233.64.1, server: localhost, request: "GET /hello HTTP/1.1", host: "10.233.64.161:80" 10.233.64.1 - - [14/Jun/2023:07:57:11 +0000] "GET /hello HTTP/1.1" 404 169 "-" "kube-probe/1.21" "-" 2023/06/14 07:57:21 [error] 7#7: *2 open() "/usr/share/nginx/html/hello" failed (2: No such file or directory), client: 10.233.64.1, server: localhost, request: "GET /hello HTTP/1.1", host: "10.233.64.161:80" 10.233.64.1 - - [14/Jun/2023:07:57:21 +0000] "GET /hello HTTP/1.1" 404 169 "-" "kube-probe/1.21" "-" 2023/06/14 07:57:31 [error] 7#7: *3 open() "/usr/share/nginx/html/hello" failed (2: No such file or directory), client: 10.233.64.1, server: localhost, request: "GET /hello HTTP/1.1", host: "10.233.64.161:80" 10.233.64.1 - - [14/Jun/2023:07:57:31 +0000] "GET /hello HTTP/1.1" 404 169 "-" "kube-probe/1.21" "-"
示例二:
创建一个如下的Pod,重启策略为Nerver。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: restartPolicy: Never containers: - name: demo1 image: harbor.openserver.cn:443/library/busybox:1.31.1 command: ["/bin/sh","-c","sleep 60,exit 1"]
查看Pod详情,可以看到Pod中容器退出状态码是1。
[root@node1 test]# kubectl describe pod -n=test-restartpolicy test-restartpolicy ...... Containers: demo1: Container ID: docker://bfb7417114d852e33ce299c5267587fa1be0103ebb9bc106c18c35401777260b ...... Command: /bin/sh -c sleep 60,exit 1 State: Terminated Reason: Error Exit Code: 1 Started: Thu, 15 Jun 2023 05:26:50 +0800 Finished: Thu, 15 Jun 2023 05:26:50 +0800 Ready: False Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-btfrd (ro) ......
查看Pod状态,Pod中容器退出状态码为非0的话,Pod状态为Error。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 0/1 Error 0 2m41s
示例三:
创建一个如下的Pod,重启策略为Nerver。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: restartPolicy: Never containers: - name: demo1 image: busybox:1.31.1 command: ["/bin/sh","-c","sleep 60,exit 1"] - name: demo2 image: busybox:1.31.1 command: - /bin/sh - -c - sleep 3000
创建Pod大约1分钟后查看Pod状态,只要Pod中有一个容器异常退出,那么它的状态就会变成error。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 1/2 Error 0 2m
此时进入demo2容器,可以正常进入,说明demo2容器是正常运行的。
[root@node1 test]# kubectl exec -it -n=test-restartpolicy test-restartpolicy -c demo2 /bin/sh kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. / # exit
对于Never重启策略的Pod,通过以上三个示例可以得出如下结论:
- Pod中只要有容器停止,并且停止容器的状态码都为0,那么Pod状态为Completed。
- Pod中只要有容器停止,并且存在非0退出状态码,那么Pod状态为Error。
- 不管退出状态码是Completed还是Error,都能查看Pod日志的,那么Never重启策略的Pod在某些场景下是非常有用的,比如Pod中的容器频繁重启,这时如果Pod是默认的Always,可以通过临时将Pod的重启策略改成Never,这样可以通过describe Pod和查看Pod日志来分析Pod重启原因。
- 同一个Pod中的一个容器宕掉时,它的故障通常不会直接影响其他容器的运行。其他容器仍然可以继续运行,除非它们与宕掉容器之间存在依赖关系。
3.2 OnFailure重启策略
示例一:
创建一个如下的Pod,重启策略为OnFailure。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: containers: - name: demo1 image: nginx:1.14-alpine ports: - name: nginx-port containerPort: 80 livenessProbe: httpGet: scheme: HTTP port: 80 path: /hello restartPolicy: OnFailure
这个示例在3.1示例一中已经讲过,这里不再详述,Pod中的demo1容器在启动30秒后会因为存活探针检测导致容器停止,容器退出码是0,此时查看Pod信息,Pod状态是Completed。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 0/1 Completed 4 3m9s
示例二:
创建一个如下的Pod,重启策略为OnFailure。
apiVersion: v1 kind: Pod metadata: name: test-restartpolicy namespace: test-restartpolicy spec: restartPolicy: OnFailure containers: - name: demo1 image: harbor.openserver.cn:443/library/busybox:1.31.1 command: ["/bin/sh","-c","sleep 10,exit"]
创建几分钟后,查看Pod状态,可看到容器当前已经重启了 5 次。
[root@node1 test]# kubectl get pods -n=test-restartpolicy NAME READY STATUS RESTARTS AGE test-restartpolicy 0/1 CrashLoopBackOff 5 4m5s
对于OnFailure重启策略的Pod,通过以上两个示例可以得出如下结论:
- Pod中容器停止,并且停止容器的状态码为0,那么Pod状态为Completed。
- Pod中容器停止,并且停止容器的状态码为非0,那么Pod就会像Always策略那样重启容器。
4、总结
- 一般Pod是通过工作负载去管理的,在五种工作负载中,一般建议:Deployment、StatefulSet、DaemonSet设置为Always;Job与CronJob设置为OnFailure或Never。
- 虽然restartPolicy字段是Pod的配置,但是其实是作用于Pod的Container,换句话说,不应该叫Pod的重启策略,而是叫Container的重启策略;Pod中的所有Container都适用于这个策略。
- 重启策略适用于Pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由Kubelet延迟一段时间后进行,且反复的重启操作的延迟时长为10s,20s,40s,80s,160s,300s,300s是最大延迟时长。
- Pod中重启的容器以k8s_<ContainerName_In_Pod>_<PodName>_<Namespace>_<PodID>_<Index>规则命名。
- 对于Always重启策略的Pod来说,Pod中容器重启时,查看Pod 状态字段值是有规则的,Pod中容器整个重启过程Pod状态会从Running -> NotReady -> CrashLoopBackOff -> Running,周而复始。
- 查看Pod RESTARTS字段值是有规则的,
RESTARTS
字段表示整个Pod中所有容器的累计重启次数。如果任何一个容器在Pod中重启,RESTARTS
字段的值会增加。这意味着即使只有一个容器重启了,整个Pod的RESTARTS
字段值也会增加。 - 同一个Pod中每个容器都有自己的进程空间, 当同一个Pod中的一个容器宕掉时,它的故障通常不会直接影响其他容器的运行。其他容器仍然可以继续运行,除非它们与宕掉容器之间存在依赖关系。 对于Never重启策略的Pod:
- Pod中只要有容器停止,并且停止容器的状态码都为0,那么Pod状态为Completed。
- Pod中只要有容器停止,并且存在非0退出状态码,那么Pod状态为Error。
- 不管退出状态码是Completed还是Error,都能查看Pod日志的,那么Never重启策略的Pod在某些场景下是非常有用的,比如Pod中的容器频繁重启,这时如果Pod是默认的Always,可以通过临时将Pod的重启策略改成Never,这样可以通过describe Pod和查看Pod日志来分析Pod重启原因。
- 同一个Pod中的一个容器宕掉时,它的故障通常不会直接影响其他容器的运行。其他容器仍然可以继续运行,除非它们与宕掉容器之间存在依赖关系。
- Pod中容器停止,并且停止容器的状态码为0,那么Pod状态为Completed。
- Pod中容器停止,并且停止容器的状态码为非0,那么Pod就会像Always策略那样重启容器。