文章目录

  • 问题现象
  • 问题分析
  • 问题解决
  • 拓展
  • 总结


问题现象

在一次测试ConfigMap的过程中,我想起一个单容器的pod,简单的打印出容器内所有的环境变量,以验证ConfigMap的传递。结果pod起来以后一直出现CrashLoopBackOff的状态。

这里为了抽离出问题的本质,去掉干扰项,将pod的生成yaml文件简化如下

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod-1
spec:
  containers:
    - name: cm-container-1
      image: alpine
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sh", "-c", "env" ]

创建pod

[root@k8s-master ~]# kubectl apply -f test.yaml
pod/configmap-pod-1 created

但是pod却一直是如下状态

[root@k8s-master ~]# kubectl get pod configmap-pod-1 -o wide
NAME              READY   STATUS             RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
configmap-pod-1   0/1     CrashLoopBackOff   3          68s   10.244.1.90   k8s-node1   <none>           <none>

不仅显示CrashLoopBackOff状态,还一直重启

问题分析

通常pod出现这种出错并不停重启的现象,归纳起来有以下几种可能:

  • 容器内应用一直在崩溃
  • yaml文件中对pod的一些字段配置有误
  • k8s集群的部署有问题

但是我这里一条都不满足。这个集群一直运行着很多应用都平安无事,说明集群本身没事。这个容器的镜像只是官方的alpine,除了额外运行了一条shell命令,所以不可能崩溃。至于yaml文件就更不可能了,寥寥无几的几个字段反复检查了好几遍。

确实神奇。

后来经过Google,发现还有另一种情况也会出现这种现象。就是容器本身没有问题,出在容器运行的命令上,也就是这一行

command: [ "/bin/sh", "-c", "env" ]

容器正常启动,然后运行配置的主命令,这个主命令要么是image的entrypoint,要么是这里的command。此时pod处于running状态(是的,pod在一个短暂的时间处于running中)。但是这个shell命令很快运行完毕,成功退出。一旦主命令退出,pod就会重启,然后循环这一过程。如果用-w方式去观察pod的状态,就可以看到pod在Running与CrashLoopBackOff之间不停变化

总结起来,如果没有指定容器的存活探针(LivenessProbe),pod会以主命令的运行状态来判断容器是否存活。如果容器主命令一直正常运行没有退出,则pod处于running状态;如果容器主命令执行完毕或者报错,根据配置的restartPolicy策略决定pod是否重启;如果根本就没有配置主命令,pod会直接根据restartPolicy决定是否重启。而因为默认情况restartPolicy是Always,也就是一直重启,所以会看到上面的现象。


问题解决

那么这个其实就是符合逻辑的正常现象。如果想要pod不用一直重启,可以给pod改一个restartPolicy

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod-1
spec:
  containers:
    - name: cm-container-1
      image: alpine
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sh", "-c", "env" ]
  restartPolicy: Never

之后再启动pod,会发现pod不重启了,而是显示Completed状态

[root@k8s-master ~]# kubectl apply -f test.yaml
pod/configmap-pod-1 created
[root@k8s-master ~]# kubectl get pod configmap-pod-1 -o wide
NAME              READY   STATUS      RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
configmap-pod-1   0/1     Completed   0          15s   10.244.1.91   k8s-node1   <none>           <none>

拓展

为了验证下上面的说法,我又做了如下几个实验。

  • 改一个一直运行不会退出的主命令

将上面的yaml文件修改如下

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod-1
spec:
  containers:
    - name: cm-container-1
      image: alpine
      imagePullPolicy: IfNotPresent
      command: [ "ping", "8.8.8.8" ]

如果你的机器因为科学上网原因访问不到 8.8.8.8,请换别的地址测试

再启动pod就不会重启了

[root@k8s-master ~]# kubectl apply -f test.yaml
pod/configmap-pod-1 created
[root@k8s-master ~]# kubectl get pod configmap-pod-1 -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
configmap-pod-1   1/1     Running   0          7s    10.244.1.92   k8s-node1   <none>           <none>

同时还可以在log中看到ping的结果

[root@k8s-master ~]# kubectl logs configmap-pod-1
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=47 time=2.664 ms
64 bytes from 8.8.8.8: seq=1 ttl=47 time=2.320 ms
64 bytes from 8.8.8.8: seq=2 ttl=47 time=2.315 ms
64 bytes from 8.8.8.8: seq=3 ttl=47 time=2.317 ms
64 bytes from 8.8.8.8: seq=4 ttl=47 time=2.376 ms
64 bytes from 8.8.8.8: seq=5 ttl=47 time=2.395 ms
64 bytes from 8.8.8.8: seq=6 ttl=47 time=2.447 ms
64 bytes from 8.8.8.8: seq=7 ttl=47 time=2.431 ms
64 bytes from 8.8.8.8: seq=8 ttl=47 time=2.264 ms
64 bytes from 8.8.8.8: seq=9 ttl=47 time=2.268 ms
64 bytes from 8.8.8.8: seq=10 ttl=47 time=2.302 ms
64 bytes from 8.8.8.8: seq=11 ttl=47 time=2.384 ms
64 bytes from 8.8.8.8: seq=12 ttl=47 time=2.443 ms
64 bytes from 8.8.8.8: seq=13 ttl=47 time=2.439 ms
64 bytes from 8.8.8.8: seq=14 ttl=47 time=2.236 ms
64 bytes from 8.8.8.8: seq=15 ttl=47 time=2.250 ms
64 bytes from 8.8.8.8: seq=16 ttl=47 time=2.330 ms
64 bytes from 8.8.8.8: seq=17 ttl=47 time=4.658 ms
64 bytes from 8.8.8.8: seq=18 ttl=47 time=2.438 ms
64 bytes from 8.8.8.8: seq=19 ttl=47 time=2.483 ms
64 bytes from 8.8.8.8: seq=20 ttl=47 time=2.403 ms
64 bytes from 8.8.8.8: seq=21 ttl=47 time=2.416 ms
64 bytes from 8.8.8.8: seq=22 ttl=47 time=2.437 ms
64 bytes from 8.8.8.8: seq=23 ttl=47 time=3.502 ms
  • 不配置主命令

修改yaml文件如下

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod-1
spec:
  containers:
    - name: cm-container-1
      image: alpine
      imagePullPolicy: IfNotPresent
  restartPolicy: Never

创建pod以后,也是没有重启,并显示Completed状态

[root@k8s-master ~]# kubectl apply -f test.yaml
pod/configmap-pod-1 created
[root@k8s-master ~]# kubectl get pod configmap-pod-1 -o wide
NAME              READY   STATUS      RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
configmap-pod-1   0/1     Completed   0          5s    10.244.1.93   k8s-node1   <none>           <none>

总结

总结一下出现CrashLoopBackOff这个现象的排查思路:

  1. 首先确保集群本身部署没有问题
  2. 确保pod创建时候的yaml文件字段信息无误
  3. 如果是自己制作的镜像,可以先用docker试试能不能起的来,确保镜像无误
  4. 最后检查下容器的主命令是否有配置,如果配置了是否很快就结束或者是执行错误退出,导致pod不断重启

如果是因为主命令原因导致不停重启,可以修改restartPolicy为Never只执行一次。或者修改下主命令使得命令能连续运行。