事件背景

因为 k8s 中采用大量的异步机制、以及多种对象关系设计上的解耦,当应用实例数 增加/删除、或者应用版本发生变化触发滚动升级时,系统并不能保证应用相关的 service、ingress 配置总是及时能完成刷新。在一些情况下,往往只是新的 Pod 完成自身初始化,系统尚未完成 Endpoint、负载均衡器等外部可达的访问信息刷新,老得 Pod 就立即被删除,最终造成服务短暂的额不可用,这对于生产来说是不可接受的,所以 k8s 就加入了一些存活性探针:StartupProbe、LivenessProbe、ReadinessProbe。

技术探索

Pod 常见的状态

  • Pending:挂起,我们在请求创建 pod 时,条件不满足,调度没有完成,没有任何一个节点能满足调度条件。已经创建了但是没有适合它运行的节点叫做挂起,这其中也包含集群为容器创建网络,或者下载镜像的过程。
  • Running:Pod 内所有的容器都已经被创建,且至少一个容器正在处于运行状态、正在启动状态或者重启状态。
  • Succeeded:Pod 中所以容器都执行成功后退出,并且没有处于重启的容器。
  • Failed:Pod 中所以容器都已退出,但是至少还有一个容器退出时为失败状态。
  • Unknown:未知状态,所谓 pod 是什么状态是 apiserver 和运行在 pod 节点的 kubelet 进行通信获取状态信息的,如果节点之上的 kubelet 本身出故障,那么 apiserver 就连不上 kubelet,得不到信息了,就会看 Unknown

Pod 重启策略

  • Always: 只要容器失效退出就重新启动容器。
  • OnFailure: 当容器以非正常(异常)退出后才自动重新启动容器。
  • Never: 无论容器状态如何,都不重新启动容器。

探针简介

1、kubelet的探测方式

在K8S集群中,为了测试pod是否正常运行,可以通过kubelet定期对pod进行健康检查,从而保证服务正常运行。

kubelet的检查机制:

  • exec: 在容器内执行指定命令,当返回码为0表示检测成功。在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds: 探针会无限期地持续运行,甚至可能超过所配置的限期,直到返回结果为止。
  • grpc:使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。gRPC 探针是一个 alpha 特性,只有在你启用了"GRPCContainerProbe" 特性门控时才能使用。
  • httpGet:对容器的IP和URL进行HTTP的GET请求,当响应码为大于等于200且小于400,表示容器正常运行。
  • tcpSocket:对容器内的端口或socket进行TCP检查,如果端口成功打开,证明容器正常运行。

探针的返回结果:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

探针的类型:

livenessProbe(存活探针):判断容器是否正常运行。如果存活探测失败,那么kubelet就会把老的容器删除,然后根据重启策略对容器做操作。如果容器没有存活探针,默认状态为Success。


readinessProbe(就绪探针):判断容器的是否就绪。比如:应用在启动时可能需要加载大量的数据或配置文件,或是启动后要依赖等待外部服务。在这种情况下,既不想杀死应用,也不想给它发送请求。这个时候就需要用到这个探针。
当就绪探测失败,端点控制器将从与 Pod匹配的所有服务的端点列表中删除该 Pod 的 IP 地址(如果是用nodeport映射端口到外网,这个时候就不能访问网站了,但是容器不会被删除),初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。


startupProbe(启动探针):指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将删除容器,而容器依其重启策略进行重启。如果容器没有提供启动探测,则默认状态为 Success。
 

探针主要字段解析:

(initialDelaySeconds: 5):初始化延迟时间,主要是告诉kubelet,容器启动需要的时间,当过了这个时间再进行探测,要不然容器可能还没启动就会删除了。
(periodSeconds: 5):探测周期间隔时间,主要是指定kubelet对容器进行探针的时间周期,也就是第一次探测结束后,等待多少时间后对容器进行探测。默认值为10秒,最小值为1秒。
(timeoutSeconds: 5):单次探测超时时间,指定kubelet对容器探测的最大时间,超过这个时间证明容器探测失败。默认为1秒,最小为1秒。
(successThreshold: 5):探测失败到成功的重试次数,当kubelet对某个容器第一次探测失败后,重新进行探测的次数,比如指定为1,那么就会直接将容器删除。如果使用的探针是livenessProbe,那么只能配置为1,最小值为1次。
(failureThreshold: 5):探测成功到失败的重试次数,当kubelet对某个容器进行探测过程中,允许失败的次数,当用于readinessProbe探针,默认是3次,最小值为1次。也就是说当3次探测失败后,容器会被删除。当用于startupProbe探针,如果还设置了periodSeconds时间,那么等待容器启动的时间为failureThreshold的时间乘以periodSeconds时间的值,在这段时间内,容器没有启动,那么就会删除容器。
 

2、livenessProbe探针
2.1 livenessProbe和kubelet-exec

首先我们定义一个nginx的deployment:

kubectl apply -f nginx-exec.yaml

pod正常运行以后,我们查下探针里的目录文件是否存在,显然是正常返回的

kubectl exec -it nginx-deployment-5546c765b7-zt6cc -- ls /usr/share/nginx/html/index.html


我们修改index.html为index.html.bak,看看pod的探测无法获取到index.html后,可以看到RESTARTS变成了1,证明容器重启过了,可以看到文件又存在了,证明容器被重建了。

2.2、livenessProbe和kubelet-httpGet

master创建容器

