K8s Pod状态与容器探针

Posted 1874

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K8s Pod状态与容器探针相关的知识,希望对你有一定的参考价值。

Pause 容器,又叫 Infra 容器,是pod的基础容器,镜像体积只有几百KB左右,配置在kubelet中,主要的功能是一个pod中多个容器的网络通信。 Infra 容器被创建后会初始化 Network Namespace,之后其它容器就可以加入到 Infra 容器中共享Infra 容器的网络了,因此如果一个 Pod 中的两个容器 A 和 B,那么关系如下: A容器和B容器能够直接使用 localhost 通信; A容器和B容器可以可以看到网卡、IP与端口监听信息。 Pod 只有一个 IP 地址,也就是该 Pod 的 Network Namespace 对应的IP 地址(由Infra 容器初始化并创建)。 k8s环境中的每个Pod有一个独立的IP地址(前提是地址足够用),并且此IP被当前 Pod 中所有容器在内部共享使用。 pod删除后Infra 容器随机被删除,其IP被回收。

1、pod的调度流程及常见状态

1.1、pod的调度流程

Pod创建过程如上图所示,首先用户向apiserver发送创建pod的请求,apiserver收到用于创建pod请求后,对应会对该用户身份信息进行验证,该用户是否是合法的用户,是否具有创建pod的权限,如果能够通过apiserver的验证,则进行下一步,对用户提交的资源进行准入控制,所谓准入控制是指对用户提交的资源做格式,语法的验证,是否满足apiserver中定义的对应资源的api格式和语法;如果上述身份验证和准入控制能够顺利通过,接下来,apiserver才会把对应创建pod的信息存入etcd中,否者就直接拒绝用户创建pod;etcd将对应数据存放好以后,会返回给apiserver一个事件,即创建pod的相关信息已经存入etcd中了;apiserver收到etcd的资源信息存入完成事件后,会返回给用户一个pod创建完成的消息;随后,scheduler通过监视到apiserver上的资源变动事件,会对pod进行调度,调度规则就是先预选(预选就是把不符合pod运行的节点先踢出去,然后在剩下的节点中进行优选),然后再优选(优选就是在满足预选留下的节点中进行打分,得分高者负责运行pod);scheduler最后通过预选+优选的方式把pod调度到后端某个node节点上运行的结果返回给apiserver,由apiserver将最终调度信息存入etcd中,等待etcd将对应调度信息更新完毕后,再返回给apiserver一个pod状态信息更新完毕,apiserver再将对应状态返回给scheduler;随后负责运行pod的node节点上的kubelet通过监视apiserver的资源变动事件,会发现一个和自己相关的事件,此时对应节点上的kubelet会调用本地容器引擎,将对应pod在本地运行起来;当本地容器引擎将pod正常运行起来后,对应容器引擎会返回给本地kubelet一个pod运行完成的事件,随后再由kubelet将对应事件返回给apiserver,随后apiserver再将pod状态信息存入etcd中,etcd将更新pod状态信息完成的事件通过apiserver将对应事件返回给kubelet;如果此时用户查询pod状态,就能够正常通过apiserver在etcd中检索出来的pod状态;以上就是pod创建的一个大概过程;

1.2、pod的常见状态

  • Unschedulable:#Pod不能被调度,kube-scheduler没有匹配到合适的node节点
  • PodScheduled:#pod正处于调度中,在kube-scheduler刚开始调度的时候,还没有将pod分配到指定的node,在筛选出合适的节点后就会更新etcd数据,将pod分配到指定的node。
  • Pending: #正在创建Pod但是Pod中的容器还没有全部被创建完成=[处于此状态的Pod应该检查Pod依赖的存储是否有权限挂载等。
  • Failed:#Pod中有容器启动失败而导致pod工作异常。
  • Unknown:#由于某种原因无法获得pod的当前状态,通常是由于与pod所在的node节点通信错误。
  • Initialized:#所有pod中的初始化容器已经完成了。
  • ImagePullBackOff:#Pod所在的node节点下载镜像失败。
  • Running:#Pod内部的容器已经被创建并且启动。
  • Ready:#表示pod中的容器已经可以提供访问服务。
  • Error: #pod 启动过程中发生错误。
  • NodeLost: #Pod 所在节点失联。
  • Waiting: #Pod 等待启动。
  • Terminating: #Pod 正在被销毁。
  • CrashLoopBackOff:#pod崩溃,但是kubelet正在将它重启。
  • InvalidImageName:#node节点无法解析镜像名称导致的镜像无法下载。
  • ImageInspectError:#无法校验镜像,镜像不完整导致。
  • ErrImageNeverPull:#策略禁止拉取镜像,镜像中心权限是私有等。
  • RegistryUnavailable:#镜像服务器不可用,网络原因或harbor宕机。
  • ErrImagePull:#镜像拉取出错,超时或下载被强制终止。
  • CreateContainerConfigError:#不能创建kubelet使用的容器配置。
  • CreateContainerError:#创建容器失败。
  • RunContainerError:#pod运行失败,容器中没有初始化PID为1的守护进程等。
  • ContainersNotInitialized:#pod没有初始化完毕。
  • ContainersNotReady:#pod没有准备完毕。
  • ContainerCreating:#pod正在创建中。
  • PodInitializing:#pod正在初始化中。
  • DockerDaemonNotReady:#node节点decker服务没有启动。
  • NetworkPluginNotReady:#网络插件没有启动。

