袁党生博客

  • 主页
  • linux基础
  • SHELL
  • WEB
  • 负载
  • 企业级应用
  • 数据库
  • KVM
  • Docker
  • K8S
  • 监控
  • 存储
  • 博客搭建问题
  1. 首页
  2. K8S
  3. 正文

八、Pod常见状态与探针

2023年6月15日 2032点热度 0人点赞 0条评论


本章概述

  • Pod常见状态
  • pause容器
  • init容器
  • Health Check(容器健康检查)
  • kubernetes pod生命周期和探针

前言
pod调度流程:

(1)客户端发起创建pod的请求
(2)api-server验证请求是否合法
api-server收到请求,对用户进行验证,调用本地/etc/.kube/config文件,如果验证不通过,拒绝用户访问(报403,promising deny);验证通过后,对用户命令(验证命令格式、参数)进行验证
(3)api-server验证通过后,将请求指令写入etcd,etcd返回api-server,表明事件写入完成。
(4)api-server通过watch机制把新事件通知kube-scheduler,scheduler获取新的事件,根据调度算法(设置的容忍、污点等策略),对node节点进行资源评分(资源评估策略包括:最小资源优先,设置标签优先,资源均衡优先),把任务调度到资源最优的node上
(5)然后kube-scheduler把调度结果返回api-server,api-server把调度结果写入etcd(即bind pod,把pod分配给某个node来创建)。
(6)etcd向api-server返回结果,表明数据写入完成;api-server也会向scheduler返回结果,告诉scheduler该事件已经向etcd写入完成
(7)node上的kubelet通过watch机制从apiserver获取新的事件
(8)kubelet调用本地node上的运行时(runtime)docker,由docker拉取镜像、创建容器,容器创建成功后把信息返回给kubelet,由kubelet把pod信息以及node信息返回给api-server,api-server写入etcd
(8)etcd返回信息给api-server,表明事件写入完成;api-server会向kubelet返回结果,告诉kubelet该事件已经向etcd写入完成
(9)此时,用户可以获取到容器创建成功的信息,可以通过kubectl命令获取到pod的信息

Pod从创建到回收,存在多种状态,了解pod的各种状态有助于我们解决pod出现的各种问题
流程图如下:

8.1 Pod常见状态

除了带有#号的之外,其他状态均是比较常见的pod状态

Unschedulable:#Pod不能被调度,kube-scheduler没有匹配到合适的node节点(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节点无法解析镜像名称导致的镜像无法下载(报404)
#ImageInspectError:#无法校验镜像,镜像不完整导致
ErrImageNeverPull:#策略禁止拉取镜像,镜像中心权限是私有等(报403)
#RegistryUnavailable:#镜像服务器不可用,网络原因或harbor宕机
ErrImagePull:#镜像拉取出错,超时或下载被强制终止
#CreateContainerConfigError:#不能创建kubelet使用的容器配置
#CreateContainerError:#创建容器失败
#RunContainerError:#pod运行失败,容器中没有初始化PID为1的守护进程等
#ContainersNotInitialized:#pod没有初始化完毕
#ContainersNotReady:#pod没有准备完毕
ContainerCreating:#pod正在创建中
#PodInitializing:#pod正在初始化中
#DockerDaemonNotReady:#node节点decker服务没有启动
#NetworkPluginNotReady:#网络插件没有启动

如果容器出现以上状态,可以通过kubectl describe pod <podname> -n <namespace>来查看具体原因

8.2 pause容器

Pause 容器,又叫 Infra 容器,是pod的基础容器,镜像体积只有几百KB 左右,配置在kubelet中,主要的功能是一个pod中多个容器的网络通信。

Infra 容器被创建后会初始化 Network Namespace,之后其它容器就可以加入到 Infra 容器中共享Infra 容器的网络了,因此如果一个 Pod 中的两个容器 A 和 B,那么关系如下 :

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

Pause容器共享的Namespace有三个,分别是:

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