删除文件,这样探针就会被触发,可以看到RESTARTS变成了1,证明容器重启过了

可以看到文件又存在了,证明容器被重建了

2.3、livenessProbe和kubelet-tcpSocket

3、readinessProbe探针

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        readinessProbe:  #指定探针
          exec:          #指定探针探测方式
            command:
            - cat
            - /usr/share/nginx/html/index.html
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  labels:
    svc: nginx-svc-nodeport
  name: nginx-svc-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  selector:
    app: nginx

 

kubectl get pod -A -o wide     查看容器名字,可以看到原本READY是1/1
kubectl exec -it nginx-677665d5db-fct77 /bin/sh  连接容器

cd /usr/share/nginx/html/   修改探针指定探测的文件名字,让探针触发
mv index.html index1.html 
exit

kubectl get pod -A -o wide  可以看到READY变成了0/1,证明探针被触发了,容器没有被删除,也没有重建
curl -I http://192.168.2.10:30080  可以看到网站访问不了
kubectl describe svc nginx-svc-nodeport  可以看到Endpoints没有值,证明探针把service和pod解除绑定了

 3.2、readinessProbe和kubelet-httpGet

[root@k8s-master readliness]# cat readliness-httpGet.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx 
        ports:
        - containerPort: 80
        readinessProbe:             #指定探针
          httpGet:                  #指定探针方式
            path: /index.html       
            port: 80                
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  labels:
    svc: nginx-svc-nodeport
  name: nginx-svc-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  selector:
    app: nginx



[root@k8s-master readliness]# kubectl apply -f readliness-httpGet.yaml 
deployment.apps/nginx created
service/nginx-svc-nodeport created

[root@k8s-master readliness]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-5865d7c55c-9p4cv   1/1     Running   0          60s   10.244.2.33   k8s-node2   <none>           <none>



[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport
Name:                     nginx-svc-nodeport
Namespace:                default
Labels:                   svc=nginx-svc-nodeport
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.99.244.94
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30080/TCP
Endpoints:                10.244.2.33:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>


[root@k8s-master readliness]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-5865d7c55c-9p4cv   1/1     Running   0          60s   10.244.2.33   k8s-node2   <none>           <none>


[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080       #成功访问svc
HTTP/1.1 200 OK
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 13:56:35 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 19 Oct 2022 07:56:21 GMT
Connection: keep-alive
ETag: "634fada5-267"
Accept-Ranges: bytes


[root@k8s-master readliness]# kubectl exec -it nginx-5865d7c55c-9p4cv bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-5865d7c55c-9p4cv:/# cd /usr/share/nginx/html/
root@nginx-5865d7c55c-9p4cv:/usr/share/nginx/html# mv index.html index.html1
root@nginx-5865d7c55c-9p4cv:/usr/share/nginx/html# exit
exit
[root@k8s-master readliness]#               #移动index.html出发探针失败


                                            #宿主机已经无法访问,流量无法进入
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080 
HTTP/1.1 403 Forbidden
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 13:57:45 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport  
Name:                     nginx-svc-nodeport
Namespace:                default
Labels:                   svc=nginx-svc-nodeport
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.99.244.94
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30080/TCP
Endpoints:                                       #service没有与pod进行绑定
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

 # pod虽然处于running状态,但不ready,不会作为service的endpoint提供服务
[root@k8s-master readliness]# kubectl get pod                 
NAME                     READY   STATUS    RESTARTS   AGE
nginx-5865d7c55c-9p4cv   0/1     Running   0          6m8s

3.3、readinessProbe和kubelet-tcpSocket

[root@k8s-master readliness]# cat readliness-tcp.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx 
        ports:
        - containerPort: 80
        readinessProbe:                 #指定探针
          tcpSocket:                    #指定探测方式
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  labels:
    svc: nginx-svc-nodeport
  name: nginx-svc-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  selector:
    app: nginx


[root@k8s-master readliness]# kubectl apply -f readliness-tcp.yaml 
deployment.apps/nginx created
service/nginx-svc-nodeport created

[root@k8s-master readliness]# kubectl get svc
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes           ClusterIP   10.96.0.1      <none>        443/TCP        18d
nginx-svc-nodeport   NodePort    10.99.24.165   <none>        80:30080/TCP   17s
[root@k8s-master readliness]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7464f54778-tbwtf   1/1     Running   0          39s
[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport 
Name:                     nginx-svc-nodeport
Namespace:                default
Labels:                   svc=nginx-svc-nodeport
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.99.24.165
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30080/TCP
Endpoints:                10.244.2.34:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>


#########svc可以正常访问
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080
HTTP/1.1 200 OK
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 14:05:53 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 19 Oct 2022 07:56:21 GMT
Connection: keep-alive
ETag: "634fada5-267"
Accept-Ranges: bytes

[root@k8s-master readliness]# kubectl exec -it nginx-7464f54778-tbwtf  bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-7464f54778-tbwtf:/# sed -i 's/80/8000/g' /etc/nginx/conf.d/default.conf 
root@nginx-7464f54778-tbwtf:/# nginx -s reload
2022/11/07 14:07:29 [notice] 37#37: signal process started
root@nginx-7464f54778-tbwtf:/# 


[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport 
Name:                     nginx-svc-nodeport
Namespace:                default
Labels:                   svc=nginx-svc-nodeport
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.99.24.165
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30080/TCP
Endpoints:                
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080
curl: (7) Failed connect to 192.168.2.10:30080; Connection refused

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