2、pause容器及init容器

2.1、pause容器简介

Pause 容器,又叫 Infra 容器,是pod的基础容器,镜像体积只有几百KB左右,配置在kubelet中,主要的功能是一个pod中多个容器的网络通信。
Infra 容器被创建后会初始化 Network Namespace,之后其它容器就可以加入到 Infra 容器中共享Infra 容器的网络了,因此如果一个 Pod 中的两个容器 A 和 B,那么关系如下 :

  • A容器和B容器能够直接使用 localhost 通信;
  • A容器和B容器可以可以看到网卡、IP与端口监听信息。
  • Pod 只有一个 IP 地址,也就是该 Pod 的 Network Namespace 对应的IP 地址(由Infra 容器初始化并创建)。
  • k8s环境中的每个Pod有一个独立的IP地址(前提是地址足够用),并且此IP被当前 Pod 中所有容器在内部共享使用。
  • pod删除后Infra 容器随机被删除,其IP被回收。

2.2、Pause容器共享的Namespace

  • NET Namespace:Pod中的多个容器共享同一个网络命名空间,即使用相同的IP和端口信息。
  • IPC Namespace:Pod中的多个容器可以使用System V IPC或POSIX消息队列进行通信。
  • .UTS Namespace:pod中的多个容器共享一个主机名。MNT Namespace、PID Namespace、User Namespace未共享。

2.3、Pause容器Namespace验证

1、 运行pod,进入容器查看iflink编号

2、到pod所在宿主机验证网卡

2.4、pause容器配置示例

2.4.1、准备nginx配置文件,并配置动静分离

error_log stderr;
events  worker_connections 1024; 
http 
  access_log /dev/stdout;
  server 
    listen 80 default_server;
    server_name www.mysite.com;
    location / 
      index index.html index.php;
      root /usr/share/nginx/html;
    
    location ~ \\.php$ 
    root /usr/share/nginx/html;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    
  

2.4.2、部署pause容器

2.4.2.1、下载pause镜像

nerdctl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.8

2.4.2.2、运行pause镜像为容器

nerdctl run -d -p 80:80 --name pause-container-test registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.8

2.4.3、准备测试web页面

root@deploy:~# mkdir html
root@deploy:~# cd html
root@deploy:~/html# echo "<h1>pause container web test</h1>" >index.html
root@deploy:~/html# cat >> index.php << EOF
> <?php
>      phpinfo();
> ?>
> EOF
root@deploy:~/html# ll
total 16
drwxr-xr-x 2 root root 4096 May 26 00:03 ./
drwxr-xr-x 9 root root 4096 May 26 00:02 ../
-rw-r--r-- 1 root root   34 May 26 00:02 index.html
-rw-r--r-- 1 root root   25 May 26 00:03 index.php
root@deploy:~/html# cat index.html 
<h1>pause container web test</h1>
root@deploy:~/html# cat index.php 
<?php
     phpinfo();
?>
root@deploy:~/html# 

2.4.4、部署nginx 容器,并使用pause容器网络

nerdctl run -d --name nginx-container-test \\
            -v `pwd`/nginx.conf:/etc/nginx/nginx.conf \\
            -v `pwd`/html:/usr/share/nginx/html \\
            --net=container:pause-container-test \\
            nginx:1.20.2

2.4.5、部署php容器,并使用pause容器网络

nerdctl run -d --name php-container-test \\
            -v `pwd`/html:/usr/share/nginx/html \\
            --net=container:pause-container-test \\
            php:5.6.40-fpm

2.4.6、pause容器验证

访问宿主机的80端口的index.php,看看是否能够访问到php页面?

2.5、init容器简介

2.5.1、init容器的作用

  • 可以为业务容器提前准备好业务容器的运行环境,比如将业务容器需要的配置文件提前生成并放在指定位置、检查数据权限或完整性、软件版本等基础运行环境。
  • 可以在运行业务容器之前准备好需要的业务数据,比如从OSS下载、或者从其它位置copy。
  • 检查依赖的服务是否能够访问。

2.5.2、init容器的特点

  • 一个pod可以有多个业务容器还能在有多个init容器,但是每个init容器和业务容器的运行环境都是隔离的。
  • init容器会比业务容器先启动。
  • init容器运行成功之后才会继续运行业务容器。
  • 如果一个pod有多个init容器,则需要从上到下逐个运行并且全部成功,最后才会运行业务容器。
  • init容器不支持探针检测(因为初始化完成后就退出再也不运行了)。

