pod健康检查之容器的存活探针、就绪探针、启动探针

前言

环境:centos7.9 docker-ce-20.10.9 kubernetes-version v1.22.6

为什么需要存活探针和就绪探针

在前面我们介绍过,可以通过配置restartPolicy字段来对容器退出后执行3种不同的重启策略,但这并不能解决我们所有的问题,比如容器中的Java应用程序抛出OutOfMemoryErrors,但JVM进程会一致存在,容器并没有退出,再比如,Java停止响应或死锁,容器也没有终止等等,这时如果有一种机制来告诉kubernetes来重启容器那就最好了,在k8s中,提供了一种存活探针的机制来实现上诉的问题。

livenessProbe,叫做存活探针,是为了检测容器是否正在运行,是否活着;
readinessProbe,叫做就绪探针,是为了检测容器是否准备就绪,是否能接受客户端请求;
startupProbe,叫做启动探针,用于判断容器进程是否已经启动。

就绪探针与存活探针的最大区别

存活探针是将检查失败的容器杀死,创建新的启动容器来保持pod正常工作;
就绪探针是,当就绪探针检查失败,并不重启容器,而是将pod移出服务,就绪探针确保服务中的pod都是可用的,确保客户端只与正常的pod交互并且客户端永远不会知道系统存在问题。

存活探针(liveness probe)

kubernetes可以通过存活探针检查容器是否还在运行,可以为pod中的每个容器单独定义存活探针,kubernetes将定期执行探针,如果探测失败,将杀死容器,并根据restartPolicy策略来决定是否重启容器。

3种类型的存活探针

kubernetes提供了3种探测容器的存活探针,如下:

exec:在容器内执行shell命令,根据命令退出状态码是否为0判定成功失败;
httpGet:根据容器IP地址、端口号、路径发送http get请求,返回200-400范围内的状态码表示成功;(生产环境建议使用这种)
tcpSocket:与容器IP地址、端口建立TCP Socket链接,能建立则表示成功;

存活探针属性

存活探针的附加属性有以下几个:

initialDelaySeconds:表示在容器启动后延时多久秒才开始探测;
periodSeconds:表示执行探测的频率,即间隔多少秒探测一次,默认间隔周期是10秒,最小1秒;
timeoutSeconds:表示探测超时时间,默认1秒,最小1秒,表示容器必须在超时时间范围内做出响应,否则视为本次探测失败;
successThreshold:表示最少连续探测成功多少次才被认定为成功,默认是1,对于liveness必须是1,最小值是1;
failureThreshold:表示连续探测失败多少次才被认定为失败,默认是3,连续3次失败,k8s 将根据pod重启策略对容器做出决定;

注意:定义存活探针时,一定要设置initialDelaySeconds属性,该属性为初始延时,如果不设置,默认容器启动时探针就开始探测了,这样可能会存在应用程序还未启动就绪,就会导致探针检测失败,k8s就会根据pod重启策略杀掉容器然后再重新创建容器的莫名其妙的问题。
在生产环境中,一定要定义一个存活探针。

exec探针

exec探针是通过在容器内执行shell命令,根据命令退出状态码是否为0判定成功失败;

[root@master ~]# cat rs_nginx.yaml 
apiVersion: apps/v1
kind: ReplicaSet
metadata: 
  name: rs-nginx-http
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-rs
    matchExpressions:
      - {key: env, operator: In, values: [dev]}
  template:
     metadata:
       labels:
         app: nginx-rs
         env: dev
     spec:
         containers:
         - image: nginx:1.7.9
           name: nginx-container
           ports:
           - containerPort: 80
           livenessProbe:									#定义探针
             exec:											#探针类型为exec探针
               command:
               - cat										#表示cat /usr/share/nginx/html/index.html文件,根据命令执行
               - /usr/share/nginx/html/index.html			#结果返回状态是否非0来表示探针状态
             initialDelaySeconds: 8							#初始延时,表示容器启动15秒后才开始探测
             periodSeconds: 3								#探测周期,3秒探测一次
             timeoutSeconds: 2								#超时时间为2秒,表示必须2秒内做出回应,否则将视本次探测失败
             failureThreshold: 1							#连续失败次数,表示连续失败1次就定义为失败,k8s将根据pod重启机制
             												#做出是否重启容器的就决定

#下面将进入容器内部删除 /usr/share/nginx/html/index.html文件,看探针是否生效
[root@master ~]# kubectl  exec rs-nginx-http-2rng2 -it -- bash			#进入容器
root@rs-nginx-http-2rng2:/# rm -rf /usr/share/nginx/html/index.html		#删除/usr/share/nginx/html/index.html
root@rs-nginx-http-2rng2:/# exit										#退出容器
[root@master ~]# kubectl get pods										#查看pod
NAME                      READY   STATUS    RESTARTS      AGE
pod/rs-nginx-http-2rng2   1/1     Running   1 (11s ago)   74s			#发现pod的容器重启过一次
pod/rs-nginx-http-bgr4m   1/1     Running   0             74s
pod/rs-nginx-http-cv9bv   1/1     Running   0             74s
[root@master ~]# kubectl  describe  pod/rs-nginx-http-2rng2				#查看pod信息,可以看出探针探测失败,容器被重新启动了
................
    Restart Count:  1
    Liveness:       exec [cat /usr/share/nginx/html/index.html] delay=8s timeout=2s period=3s #success=1 #failure=1