但是MNT Namespace、PID Namespace、User Namespace这三个namespace并不共享。

如何查看容器使用宿主机的哪个网卡
通过deployment创建两个容器,其中一个容器10.200.107.205在172.31.7.113上

在宿主机172.31.7.113上进入容器10.200.107.205
kubectl exec -it nginx-deployment-55fdf878b9-825k6 bash
由于使用的是官方nginx镜像,需要安装ethtool、net-tools
apt install ethtool net-tools
查看pod网卡peer编号(pod使用的虚拟网卡为一对,一个在pod网卡eth0上,另一个在宿主机网卡上,因此要查看peer编号)
ethtool -S eth0

在宿主机172.31.7.113上使用ip link show命令查看宿主机网卡编号,编号为16的网卡就是pod使用的物理机网卡

查看同一个pod中多个容器共用的namespace
由于同一个pod中的两个nginx容器和pause容器共用network namespace,因此我们在该pod的network namespace中可以查到三个容器共用的ip地址
在容器所在宿主机上/run/docker/netns目录下保存着容器的namespace信息

通过命令查看/run/docker/netns目录下每个namespace对应的id,可以看到15bc17992dff对应的ip地址为10.200.107.205,该地址是容器的ip地址,因此15bc17992dff就是三个容器共用的network namespace,命令如下:
sudo nsenter --net=/run/docker/netns/15bc17992dff ifconfig

8.3 init容器

init容器简介
init容器的作用:

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

init容器的特点:

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


以mysql主从为例:
部署mysql主从时,在主节点部署完成后,一般会将主节点数据复制到从节点,完成基础的全量数据同步,再部署从节点,这样一旦从节点部署完成,备节点只需同步后续新增数据,也可以直接在备节点读取数据。
以上操作可以通过init容器实现:
在部署mysql slave节点之前,可以使用init容器把mysql master节点的数据同步到slave节点的pvc(存储)中,数据同步完成后再部署slave节点。

yaml文件字段查询:kubectl explain deployment.spec.template.spec.initContainers
示例:(由init容器为nginx容器生成web页面,init容器和nginx容器挂载同一个路径下的存储卷)

vim 1-init-container.yaml
kind: Deployment
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.16.0
          #imagePullPolicy: Always
          volumeMounts:
          - mountPath: "/usr/share/nginx/html/myserver"   #指定nginx容器内挂载路径
            name: myserver-data  #指定挂载的存储卷,和init容器挂载的存储卷一样
      initContainers:
        - name: init-web-data   #指定第1个init容器,为nginx生成web页面
          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"]  #在/data/nginx/html/myserver/index.html生成数据,为nginx生成web页面
          volumeMounts:
          - mountPath: "/data/nginx/html/myserver"
            name: myserver-data   #指定挂载的存储卷,和nginx容器挂载的存储卷一样
        - name: change-data-owner  #指定第2个init容器,更改nginx文件权限
          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
      volumes:
      - name: myserver-data
        hostPath:   #指定hostPath类型的存储卷,容器删除后,数据保留,但是该数据不能跨主机访问
          path: /tmp/data/html

---
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        #指定暴露的node端口
  selector:
app: myserver-myapp-frontend

创建pod
kubectl apply -f 1-init-container.yaml
验证:(1)init容器是按照顺序执行的 (2)先执行init容器创建,再执行业务容器创建
查看容器初始化过程(按照顺序执行):
第一个init容器正在初始化

第二个init容器正在初始化

pod正在初始化

初始化完成,pod正常运行

访问验证:在浏览器输入172.31.7.113:30080/myserver/index.html

注意:如果yaml文件配置(如修改镜像版本),然后重新执行yaml文件,初始化的动作会再执行一遍。在新的容器创建之前,访问的还是旧的页面,一旦容器创建完成,就会访问到新的web页面

8.4 Health Check(容器健康检查)

