一、探针

1.1 探针类型

kubernetes 提供了三种探针(支持exec、tcp和http方式)来探测容器的状态:

LivenessProbe:

容器存活性检查,用于判断容器是否健康,告诉 kubelet 一个容器什么时候处于不健康 的状态。如果 LivenessProbe 探针探测到容器不健康,则 kubelet 将删除该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含 LivenessProbe 探针,那么 kubelet 认为该容器的 LivenessProbe 探针返回的值永远是 Success;

ReadinessProbe:

容器就绪性检查,用于判断容器是否启动完成且准备接收请求。如果ReadinessProbe探针探测到失败,Endpoint Controller 将从 Service 的 Endpoint 中删除包含该容器所在 Pod 的 IP 地址的Endpoint条目。如果容器不提供就绪态探针,则默认状态为 Success。

startupProbe:

容器启动检查,探测容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器按照重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。

探针的配置非常灵活,用户可以指定探针的探测频率、探测成功阈值、探测失败阈值 等。各参数的含义和配置方法可参考文档 Configure Liveness and Readiness Probes

1.1.1 exec命令型

    livenessProbe:
      exec:
        command:
        - curl
        - 127.0.0.1:8000
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功一次表示正常
      timeoutSeconds: 3 # 执行检查指令的超时时间
    readinessProbe:
      exec:             #退出0 表示成功
        command:
          - curl
          - 127.0.0.1:8000
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功的阈值
      timeoutSeconds: 3 #单次执行超时的时间

1.1.2 http型(七层)

    livenessProbe:
      httpGet:
        path: /
        port: 8080
        scheme: HTTP # 返回200表示正常
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功一次表示正常
      timeoutSeconds: 3 # 执行检查指令的超时时间
    readinessProbe:
      httpGet:
        path: /
        port: 8000
        scheme: HTTP
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功的阈值
      timeoutSeconds: 3 #单次执行超时的时间

1.1.3 tcp型(四层)

    readinessProbe:
      tcpSocket:
        port: 8000
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功的阈值
      timeoutSeconds: 3 # 单次执行超时的时间
    livenessProbe:
      tcpSocket:
        port: 8000
      initialDelaySeconds: 5 #第一次探测时等待5s
      periodSeconds: 5 #每5s执行一次
      failureThreshold: 2 #失败的阈值
      successThreshold: 1 #成功的阈值
      timeoutSeconds: 3 # 单次执行超时的时间

1.1.4 startup案例

livenessProbe:
  httpGet:
    path: /test
    port: 80
  failureThreshold: 1 #失败的阈值
  initialDelaySeconds: 10 #第一次探测时等待10s
  periodSeconds: 10 #每10s执行一次

startupProbe:
  httpGet:
    path: /test
    port: 80
  failureThreshold: 10 #失败的阈值
  initialDelaySeconds: 10 #第一次探测时等待10s
  periodSeconds: 10 #每10s执行一次

注意点:上面的配置是只有startupProbe探测成功后再交给livenessProbe。

我们startupProbe配置的是10*10s,也就是说只要应用在100s内启动都是OK的,而且应用挂掉了10s就会发现问题。

示例

# 创建ns
[root@master01 1]# k create ns study

# 定义yaml文件
[root@master01 1]# vim tanzhen.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: study
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/abroad_images/python:latest
        imagePullPolicy: IfNotPresent
        name: web
        ports:
        - containerPort: 8000
        args:
        - "python"
        - "-m"
        - "http.server"
        livenessProbe:
          httpGet:
            path: /
            port: 8000
            scheme: HTTP
          initialDelaySeconds: 5 #第一次探测时等待5s
          periodSeconds: 5 #每5s执行一次
          failureThreshold: 2 #失败的阈值
          successThreshold: 1 #成功的阈值
          timeoutSeconds: 3 #单次执行超时的时间
        readinessProbe:
          httpGet:
            path: /
            port: 8000
            scheme: HTTP
          initialDelaySeconds: 5 #第一次探测时等待5s
          failureThreshold: 2 #失败的阈值
          successThreshold: 1 #成功的阈值
          timeoutSeconds: 3 #单次执行超时的时间