................................
  Normal   Pulled     9m32s (x2 over 10m)  kubelet            Container image "nginx:1.7.9" already present on machine
  Normal   Created    9m32s (x2 over 10m)  kubelet            Created container nginx-container
  Warning  Unhealthy  9m32s                kubelet            Liveness probe failed: cat: /usr/share/nginx/html/index.html: No such file or directory
  Normal   Killing    9m32s                kubelet            Container nginx-container failed liveness probe, will be restarted
  Normal   Started    9m31s (x2 over 10m)  kubelet            Started container nginx-container
[root@master ~]# 

httpGet探针 (生产环境建议使用 httpGet探针 进行探测)

httpGet探针是根据容器的IP地址、端口、路径发送http get 请求,返回200-400范围内的状态码表示成功;

[root@master ~]# cat rs_nginx.yaml 
apiVersion: apps/v1
kind: ReplicaSet
metadata: 
  name: rs-nginx-http
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-rs
    matchExpressions:
      - {key: env, operator: In, values: [dev]}
  template:
     metadata:
       labels:
         app: nginx-rs
         env: dev
     spec:
         containers:
         - image: nginx:1.7.9
           name: nginx-container
           ports:
           - containerPort: 80
           livenessProbe:						#为容器定义一个存活探针
             httpGet:							#探针类型为httpGet探针
       #       host: 10.244.0.49			    #指定主机,默认是pod的ip,一般省略不写
               path: /							#路径
               port: 80							#端口
             initialDelaySeconds: 15			#初始延时时间为15s
             periodSeconds: 5					#探测周期
             timeoutSeconds: 2					#超时时间
             failureThreshold: 9				#失败次数
[root@master ~]# 

tcpSocket探针

tcpSocket探针,根据容器的IP地址、端口发起tcp Socket链接,探测是否能成功与容器的端口建立tcp Socket链接;

[root@master ~]# cat rs_nginx.yaml 
apiVersion: apps/v1
kind: ReplicaSet
metadata: 
  name: rs-nginx-http
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-rs
    matchExpressions:
      - {key: env, operator: In, values: [dev]}
  template:
     metadata:
       labels:
         app: nginx-rs
         env: dev
     spec:
         containers:
         - image: nginx:1.7.9
           name: nginx-container
           ports:
           - containerPort: 80
           livenessProbe:								#定义存活探针
             tcpSocket: 								#探针类型为tcpSocket探针
       #       host: 10.244.0.49			    		#指定主机,默认是pod的ip,一般省略不写
               port: 80									#探测与80端口是否能成功建立tcp链接
             initialDelaySeconds: 8
             periodSeconds: 3
             timeoutSeconds: 2
             failureThreshold: 1

就绪探针(readiness probe)

我们知道,当一个pod启动后,就会立即加入service的endpoint ip列表中,并开始接收到客户端的链接请求,假若此时pod中的容器的业务进程还没有初始化完毕,那么这些客户端链接请求就会失败,为了解决这个问题,kubernetes提供了就绪探针来解决这个问题的。

在pod中的容器定义一个就绪探针,就绪探针周期性检查容器,如果就绪探针检查失败了,说明该pod还未准备就绪,不能接受客户端链接,则该pod将从endpoint列表中移除,被剔除了service就不会把请求分发给该pod,然后就绪探针继续检查,如果随后容器就绪,则再重新把pod加回endpoint列表。

就绪探针的三大类型

就绪探针也有三种类型,exec,httpGet和tcpSocket。

exec:执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明容器已经准备就绪;
httpGet:向容器的ip,端口、路径发送http get请求,通过响应的http状态码是否位于200-400之间判断容器是否准备就绪;
tcpSocke:向容器的IP地址、端口发起tcp socket链接,如果能正常建立链接,则认为容器已经准备就绪。

就绪探针的属性

就绪探针的附加属性有以下几个:

initialDelaySeconds:延时秒数,即容器启动多少秒后才开始探测,不写默认容器启动就探测;
periodSeconds :执行探测的频率(秒),默认为10秒,最低值为1;
timeoutSeconds :超时时间,表示探测时在超时时间内必须得到响应,负责视为本次探测失败,默认为1秒,最小值为1;
failureThreshold :连续探测失败的次数,视为本次探测失败,默认为3次,最小值为1次;
successThreshold :连续探测成功的次数,视为本次探测成功,默认为1次,最小值为1次;

创建一个exec存活探针

exec存活探针,执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明容器已经准备就绪;
下面使用deployment部署3个有exec就绪探针的pod(service已经存在了),如下所示:

[root@master ~]# cat deplyment_nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata: 
  name: deployment-nginx
  namespace: default
