k8s☞05容器生命周期-回调和探针

阅读量: zyh 2020-09-04 15:19:04
Categories: > Tags:

容器生命周期-回调

💥这里说的是容器,容器,容器,不是Pod,Pod,Pod

💥这里说的是容器,容器,容器,不是Pod,Pod,Pod

💥这里说的是容器,容器,容器,不是Pod,Pod,Pod

容器状态包含3个:

我们在实际工作中,可能会遇到需要在容器生命周期中按预定计划执行某个动作:

k8s 给我们提供了两个回调用于处理这些工作,分别是 postStart和 preStop.

https://kubernetes.io/zh/docs/concepts/containers/container-lifecycle-hooks/

执行方式

回调函数的执行方式有三种:

$ kubectl explain pod.spec.containers.lifecycle.postStart
FIELDS:
   exec <Object>
     One and only one of the following should be specified. Exec specifies the
     action to take.

   httpGet      <Object>
     HTTPGet specifies the http request to perform.

   tcpSocket    <Object>
     TCPSocket specifies an action involving a TCP port. TCP hooks not yet
     supported

👙通常主要使用的就是 exec 和 httpGet

postStart

  1. k8s在在容器创建后立即【发送】postStart,但【不保证】在 ENTRYPOINT 之前运行
  2. 如果它卡住,【容器】无法达到 running 状态。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: poststart-test
spec:
  containers:
  - name: poststart-test
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo This is postStart-test > /usr/share/nginx/html/index.html"]
[root@k8s00 ~]# kubectl apply -f poststart-test.yaml
pod/poststart-test created
[root@k8s00 ~]# kubectl get pod -o wide
NAME             READY   STATUS              RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
poststart-test   0/1     ContainerCreating   0          7s    <none>   k8s02   <none>           <none>
[root@k8s00 ~]# kubectl get pod -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP          NODE    NOMINATED NODE   READINESS GATES
poststart-test   1/1     Running   0          10s   10.97.2.6   k8s02   <none>           <none2
[root@k8s00 ~]# curl 10.97.2.6
This is postStart-test

preStop

  1. k8s它将在【容器】结束前立即【发送】preStop,但执行时间受限于【Pod】终止宽限期。
  2. 应该设置足够长的终止宽限期terminationGracePeriodSeconds,这个期限应该大于preStop执行时间+kubelet通知【container runtime】发关闭信号给【容器】+【容器】关闭时间。
  3. 它执行期间,Pod依然处于svc的端点列表内。

【容器】的结束需要被k8s知悉,任何k8s不知悉的结束动作,不会触发preStop。例如容器因执行完自动结束,这种状态k8s无法得知。

示例1:

终止前写入数据

apiVersion: v1
kind: Pod
metadata:
  name: prestop-test
spec:
  containers:
  - name: prestop-test
    image: nginx
    volumeMounts:
    - name: prestop-test-tmp
      mountPath: /usr/share
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo This is preStop > /usr/share/message"]
  volumes:
  - name: prestop-test-tmp
    hostPath:
      path: /tmp
[root@k8s00 ~]# kubectl delete pod prestop-test
# 这里 prestop-test 被调度到 k8s02
[root@k8s02 ~]# cat /tmp/message
This is preStop

示例2:

终止前优雅关闭nginx

apiVersion: v1
kind: Pod
metadata:
  name: hook-demo2
spec:
  containers:
  - name: hook-demo2
    image: nginx
    lifecycle:
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]  # 优雅退出

💥分歧💥

说明: Kubernetes 只有在 Pod 手动结束或因控制器调度的时候才会发送 preStop 事件, 但在 Pod(Completed)时 preStop 的事件处理逻辑不会被触发。这个限制在 issue #55087 中被追踪。

因此,💥Job类不应该使用preStop来执行结束逻辑💥,而是应该将逻辑放在容器内执行。k8s无法提前获悉容器退出信号,因此无法在容器退出前发送preStop。

容器生命周期-探针

https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes

https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#lifecycle-1

k8s通过探测器判断何时重启容器。

探测器种类:启动探测器startupProbe、存活探测器livenessProbe,就绪探测器readinessProbe。

每种探测器探测方式:命令方式,http方式,tcp方式。

例如命令方式:

    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

优先级:启动探测器>存活探测器>就绪探测器

👙默认行为:任何没有显式指定的探针,自动设为success

kubelet 使用启动探测器可以知道应用程序容器什么时候启动了。 如果配置了这类探测器,就可以控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

若启动探针存在,则其它两类探针将延后运行。行为上,探针失败则重启【容器】.。

若存活探针存在,行为上,探针失败重启【容器】。

若就绪探针存在,行为上,探针成功Pod加入svc端点列表。默认为failure,因此,成功之前svc会将Pod从端点列表中移除。

首先,就绪探针比较符合常见需求,即探针失败,则不会提供服务。但它不会重启容器,因此无法自动解决问题。

其次,需要存活探针来判断程序的基本条件从而确保容器重启。

最后,启动探针可以判断环境,环境不满足直接重启。

三种探测器,均在 pod.spec.containers 下配置

➜  ~ kubectl explain pod.spec.containers | grep Probe
   livenessProbe        <Object>
   readinessProbe       <Object>
   startupProbe <Object>

探测器的探测周期配置

启动探测器 startupProbe

💥目的在于设置一个容器启动宽容期,适合慢速启动程序的初期检测。只要启动探测器在超时时间以内成功一次,后续的健康状态将交给存活探测器。

💥如果启动探测器在超时时间内没有成功,则容器直接被杀死,而pod按照 restartPolicy 策略执行。

例如:

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30  # 错误次数阈值
  periodSeconds: 10  # 检测间隔周期
  # 启动超时时间=failureThreshold*periodSeconds

上述例子意思是:kubelet 检测总时间超过 300 秒后,如果还未检测成功,认为启动失败。

存活探测器 livenessProbe

💥目的在于确认容器是否健康,但在不健康的时候重启容器。它将在容器整个生命周期中运行。

💥它与就绪探测器最大区别就是,kubelet检测失败后会重启容器。

例子如下:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

探测器会认为 200<=x<400 的状态码为正常。

就绪探测器 readinessProbe

👙生产应用应该有的配置。

💥目的在于仅确认容器是否健康,但是并不重启容器;它将在容器整个生命周期中运行。

💥容器刚启动的时候,就绪探针会告知kubelet是否把当前pod的endpoint地址加入svc 。

💥容器运行中如果就绪探针失败,就绪探针负责告知kubelet将pod endpoint从svc中移除

适用于下列场景:

  1. 容器启动后,加载数据多,加载完之前无法提供服务

例如:

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10

上述例子意思是:kubelet 等待 initialDelaySeconds 后开始第一次检测,每次检测间隔10秒,检测成功,认为可以提供服务

最佳实践

  1. 存活和就绪探针的健康状态接口应该是两个互相独立的接口

  2. 存活探针的健康状态接口应该是直接返回http code,不应该有任何逻辑

  3. 就绪探针的健康状态接口应该提供就绪所需要的逻辑。但是不应该添加重新构建就绪的逻辑。

    例如:处理http请求的程序需要一个数据库连接的就绪状态,那么就应该在就绪探针的健康状态接口里添加数据库连接的检查逻辑。

  4. 存活探针可以用来判定程序的最简单功能是否正常,如果最简单都不正常,则无需让程序启动。

  5. 就绪探针可以用来判定程序复杂一些的逻辑是否正常。