# 应用
[root@master01 1]# kaf tanzhen.yaml

# 环境复原
[root@master01 1]# k delete -f  tanzhen.yaml

1.2 minReadySeconds

默认情况下,一旦新创建的 pod 变成就绪状态 k8s 就会认为该 pod 是可用的,从而将老的pod 删除掉。但有时问题可能会在新 pod 真正处理用户请求时才暴露,因此一个更稳健的做法是当某个新 pod 就绪后对其观察一段时间再删掉老的 pod。

# 创建ns
[root@master01 1]# k create ns study

# 定义yaml文件
[root@master01 1]# vim minreadyseconds.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
  namespace: study
spec:
  minReadySeconds: 10 #指定了Pod进入就绪状态所需的时间
  strategy:
    rollingUpdate:
      maxUnavailable: 1 #指定允许不可用的最大Pod数为1
      maxSurge: 1 #指定允许超出副本数的最大Pod数为1
  replicas: 2
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: busybox
          image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox
          command: ["/bin/sh", "-c", "sleep 3600"]

# 应用
[root@master01 1]# kaf minreadyseconds.yaml

# 查看
[root@master01 1]#kgp -n study
NAME                      READY   STATUS             RESTARTS   AGE
busybox-f84877545-cjw4c   1/1     Running            0          2m3s
busybox-f84877545-xtlxd   1/1     Running            0          2m3s

# 环境复原
[root@master01 1]# k delete -f  minreadyseconds.yaml

1.3 terminationGracePeriodSeconds

定义优雅关闭的宽限期,即在收到停止请求后,有多少时间来进行资源释放或者做其它操作,如果到了最大时间还没有停止,会被强制结束。(默认 30 秒)

验证:

# 创建ns
[root@master01 1]# k create ns study

# 定义yaml文件
[root@master01 1]# vim terminationgraceperiodseconds.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
  namespace: study
spec:
  minReadySeconds: 10 #指定了Pod进入就绪状态所需的时间
  strategy:
    rollingUpdate:
      maxUnavailable: 1 #指定允许不可用的最大Pod数为1
      maxSurge: 1 #指定允许超出副本数的最大Pod数为1
  replicas: 2
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: busybox
          image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox
          command: ["/bin/sh", "-c", "sleep 3600"]

# 应用
[root@master01 1]# kaf terminationgraceperiodseconds.yaml

# 查看
[root@master01 1]#kgp -n study
NAME                      READY   STATUS             RESTARTS   AGE
busybox-f84877545-cjw4c   1/1     Running            0          2m3s
busybox-f84877545-xtlxd   1/1     Running            0          2m3s

# 环境复原
[root@master01 1]# k delete -f  terminationgraceperiodseconds.yaml

二、Pod的生命周期

Day01-kubernetes基础架构解读-图7

2.1 Pod终止过程

终止过程主要分为如下几个步骤:

  1. 用户发出删除 pod 命令;
  2. K8S 会给旧POD发送SIGTERM信号;将 pod 标记为“Terminating”状态;pod 被视为“dead”状态,此时将不会有新的请求到达旧的pod;
  3. 等待宽限期(terminationGracePeriodSeconds 参数定义,默认情况下30秒)时间;
  4. 第三步同时运行,监控到 pod 对象为“Terminating”状态的同时启动 pod 关闭过程;
  5. 第三步同时进行,endpoints 控制器监控到 pod 对象关闭,将pod与service匹配的 endpoints 列表中删除;
  6. 如果 pod 中定义了 preStop 处理程序,则 pod 被标记为“Terminating”状态时以同步的方式启动执行;若宽限期结束后,preStop 仍未执行结束,第二步会重新执行 并额外获得一个2秒的小宽限期(最后的宽限期,所以定义prestop 注意时间,和 terminationGracePeriodSeconds 参数配合使用),
  7. Pod 内对象的容器收到 TERM 信号;
  8. 宽限期结束之后,若存在任何一个运行的进程,pod 会收到 SIGKILL 信号;
  9. Kubelet 请求 API Server 将此 Pod 资源宽限期设置为0从而完成删除操作;