spec:
  replicas: 3
  selector:
     matchLabels:
         app: nginx
  template:
     metadata:
       labels:
         app: nginx
     spec:
         containers:
         - image: nginx:1.7.9
           name: nginx-container
           readinessProbe:					#定义exec探针
             initialDelaySeconds: 5			#容器启动5秒后才开始探测
             periodSeconds: 10				#探针周期为10s探测一次
             timeoutSeconds: 2				#超时时间,2秒得不到响应则视为探测失败
             failureThreshold: 3			#连续探测失败3次,视为本次探测失败
             successThreshold: 1			#探测成功1次则视为本次探测成功
             exec:							#探针类型为exec
               command:						#执行的命令是ls /var/ready
               - ls
               - /var/ready
           ports:
           - name: http 
             containerPort: 80
[root@master ~]# kubectl  get pods 			#查看pod,发现没有一个是ready状态的
NAME                                READY   STATUS    RESTARTS   AGE
deployment-nginx-68bb45dd46-5rj7c   0/1     Running   0          3m1s
deployment-nginx-68bb45dd46-78ld2   0/1     Running   0          3m1s
deployment-nginx-68bb45dd46-gnnhn   0/1     Running   0          3m1
[root@master ~]# kubectl describe  pods deployment-nginx-68bb45dd46-5rj7c |tail -n 3	#查看某个pod的详细信息,显示存活探针检测失败了
  Normal   Created    5m7s                   kubelet            Created container nginx-container
  Normal   Started    5m7s                   kubelet            Started container nginx-container
  Warning  Unhealthy  2m18s (x22 over 5m6s)  kubelet            Readiness probe failed: ls: cannot access /var/ready: No such file or directory
[root@master ~]# 
[root@master ~]# kubectl  get ep svc-rc-nginx-nodeport		#查看service的endpoint列表,显示没有任何pod ip列表可用
NAME                    ENDPOINTS   AGE
svc-rc-nginx-nodeport               6h32m

# 现在,我们手动在某个pod中创建一个/var/ready文件,这样pod就会处于准备就绪状态,service就能成功添加该pod
[root@master ~]# kubectl  exec deployment-nginx-68bb45dd46-5rj7c -- touch /var/ready	#为该pod创建/var/ready文件
[root@master ~]# kubectl  get pods,ep -o wide										#查看该pod,发现pod已处于就绪状态
NAME                                    READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
pod/deployment-nginx-68bb45dd46-5rj7c   1/1     Running   0          8m52s   10.244.1.77   node1   <none>           
pod/deployment-nginx-68bb45dd46-78ld2   0/1     Running   0          8m52s   10.244.1.76   node1   <none>           
pod/deployment-nginx-68bb45dd46-gnnhn   0/1     Running   0          8m52s   10.244.2.38   node2   <none>           

NAME                              ENDPOINTS              AGE						#endpoint已经有该pod的ip端口
endpoints/svc-rc-nginx-nodeport   10.244.1.77:80         6h36m		
[root@master ~]#

创建一个httpGet存活探针

httpGet存活探针,向容器发送http get请求,通过响应的http状态码判断容器是否准备就绪;
下面使用deployment部署3个有httpGet就绪探针的pod,如下所示:

..........
containers:
         - image: nginx:1.7.9
           name: nginx-container
           readinessProbe:					#定义探针
              httpGet:                		# 探针类型为httpGet
       #       host: 10.244.0.49			#指定主机,默认是pod的ip,一般省略不写
               path: /read
               port: 80
...........

创建一个tcpSocket存活探针

tcpSocket存活探针,打开一个tcp连接到容器的指定端口,如果连接已建立,则认为容器已经准备就绪。
下面使用deployment部署有tcpSocket就绪探针的pod,如下所示:

..........
containers:
         - image: nginx:1.7.9
           name: nginx-container
           readinessProbe:					#定义探针
              tcpSocket:                	# 探针类型为tcpSocket
        #       host: 10.244.0.49			#指定主机,默认是pod的ip,一般省略不写
                port: 80
...........

startupProbe 启动探针

startupProbe 启动探针,startupProbe探针是1.16版本加入的探针,用于判断容器进程是否已经启动,是为了解决程序启动时间很长,启动慢等问题的,当配置了startupProbe启动探针,会先禁用其他探针,直到startupProbe探针成功,成功后将退出不在进行探测,如果startupProbe探针探测失败,pod将会重启。

startupProbe 启动探针也是使用exec 、httpGet、tcpSocket等三种方法进行探针,这里不在陈诉。

务必定义就绪探针

首先,如果没有定义就绪探针,那么新创建的pod就会立即加入到service的endpoint列表,如果容器里的业务程序需要很长时间才能开始处理传入的链接,而此时service又将客户端连接转发给了该pod,那么客户端就会看到“连接被拒绝”等类型的错误,所以,为每个容器定义一个就绪探针是很有必要的,即使是定义一个简单的http get 探针。
在生产环境中,一般会让开发提供容器的健康检查接口,在spring boot已经有原生的健康检查接口,所有然开发定义健康检查接口后我们再使用http get方法来定义我们的探针。

你可能感兴趣的:(kubernetes,docker,容器,kubernetes)