由发起者对容器进行周期性健康状态检测。
周期检测相当于人类的周期性体检
每次检测相当于人类每次体检的内容
实现方式:
1、在docker-compose实现探针(需要安装docker-compose)

vim 1.docker-compose.yaml
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状态

更改文件名:
mv 1.docker-compose.yaml docker-compose.yaml
执行docker-compose创建容器
docker-compose up -d
查看容器状态(显示容器状态)

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

vim 2.Dockerfile
FROM nginx:1.20.2
maintainer "jack 2973707860@qq.com"
HEALTHCHECK --interval=5s --timeout=2s --retries=3 \
  CMD curl --silent --fail localhost:80 || exit 1

更改文件名:
mv 2.Dockerfile Dockerfile
根据Dockerfile打镜像
docker build -t myserver-myapp:v1 .

根据新打包的镜像拉起容器
docker run -d -p 8080:80 --name=myapp-test myserver-myapp:v1
查看容器状态

8.5 kubernetes pod生命周期和探针

8.5.1 pod lifecycle(pod生命周期)

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

pod生命周期存在以下几个阶段:
  Start、Readiness、Liveness、Stop

8.5.2 探针

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

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

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

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

8.5.2.2 探测失败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 #从不到镜像中心拉取镜像,只使用本地镜像

8.5.2.3 探针类型

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

8.5.2.4 探针通用配置参数
官网链接: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。注意:timeoutSeconds的值要小于等于periodSeconds的值。
successThreshold: 1
    从失败转为成功的重试次数,探测器在失败后,被视为成功的最小连续成功数,默认值是1,存活探测的这个值必须是1,最小值是 1。(如果写其他值,创建容器时会提示语法错误)
failureThreshold: 3
    从成功转为失败的重试次数,当Pod启动了并且探测到失败,Kubernetes的重试次数,存活探测情况下的放弃就意味着重新启动容器,就绪探测情况下的放弃Pod 会被打上未就绪的标签,默认值是3,最小值是1。

8.5.2.5 探针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 之间。

示例1:(http探针)

vim1-http-Probe.yaml
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:       #指定http检测
            #path: /monitor/monitor.html
            path: /index.html   #指定探测路径
            port: 80           #指定探测端口
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 40018        #暴露宿主机端口,用来在集群外部进行访问测试
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label

创建pod

访问验证:

验证liveness存活探针:(把liveness探测路径故意改错,查看效果)
更改内容如下(带有注释部分):

        livenessProbe:
          httpGet:
            path: /monitor/monitor.html      #修改探测路径
            #path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3

重新加载yaml文件,然后查看pod状态,pod重启次数在增加,说明liveness探针已生效

查看pod日志,可以发现liveness检测没通过,容器在重启
kubectl describe pod myserver-myapp-frontend-deployment-75bbd47ff-lstdg -n myserver

此时,pod因为liveness探针检测没通过而一直重启,但是流量仍然会向该容器调度,我们仍然会访问该服务。
为了防止这种情况,需要配置readiness探针(就绪探测可以将流量调度到其他pod)
readiness探测配置如下:

vim1-http-Probe.yaml
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:       #指定http检测
            path: /monitor/monitor.html  
            #path: /index.html       #指定探测路径
            port: 80             #指定探测端口
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3
        readinessProbe:        #配置就绪探针
          httpGet:        #指定http检测
            #path: /monitor/monitor.html
            path: /index.html        #指定探测路径
            port: 80              #指定探测端口
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: myserver-myapp-frontend-service
  namespace: myserver
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 40018
    protocol: TCP
  type: NodePort
  selector:
    app: myserver-myapp-frontend-label
为了验证readiness探针,把探测路径故意写错
更改内容如下(红色字体部分):
        readinessProbe:
          httpGet:
            path: /monitor/monitor.html
            #path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3

创建pod
kubectl apply -f 1-http-Probe.yaml
查看pod,READY列为0/1,pod未就绪,说明readiness探针已生效

查看pod日志,发现readiness检测未通过