2.2 钩子函数(postStart和preStop)

钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码,k8s在主容器的 启动之后停止之前 提供了两个钩子函数。

  • post start :容器创建之后执行,如果失败了会重启容器。

  • pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。

钩子处理器支持使用下面三种方式定义动作:

  • exec命令(自定义):在容器内执行一次命令
...
...
lifecycle:
  postStart:
    exec:
      command:
      - cat
      - /tmp/healthy
...
...
  • tcpSocket :在当前容器尝试访问指定的socket,监听端口,属于四层
...
...
lifecycle:
  postStart:
    tcpSocket:
      port: 8080
...
...
  • httpGet :在当前容器中向某url发起http请求,监听接口,属于七层
...
...
lifecycle:
  postStart:
    httpGet:
      path: /api/health
      port: 8080
      host:
      scheme: HTTP #支持的协议,http或者https
...
...

2.3 示例验证

# 创建ns
[root@master01 1]# k create ns study

# 创建deploy模板
[root@master01 1]# k create deploy nginx-blue-deployment --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox -r=1 --dry-run=client -oyaml > alloftest.yaml

# 定义yaml文件
[root@master01 1]# vim alloftest.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-hook-exec
  namespace: study
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pod-hook-exec
  template:
    metadata:
      labels:
        app: pod-hook-exec
    spec:
      terminationGracePeriodSeconds: 8 # 设置8秒宽限时间默认是30s
      nodeName: node02 # 为了测试方便指定调度机器
      initContainers:
      - name: init-containers
        image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox
        command: ["sh","-c","echo init-containers...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
        volumeMounts:
        - name: logs
          mountPath: /tmp/
      containers:
      - name: main-container
        image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox
        command: ["sh","-c","echo main-container...|tee -a  /tmp/pod-hook-exec.log;sleep 3600s"]
        volumeMounts:
        - name: logs
          mountPath: /tmp/
        startupProbe:
          exec:
            command: ["sh","-c","echo startupProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        livenessProbe:
          exec:
            command: ["sh","-c","echo livenessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        readinessProbe:
          exec:
            command: ["sh","-c","echo readinessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          timeoutSeconds: 10
        lifecycle:
          postStart:
            exec: #在容器启动的时候执行一个命令
              command: ["sh","-c","echo postStart...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
          preStop: # 在pod停止之前执行这个命令
            exec:
              command: ["sh","-c","echo preStop...|tee -a  /tmp/pod-hook-exec.log"]
      volumes:
      - name: logs #和上面保持一致 这是本地的文件路径上面是容器内部的路径
        hostPath:
          path: /tmp/logs/

# 应用
[root@master01 1]# kaf alloftest.yaml

# 验证在node02节点上查看/tmp/logs/pod-hook-exec.log日志文件
[root@node02 ~]# tail -f /tmp/logs/pod-hook-exec.log
init-containers...
main-container...
postStart...
startupProbe...
readinessProbe...
livenessProbe...
readinessProbe...
readinessProbe...
livenessProbe...
readinessProbe...

...
...

# 环境复原
[root@master01 1]# k delete -f  alloftest.yaml

从上图的日志就可看出,被分为6个执行阶段,执行的先后顺序: initContainers -->【main-container、postStart】--> startupProbe--> readinessProbe --> livenessProbe --> preStop

main-container 和 postStart 是同时执行,虽然 readinessProbe 和 livenessProbe 也是同时执行,但是他们不是真正的并行执行,也有先后顺序的。