2.5.3、init容器示例

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: myserver-myapp 
  name: myserver-myapp-deployment-name
  namespace: myserver
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: myserver-myapp-frontend
  template:
    metadata:
      labels:
        app: myserver-myapp-frontend
    spec:
      containers:
        - name: myserver-myapp-container
          image: nginx:1.20.0 
          #imagePullPolicy: Always
          volumeMounts:
          - mountPath: "/usr/share/nginx/html/myserver"
            name: myserver-data
          - name: tz-config
            mountPath: /etc/localtime
      initContainers:
        - name: init-web-data
          image: centos:7.9.2009
          command: [\'/bin/bash\',\'-c\',"for i in `seq 1 10`;do echo \'<h1>\'$i web page at $(date +%Y%m%d%H%M%S) \'<h1>\' >> /data/nginx/html/myserver/index.html;sleep 1;done"]
          volumeMounts:
          - mountPath: "/data/nginx/html/myserver"
            name: myserver-data
          - name: tz-config
            mountPath: /etc/localtime
        - name: change-data-owner
          image: busybox:1.28
          command: [\'/bin/sh\',\'-c\',"/bin/chmod 644 /data/nginx/html/myserver/* -R"]
          volumeMounts:
          - mountPath: "/data/nginx/html/myserver"
            name: myserver-data
          - name: tz-config
            mountPath: /etc/localtime
      volumes:
      - name: myserver-data
        hostPath:
          path: /tmp/data/html
      - name: tz-config
        hostPath:
          path: /etc/localtime 

---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: myserver-myapp-service
  name: myserver-myapp-service-name
  namespace: myserver
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30080
  selector:
    app: myserver-myapp-frontend

上述配置清单,主要利用两个初始化容器对nginx主容器生成数据和修改数据文件权限的操作;在spec.template.spec字段下用initcontainers来定义初始化容器相关内容;
应用配置清单

访问nginx服务,看看对应数据是否生成?

2.6、Health Check

health check是指对容器做健康状态检测;该检测主要确保容器里的某些服务是否处于健康状态;该检测是一个周期性的动作,即每隔几秒或指定时间周期内进行检测;

2.6.1、docker health check实现方式

2.6.1.1、在docker-compose实现健康状态检测

version: \'3.6\'
services:
  nginx-service:
    image: nginx:1.20.2 
    container_name: nginx-web1
    expose:
      - 80
      - 443
    ports:
      - "80:80"
      - "443:443"
    restart: always
    healthcheck: #添加服务健康状态检查
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 5s #健康状态检查的间隔时间,默认为30s
      timeout: 5s #单次检查的失败超时时间,默认为30s
      retries: 3 #连续失败次数默认3次,当连续失败retries次数后将容器置为unhealthy状态
      start_period: 60s #60s后每间隔interval的时间检查一次,连续retries次后才将容器置为unhealthy状态, 但是start_period时间内检查成功就认为是检查成功并装容器置于healthy状态

应用配置清单

docker-compose -f docker-compose-demo.yaml up -d

验证容器健康状态

2.6.1.2、在dockerfile实现健康状态检测

FROM nginx:1.20.2

HEALTHCHECK --interval=5s --timeout=2s --retries=3 \\
CMD curl --silent --fail localhost:80 || exit 1

生成镜像

docker build -t mynginx:1.20.2 -f ./dockerfile .

运行容器

docker run -it -d -p 80:80 mynginx:1.20.2

验证健康状态检测

root@k8s-deploy:/compose# docker ps 
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS                            PORTS                               NAMES
c3af9bdd5a41   mynginx:1.20.2   "/docker-entrypoint.…"   4 seconds ago   Up 2 seconds (health: starting)   0.0.0.0:80->80/tcp, :::80->80/tcp   keen_brown
root@k8s-deploy:/compose# docker ps 
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS                   PORTS                               NAMES
c3af9bdd5a41   mynginx:1.20.2   "/docker-entrypoint.…"   9 seconds ago   Up 8 seconds (healthy)   0.0.0.0:80->80/tcp, :::80->80/tcp   keen_brown
root@k8s-deploy:/compose# 

在检测通过之前容器处于starting状态,检测通过(检测返回状态码为 0)之后为healthy状态,检测失败(检测返回状态码为 1)之后为unhealthy状态;

3、kubernetes pod生命周期

pod的生命周期( pod lifecycle),从pod start时候可以配置postStart检测,运行过程中可以配置livenessProbe和readinessProbe,最后在 stop前可以配置preStop操作。

3.1、探针简介

探针是由 kubelet 对容器执行的定期诊断,以保证Pod的状态始终处于运行状态,要执行诊断,kubelet 调用由容器实现的Handler(处理程序),也成为Hook(钩子),有三种类型的处理程序:

  1. ExecAction #在容器内执行指定命令,如果命令退出时返回码为0则认为诊断成功。
  2. TCPSocketAction #对指定端口上的容器的IP地址进行TCP检查,如果端口打开,则诊断被认为是成功的。
  3. HTTPGetAction:#对指定的端口和路径上的容器的IP地址执行HTTPGet请求,如果响应的状态码大于等于200且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  1. 成功:容器通过了诊断。
  2. 失败:容器未通过诊断。
  3. 未知:诊断失败,因此不会采取任何行动。

Pod 重启策略:Pod 一旦配置探针,在检测失败时候,会基于restartPolicy 对 Pod进行下一步操作:

  • restartPolicy (容器重启策略):
    • Always:当容器异常时,k8s自动重启该容器,ReplicationController/Replicaset/Deployment,默认为Always。
    • OnFailure:当容器失败时(容器停止运行且退出码不为0),k8s自动重启该容器。
    • Never:不论容器运行状态如何都不会重启该容器,Job或CronJob
  • imagePullPolicy (镜像拉取策略):
    • IfNotPresent:node节点没有此镜像就去指定的镜像仓库拉取,node有就使用node本地镜像。
    • Always:每次重建pod都会重新拉取镜像。
    • Never:从不到镜像中心拉取镜像,只使用本地镜像。

3.2、探针类型

  • startupProbe: #启动探针,kubernetes v1.16引入
    判断容器内的应用程序是否已启动完成,如果配置了启动探测,则会先禁用所有其它的探测,直到startupProbe检测成功为止,如果startupProbe探测失败,则kubelet将杀死容器,容器将按照重启策略进行下一步操作,如果容器没有提供启动探测,则默认状态为成功
  • livenessProbe: #存活探针
    检测容器容器是否正在运行,如果存活探测失败,则kubelet会杀死容器,并且容器将受到其重启策略的影响,如果容器不提供存活探针,则默认状态为 Success,livenessProbe用于控制是否重启pod。
  • readinessProbe: #就绪探针
    如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址,初始延迟之前的就绪状态默认为Failure(失败),如果容器不提供就绪探针,则默认状态为 Success,readinessProbe用于控制pod是否添加至service。

3.3、探针配置参数

探针有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为,官方文档https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

  • initialDelaySeconds: 120 #初始化延迟时间,告诉kubelet在执行第一次探测前应该等待多少秒,默认是0秒,最小值是0。
  • periodSeconds: 60 #探测周期间隔时间,指定了kubelet应该每多少秒秒执行一次存活探测,默认是 10 秒。最小值是 1。
  • timeoutSeconds: 5 #单次探测超时时间,探测的超时后等待多少秒,默认值是1秒,最小值是1。
  • successThreshold: 1 #从失败转为成功的重试次数,探测器在失败后,被视为成功的最小连续成功数,默认值是1,存活探测的这个值必须是1,最小值是 1。
  • failureThreshold:3 #从成功转为失败的重试次数,当Pod启动了并且探测到失败,Kubernetes的重试次数,存活探测情况下的放弃就意味着重新启动容器,就绪探测情况下的放弃Pod 会被打上未就绪的标签,默认值是3,最小值是1。

3.3.1、探针http配置参数

HTTP 探测器可以在 httpGet 上配置额外的字段

  • host: #连接使用的主机名,默认是Pod的 IP,也可以在HTTP头中设置 “Host” 来代替。
  • scheme: http #用于设置连接主机的方式(HTTP 还是 HTTPS),默认是 HTTP。
  • path: /monitor/index.html #访问 HTTP 服务的路径。
  • httpHeaders: #请求中自定义的 HTTP 头,HTTP 头字段允许重复。
  • port: 80 #访问容器的端口号或者端口名,如果数字必须在 1 ~ 65535 之间。

3.4、探针示例

3.4.1、使用httpGet实现pod存活性探测

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp-frontend-deployment
  namespace: myserver
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: myserver-myapp-frontend-label
    #matchExpressions:
    #  - key: app, operator: In, values: [myserver-myapp-frontend,ng-rs-81]
  template:
    metadata:
      labels:
        app: myserver-myapp-frontend-label
    spec:
      containers:
      - name: myserver-myapp-frontend-label
        image: nginx:1.20.2
        ports:
        - containerPort: 80
        readinessProbe:
        livenessProbe:
          httpGet:
            #path: /monitor/monitor.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 1
          successThreshold: 1
          failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 81
    targetPort: 80
    nodePort: 40012
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label 

上述配置清单,主要描述了使用httpget探针对nginxpod进行存活性探测,探测方法就是对容器的80端口,路径为/index.html进行每隔3秒访问一次,探测超时等待1秒,如果连续3次访问失败,则该pod存活性探测失败;只要有一次访问成功,则该pod存活性探测成功;

3.4.2、使用tcpSocket实现pod存活性探测

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp-frontend-deployment
  namespace: myserver
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: myserver-myapp-frontend-label
    #matchExpressions:
    #  - key: app, operator: In, values: [myserver-myapp-frontend,ng-rs-81]
  template:
    metadata:
      labels:
        app: myserver-myapp-frontend-label
    spec:
      containers:
      - name: myserver-myapp-frontend-label
        image: nginx:1.20.2
        ports:
        - containerPort: 80
        livenessProbe:
        #readinessProbe:
          tcpSocket:
            port: 80
            #port: 8080
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 81
    targetPort: 80
    nodePort: 40012
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label

3.4.3、使用exec执行命令的方式实现pod存活性探测

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp-redis-deployment
  namespace: myserver
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: myserver-myapp-redis-label
    #matchExpressions:
    #  - key: app, operator: In, values: [myserver-myapp-redis,ng-rs-81]
  template:
    metadata:
      labels:
        app: myserver-myapp-redis-label
    spec:
      containers:
      - name: myserver-myapp-redis-container
        image: redis
        ports:
        - containerPort: 6379
        livenessProbe:
        #readinessProbe:
          exec:
            command:
            #- /apps/redis/bin/redis-cli
            - /usr/local/bin/redis-cli 
            - quit
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
      
---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-redis-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 6379
    targetPort: 6379
    nodePort: 40016
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-redis-label

3.4.4、启动探针示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp-frontend-deployment
  namespace: myserver
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: myserver-myapp-frontend-label
    #matchExpressions:
    #  - key: app, operator: In, values: [myserver-myapp-frontend,ng-rs-81]
  template:
    metadata:
      labels:
        app: myserver-myapp-frontend-label
    spec:
      containers:
      - name: myserver-myapp-frontend-label
        image: nginx:1.20.2
        ports:
        - containerPort: 80
        startupProbe:
          httpGet:
            path: /index.html
            port: 80
          initialDelaySeconds: 5 #首次检测延迟5s
          failureThreshold: 3  #从成功转为失败的次数
          periodSeconds: 3 #探测间隔周期
---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 81
    targetPort: 80
    nodePort: 40012
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label

3.4.5、启动探针,存活探针,就绪探针示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp-frontend-deployment
  namespace: myserver
spec:
  replicas: 3
  selector:
    matchLabels: #rs or deployment
      app: myserver-myapp-frontend-label
    #matchExpressions:
    #  - key: app, operator: In, values: [myserver-myapp-frontend,ng-rs-81]
  template:
    metadata:
      labels:
        app: myserver-myapp-frontend-label
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: myserver-myapp-frontend-label
        image: nginx:1.20.2
        ports:
        - containerPort: 80
        startupProbe:
          httpGet:
            path: /index.html
            port: 80
          initialDelaySeconds: 5 #首次检测延迟5s
          failureThreshold: 3  #从成功转为失败的次数
          periodSeconds: 3 #探测间隔周期
        readinessProbe:
          httpGet:
            #path: /monitor/monitor.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        livenessProbe:
          httpGet:
            #path: /monitor/monitor.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 81
    targetPort: 80
    nodePort: 40012
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label

3.5、postStart and preStop handlers简介

官方文档https://kubernetes.io/zh/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/
postStart 和 preStop handlers 处理函数

  • postStart:Pod启动后立即执行指定的擦操作:
    • Pod被创建后立即执行,即不等待pod中的服务启动。
    • 如果postStart执行失败pod不会继续创建。
  • preStop:
    • pod被停止之前执行的动作。
    • 如果preStop一直执行不完成,则最后宽限2秒后强制删除。

3.5.1、postStart and preStop handlers示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myserver-myapp1-lifecycle
  labels:
    app: myserver-myapp1-lifecycle
  namespace: myserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myserver-myapp1-lifecycle-label
  template:
    metadata:
      labels:
        app: myserver-myapp1-lifecycle-label
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: myserver-myapp1-lifecycle-label
        image: tomcat:7.0.94-alpine 
        lifecycle:
          postStart:
            exec:
             #command: 把自己注册到注册在中心
              command: ["/bin/sh", "-c", "echo \'Hello from the postStart handler\' >> /usr/local/tomcat/webapps/ROOT/index.html"]

            #httpGet:
            #  #path: /monitor/monitor.html
            #  host: www.magedu.com
            #  port: 80
            #  scheme: HTTP
            #  path: index.html
          preStop:
            exec:
             #command: 把自己从注册中心移除
              command:
                - /bin/bash
                - -c
                - \'sleep 10000000\'
              #command: ["/usr/local/tomcat/bin/catalina.sh","stop"]
              #command: [\'/bin/sh\',\'-c\',\'/path/preStop.sh\']
        ports:
          - name: http
            containerPort: 8080

---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp1-lifecycle-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
    nodePort: 30012
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp1-lifecycle-label

3.6、Pod的终止流程

  1. 向API-Server提交删除请求、API-Server完成鉴权和准入并将事件写入etcd
  2. Pod被设置为”Terminating”状态、从service的Endpoints列表中删除并不再接受客户端请求。
  3. pod执行PreStop
  4. kubelet向pod中的容器发送SIGTERM信号(正常终止信号)终止pod里面的主进程,这个信号让容器知道自己很快将会被关闭terminationGracePeriodSeconds: 60 #可选终止等待期(pod删除宽限期),如果有设置删除宽限时间,则等待宽限时间到期,否则最多等待30s,Kubernetes等待指定的时间称为优雅终止宽限期,默认情况下是30秒,值得注意的是等待期与preStop Hook和SIGTERM信号并行执行,即Kubernetes可能不会等待preStop Hook完成(最长30秒之后主进程还没有结束就就强制终止pod)。
  5. SIGKILL信号被发送到Pod,并删除Pod

k8s-Pod状态和探针

Pod状态

第一阶段:

Pending:
#正在创建Pod但是Pod中的容器还没有全部被创建完成,处于此状态的Pod应该检查Pod依赖的存储是否有权限挂载、镜像是否可以下载、调度是否正常等。

Failed
#Pod中有容器启动失败而导致pod工作异常。检查事件

Unknown
#由于某种原因无法获得pod的当前状态,通常是由于与pod所在的node节点通信错误。

Succeeded
#Pod中的所有容器都被成功终止即pod里所有的containers均已terminated。

第二阶段:

Unschedulable:
#Pod不能被调度,kube-scheduler没有匹配到合适的node节点

PodScheduled
#pod正处于调度中,在kube-scheduler刚开始调度的时候,还没有将pod分配到指定的node,在筛选出合适的
节点后就会更新etcd数据,将pod分配到指定的node

Initialized
#所有pod中的初始化容器已经完成了

ImagePullBackOff:
#Pod所在的node节点下载镜像失败

Running
#Pod内部的容器已经被创建并且启动。

Ready
#表示pod中的容器已经可以提供访问服务

范例:其他状态

Error: #pod 启动过程中发生错误
NodeLost: #Pod 所在节点失联
Unkown: #Pod 所在节点失联或其它未知异常
Waiting: #Pod 等待启动
Pending: #Pod 等待被调度
Terminating: #Pod 正在被销毁
CrashLoopBackOff:#pod,但是kubelet正在将它重启
InvalidImageName:#node节点无法解析镜像名称导致的镜像无法下载
ImageInspectError:#无法校验镜像,镜像不完整导致
ErrImageNeverPull:#策略禁止拉取镜像,镜像中心权限是私有等
ImagePullBackOff:#镜像拉取失败,但是正在重新拉取
RegistryUnavailable:#镜像服务器不可用,网络原因或harbor宕机
ErrImagePull:#镜像拉取出错,超时或下载被强制终止
CreateContainerConfigError:#不能创建kubelet使用的容器配置
CreateContainerError:#创建容器失败
PreStartContainer: #执行preStart hook报错,Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发
起的,当容器中的进程启动前或者容器中的进程终止之前运行,比如容器创建完成后里面的服务启动之前可以检查一下
依赖的其它服务是否启动,或者容器退出之前可以把容器中的服务先通过命令停止。
PostStartHookError:#执行 postStart hook 报错
RunContainerError:#pod运行失败,容器中没有初始化PID为1的守护进程等
ContainersNotInitialized:#pod没有初始化完毕
ContainersNotReady:#pod没有准备完毕
ContainerCreating:#pod正在创建中
PodInitializing:#pod正在初始化中
DockerDaemonNotReady:#node节点docker服务没有启动
NetworkPluginNotReady:#网络插件还没有完全启动

探针(保证k8s中服务的可用性)

探针 是由 kubelet 对容器执行的定期诊断,以保证Pod的状态始终处于运行状态,要执行诊断,kubelet 调用由容

两种都是检查通过就什么都不做

器实现的Handler(处理程序),有三种类型的处理程序:

ExecAction
#在容器内执行指定命令,如果命令退出时返回码为0则认为诊断成功。

TCPSocketAction
#对指定端口上的容器的IP地址进行TCP检查,如果端口打开,则诊断被认为是成功的。

HTTPGetAction
#对指定的端口和路径上的容器的IP地址执行HTTPGet请求,如果响应的状态码大于等于200且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

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

探针类型:

livenessProbe(检查不通过,就重启;基于镜像还原)
#存活探针,检测容器容器是否正在运行,如果存活探测失败,则kubelet会杀死容器,并且容器将受到其重启策略的影响,如果容器不提供存活探针,则默认状态为 Success,livenessProbe用于控制是否重启pod。

readinessProbe(检查不通过,不会添加到ep,反之)
#就绪探针,如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址,初始延迟之前的就绪状态默认为Failure(失败),如果容器不提供就绪探针,则默认状态为 Success,readinessProbe用于控制pod是否添加至service。

探针有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:

initialDelaySeconds: 120
#初始化延迟时间,告诉kubelet在执行第一次探测前应该等待多少秒,默认是0秒,最小值是0

periodSeconds: 60
#探测周期间隔时间,指定了kubelet应该每多少秒秒执行一次存活探测,默认是 10 秒。最小值是 1

timeoutSeconds: 5
#单次探测超时时间,探测的超时后等待多少秒,默认值是1秒,最小值是1。

successThreshold: 1
#从失败转为成功的重试次数,探测器在失败后,被视为成功的最小连续成功数,默认值是1,存活探测的这个值必须是1,最小值是 1。

failureThreshold: 3
#从成功转为失败的重试次数,当Pod启动了并且探测到失败,Kubernetes的重试次数,存活探测情况下的放弃就意味着重新启动容器,就绪探测情况下的放弃Pod 会被打上未就绪的标签,默认值是3,最小值是1。

HTTP 探测器可以在 httpGet 上配置额外的字段:

host:
#连接使用的主机名,默认是Pod的 IP,也可以在HTTP头中设置 “Host” 来代替。
scheme: http
#用于设置连接主机的方式(HTTP 还是 HTTPS),默认是 HTTP。
path: /monitor/index.html
#访问 HTTP 服务的路径。
httpHeaders:
#请求中自定义的 HTTP 头,HTTP 头字段允许重复。
port: 80
#访问容器的端口号或者端口名,如果数字必须在 1 ~ 65535 之间。

livenessProbe和readinessProbe的对比:

配置参数一样
livenessProbe #连续探测失败会重启、重建pod,readinessProbe不会执行重启或者重建Pod操作

livenessProbe #连续检测指定次数失败后会将容器置于(Crash Loop BackOff)且不可用,readinessProbe不会

readinessProbe #连续探测失败会从service的endpointd中删除该Pod,livenessProbe不具备此功能,但是会将容器挂起livenessProbe

livenessProbe用户控制是否重启pod,readinessProbe用于控制pod是否添加至service

建议:
两个探针都配置

这里都是存活探针的案例

HTTP探针示例:(这是正确的)

[root@k8s-server1 case2]# cat case1.yaml 
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: ng-deploy-80
    #matchExpressions:
    # - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
  template:
    metadata:
      labels:
        app: ng-deploy-80
    spec:
      containers:
      - name: ng-deploy-80
        image: harbor.longxuan.net/n520/nginx:1.18.0
        ports:
        - containerPort: 80
        #readinessProbe:
        livenessProbe:
          httpGet:
            #path: /montic/montic.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  ports:
  - name: http
    port: 86
    targetPort: 80
    nodePort: 30025
    protocol: TCP
  type: NodePort
  selector:
    app: ng-deploy-80

执行

[root@k8s-server1 case2]# kubectl apply -f case1.yaml -n linux66
deployment.apps/nginx-deployment configured
service/ng-deploy-80 unchanged

当打开不存在的url时,pod将会一直重启

TCP探针示例

创建(正确的)

[root@k8s-server1 case2]# cat case2.yaml 
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: ng-deploy-80
    #matchExpressions:
    # - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
  template:
    metadata:
      labels:
        app: ng-deploy-80
    spec:
      containers:
      - name: ng-deploy-80
        image: harbor.longxuan.net/n520/nginx:1.18.0
        ports:
        - containerPort: 80
        #readinessProbe:
        livenessProbe:
          tcpSocket:
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  ports:
  - name: http
    port: 86
    targetPort: 80
    nodePort: 30026
    protocol: TCP
  type: NodePort
  selector:
    app: ng-deploy-80

执行

[root@k8s-server1 case2]# kubectl apply -f case2.yaml -n linux66
deployment.apps/nginx-deployment created
service/ng-deploy-80 created

当yaml文件写错端口,pod就会被一直重启,同时服务也访问不了

[root@k8s-server1 case2]# kubectl get pod -n linux66
NAME                                        READY   STATUS             RESTARTS   AGE
            0          41h
nginx-deployment-69bd984878-nttnp           0/1     CrashLoopBackOff   3          60s

ExecAction探针:

可以基于指定的命令对Pod进行特定的状态检查。

# 这是正确的
[root@k8s-server1 case2]# cat case3.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis-deploy-6379
  template:
    metadata:
      labels:
        app: redis-deploy-6379
    spec:
      containers:
      - name: redis-deploy-6379
        image: harbor.longxuan.net/n520/redis:v1
        ports:
        - containerPort: 80
        livenessProbe:
        #readinessProbe:
          exec:
            command:
              #- /app/redis/bin/redis-cli
              - /usr/local/bin/redis-cli
              - quit
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: redis-deploy-6379
spec:
  ports:
  - name: http
    port: 6379
    targetPort: 6379
    nodePort: 30679
    protocol: TCP
  type: NodePort
  selector:
    app: redis-deploy-6379

执行

[root@k8s-server1 case2]# kubectl apply -f case3.yaml -n linux66
deployment.apps/redis-deployment created
service/redis-deploy-6379 created

如果是错误的,pod就会重启三次,但名字不会变,如果端口检测连续超过指定的三次都没有通过,则Pod状态如下:

[root@k8s-server1 case2]# kubectl get pod -n linux66
NAME                                        READY   STATUS             RESTARTS   AGE
redis-deployment-574f795dcd-gx9v4           0/1     CrashLoopBackOff   3          109s

就绪探针

当路径不存在时,查看ep(用来记录一个service对应的所有pod的访问地址。)是不会添加进去,同时pod是不会重启

# 这是正确的
[root@k8s-server1 case2]# cat case1.yaml 
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels: #rs or deployment
      app: ng-deploy-80
    #matchExpressions:
    # - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
  template:
    metadata:
      labels:
        app: ng-deploy-80
    spec:
      containers:
      - name: ng-deploy-80
        image: harbor.longxuan.net/n520/nginx:1.18.0
        ports:
        - containerPort: 80
        readinessProbe:
        #livenessProbe:
          httpGet:
            #path: /montic/montic.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  ports:
  - name: http
    port: 86
    targetPort: 80
    nodePort: 30025
    protocol: TCP
  type: NodePort
  selector:
    app: ng-deploy-80

执行

[root@k8s-server1 case2]# kubectl apply -f case1.yaml -n linux66

deployment.apps/nginx-deployment configured
service/ng-deploy-80 unchanged

查看ep

[root@k8s-server1 case2]# kubectl get ep -n linux66
NAME                    ENDPOINTS                                                   AGE
linux66-nginx-service   10.100.1.14:443,10.100.2.6:443,10.100.1.14:80 + 1 more...   42h
ng-deploy-80            10.100.5.15:80                                              36m
redis-deploy-6379                                                                   16m
不正确的,查看ep结果如下:
[root@k8s-server1 case2]# kubectl get ep -n linux66
NAME                    ENDPOINTS                                                   AGE
linux66-nginx-service   10.100.1.14:443,10.100.2.6:443,10.100.1.14:80 + 1 more...   42h
ng-deploy-80                                                                        3s
redis-deploy-6379                                                                   18m

组合使用

[root@k8s-server1 case2]# cat case1.yaml 
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels: #rs or deployment
      app: ng-deploy-80
    #matchExpressions:
    # - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
  template:
    metadata:
      labels:
        app: ng-deploy-80
    spec:
      containers:
      - name: ng-deploy-80
        image: harbor.longxuan.net/n520/nginx:1.18.0
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            #path: /montic/montic.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

        livenessProbe:
          httpGet:
            #path: /montic/montic.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  ports:
  - name: http
    port: 86
    targetPort: 80
    nodePort: 30025
    protocol: TCP
  type: NodePort
  selector:
    app: ng-deploy-80

执行

[root@k8s-server1 case2]# kubectl apply -f case1.yaml -n linux66

deployment.apps/nginx-deployment configured
service/ng-deploy-80 unchanged

当路径不存在时,查看ep如下信息

[root@k8s-server1 case2]# kubectl get ep -n linux66
NAME                    ENDPOINTS                                                   AGE
linux66-nginx-service   10.100.1.14:443,10.100.2.6:443,10.100.1.14:80 + 1 more...   42h
ng-deploy-80            10.100.2.14:80                                              8m51s
redis-deploy-6379                                                                   26m

查看pod时,信息如下,探测到失败就已经重启

[root@k8s-server1 case2]# kubectl get pod -n linux66
nginx-deployment-5454b65b59-nxxjz           1/1     Running            1          2m37s

再查看ep,如下信息

[root@k8s-server1 case2]# kubectl get ep -n linux66
NAME                    ENDPOINTS                                                   AGE
linux66-nginx-service   10.100.1.14:443,10.100.2.6:443,10.100.1.14:80 + 1 more...   42h
ng-deploy-80            10.100.2.14:80,10.100.5.17:80                               9m7s
redis-deploy-6379                                                                   27m

Pod重启策略:

k8s在Pod出现异常的时候会自动将Pod重启以恢复Pod中的服务。

restartPolicy:
Always:当容器异常时,k8s自动重启该容器,ReplicationController/Replicaset/Deployment。
OnFailure:当容器失败时(容器停止运行且退出码不为0),k8s自动重启该容器。
Never:不论容器运行状态如何都不会重启该容器,Job或CronJob。

镜像拉取策略:

imagePullPolicy: IfNotPresent #node节点没有此镜像就去指定的镜像仓库拉取,node有就使用node本地镜像。
imagePullPolicy: Always #每次重建pod都会重新拉取镜像
imagePullPolicy: Never #从不到镜像中心拉取镜像,只使用本地镜像

以上是关于K8s Pod状态与容器探针的主要内容,如果未能解决你的问题,请参考以下文章

K8S 之 为POD创建基于HTTP的存活探针

k8s实践16:kubernetes监测探针简单测试

k8s之Pod健康检测

再战 k8s:Pod 生命周期与重启策略

k8s 探针

k8s-探针(四)