查看endpoint,地址为空,说明已经将pod从service上摘除,不再向pod调度流量

访问web页面,也无法再访问

另外,如果liveness探针探测路径配置正常,但是readiness探针探测路径配置错误,虽然容器可以正常提供服务,但pod也会被从service上摘除。

示例2:(tcp探测)

vim 2-tcp-Probe.yaml
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:          #指定tcp检测
            #port: 8080
            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

创建pod
kubectl apply -f 2-tcp-Probe.yaml
查看pod

验证tcp liveness探针,把tcp探测端口故意改错
更改内容如下(将80端口修改为8080端口):

livenessProbe:
        #readinessProbe:
          tcpSocket:          #指定tcp检测
            port: 8080          #指定错误的探测端口
            #port: 80        
          initialDelaySeconds: 5          #初始化延迟时间
          periodSeconds: 3              #检测间隔时间
          timeoutSeconds: 5             #超时时间
          successThreshold: 1           #从失败转为成功检测次数
          failureThreshold: 3          #从成功转为失败检测次数

重新加载yaml文件,发现pod重启次数在增加
kubectl apply -f 2-tcp-Probe.yaml
kubectl get pod -n myserver

查看pod日志,tcp探测未通过,容器在重启
kubectl describe pod myserver-myapp-frontend-deployment-7488ff6449-ts4sc -n myserver

同样,如果配置错误的readiness探针,会将pod从service上摘除(将80端口修改为8080进行验证)

readinessProbe:
          tcpSocket:          #指定tcp检测
            port: 8080      #指定错误的探测端口
            #port: 80        
          initialDelaySeconds: 5          #初始化延迟时间
          periodSeconds: 3              #检测间隔时间
          timeoutSeconds: 5             #超时时间
          successThreshold: 1           #从失败转为成功检测次数
          failureThreshold: 3          #从成功转为失败检测次数

查看pod,pod状态READY为0/1,说明pod未就绪

查看pod日志,readiness探针检测失败,容器会从service上摘除,service不向该pod调度流量
kubectl describe pod myserver-myapp-frontend-deployment-77c796bd5c-sv7jt -n myserver

service的endpoint为空,说明pod已经被摘除

示例3:(通过命令检测)

vim 3-exec-Probe.yaml
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        #部署redis
        ports:
        - containerPort: 6379
        livenessProbe:
        #readinessProbe:
          exec:
            command:         #指定探测的命令
            #- /apps/redis/bin/redis-cli
            - /usr/local/bin/redis-cli       #通过命令行登录redis客户端
            - 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

创建pod
kubectl apply -f 3-exec-Probe.yaml
查看pod

进行端口验证(在主机进行telnet测试)
telnet 172.31.7.113 40016 可以telnet通,说明服务正常

验证liveness探针,将配置故意写错
更改内容如下(修改探测路径):

        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

查看pod日志,liveness探针检测失败,指定的命令找不到

容器在不断地重启,说明liveness探针已生效

示例4:(startupProbe检测)
k8s版本1.16以后引入,会在容器启动后就进行检测,该检测在liveness和readiness检测之前进行,该检测通过后才会进行其他检测(liveness和readiness检测)

vim 5-startupProbe-livenessProbe-readinessProbe.yaml
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:
      terminationGracePeriodSeconds: 60
      containers:
      - name: myserver-myapp-frontend-label
        image: nginx:1.20.2
        ports:
        - containerPort: 80
        startupProbe:     #指定startupProbe检测
          httpGet:
            #path: /monitor/index.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5 #首次检测延迟5s
          failureThreshold: 3  #从成功转为失败的次数
          periodSeconds: 3 #探测间隔周期
        readinessProbe:      #指定readinessProbe检测
          httpGet:
            #path: /monitor/monitor.html
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        livenessProbe:         #指定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

创建pod

可以正常访问web页面

验证startupProbe探针,故意将探测路径改错
更改内容如下(修改探测路径)

        startupProbe:     #指定startupProbe检测
          httpGet:
            #path: /monitor/index.html      #指定错误的探测路径
            path: /index.html
            port: 80
          initialDelaySeconds: 5 #首次检测延迟5s
          failureThreshold: 3  #从成功转为失败的次数
          periodSeconds: 3 #探测间隔周期

查看pod日志,startupProbe检测失败,会将容器重启,但readiness和liveness探针检测都不会进行

容器READY显示为0/1,说明容器未就绪;状态为CrashLoopBackOff(探针检测失败,容器正在进行重启),容器重启次数也在增加。

再次访问web页面,访问失败

8.5.2.6 postStart and preStop handlers
官网链接:https://kubernetes.io/zh/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/
postStart 和 preStop handlers 处理函数:

postStart:
    Pod被创建后立即执行检测,即不等待pod中的服务启动。
    如果postStart执行失败pod不会继续创建
    postStart就是启动探针,即startupProbe
preStop:
    在pod被停止之前执行

init container,初始化容器,在主容器启动之前初始化环境。
postStart,即starupProbe启动探针,容器启动后进行探测,启动探针探测通过后才会进行其他探测(readinessProbe和livenessProbe),一般会延迟一段时间再进行检测。
readiness,就绪探针, 用于控制pod是否添加至service。
liveness,存活探针,用于控制是否重启pod。
preStop,容器停止之前的动作,可以在容器关闭前执行一些命令,使容器优雅的停止。

preStop使用场景:

如果直接关闭容器,会使得正在访问该容器的用户访问出现异常,给用户的体验不好。如果在容器关闭之前做一些操作,比如将容器从前端负载均衡上摘除或者将容器地址从注册中心删除,后续的请求将不会再发送给该容器,然后设置延迟多少秒后关闭,可以让容器将正在运行的任务关闭,然后再关闭容器,这样一来,就不会产生用户访问服务异常的情况。

Pod的终止流程

1.创建pod
(1)完成调度流程
(2)容器启动并执行postStart
(3)livenessProbe(周期性存活探针检测)
(4)进入running状态
(5)readinessProbe(周期性就绪探针检测)
(6)service关联pod
(7)接受客户端请求
2.删除pod
(1)Pod被设置为”Terminating”状态、从service的Endpoints列表中删除并不再接受客户端请求。
(2)执行PreStop
(3)Kubernetes向pod中的容器发送SIGTERM信号(正常终止信号)终止pod里面的主进程,这个信号让容器知道自己很快将会被关闭
(4)terminationGracePeriodSeconds: 60 #可选终止等待期,如果有设置删除宽限时间,则等待宽限时间到期,如果没有配置则最多等待30s,
(5)Kubernetes等待指定的时间称为优雅终止宽限期,默认情况下是30秒,值得注意的是等待期与preStop Hook和SIGTERM信号并行执行,如果没有配置宽限时间,Kubernetes可能不会等待preStop Hook完成就终止pod(最长30秒之后主进程还没有结束就强制终止pod)。
(6)SIGKILL信号被发送到Pod,并删除Pod

流程如下图所示:

举例:(poststart探针和prestop探针)

vim 1-myserver-myapp1-postStart-preStop.yaml
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  #指定宽限时间为60s,容器关闭前会等待60s,等待容器内运行任务完成后再关闭容器
      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"]     #使用tomcat模拟postStart,为tomcat生成web页面
          preStop:     #指定在容器关闭之前做的操作
            exec:
             #command: 把自己从注册中心移除
              command: ["/usr/local/tomcat/bin/catalina.sh","stop"]    #使用tomcat模拟preStop
        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

创建并查看pod
kubectl apply -f 1-myserver-myapp1-postStart-preStop.yaml
kubectl get pod -n myserver -o wide

访问web页面,可以看到postStart操作准备的web页面

标签: k8s
最后更新:2023年6月15日

袁党生

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2023 linux学习. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

豫ICP备18039507号-1