深入解析Kubernetes Pod生命周期(含示例代码)

文摘   2024-11-29 08:03   辽宁  

 

关注下方公众号,获取更多热点资讯

深入解析Kubernetes Pod生命周期(含示例代码)

Kubernetes 是一个强大的容器编排平台,其核心功能之一是管理 Pod 生命周期。本文将详细介绍 Pod 的生命周期管理机制,包括 initContainer、探针、钩子以及调度流程,帮助您深入理解 Kubernetes 的运行原理。

提示:文章较长建议收藏后仔细阅读。

Pod 生命周期

关键概念

  • initC:全称 init Container,用于在主容器启动之前运行一次或多次,常用于准备工作。

  • MainC:全称 Main Container,主要执行应用程序逻辑的容器。

  • postStart:启动后钩子,在容器启动后立即执行的操作,常用于初始化任务。

  • preStop:停止前钩子,在容器停止前执行的操作,常用于清理任务。

  • 容器探测(探针):

    • 启动探测:用于在容器启动时执行一次的探测,确保容器启动成功。

    • 就绪探测:用于在容器启动后定期执行的探测,确保容器处于就绪状态。

    • 存活探测:用于在容器运行期间定期执行的探测,确保容器持续处于健康状态。

initC介绍

Init 容器的概念

  • • 定义:Init 容器是一种特殊的容器,用于在 Pod 的应用容器启动之前运行。它们可以包含一些初始化任务或脚本,这些任务或脚本不需要在应用容器中存在。

  • • 特性:

    • • Init 容器总是运行到成功完成为止。

    • • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成。

    • • Init 容器与应用容器可以使用不同的镜像,因此可以在 Init 容器中放置一些危险的工具进行使用。

    • • Init 容器之间是线性启动的,可以做一些延迟性的操作。

    • • 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动。

Init 容器的使用场景

  • • 初始化任务:可以在 Init 容器中运行一些初始化任务,如配置文件的生成、依赖项的安装等。

  • • 延迟启动:由于 Init 容器之间是线性启动的,可以利用这一特性进行一些需要延迟的操作,如等待某个服务启动完成等。

Init 容器与应用容器的区别

特性Init 容器应用容器
运行时机主容器启动之前Pod 启动后
探针支持不支持 readinessProbe支持
失败处理Pod 重启视重启策略而定

使用样例

这里我们用到busybox容器,busybox 是一个非常小巧的 Linux 发行版,包含了一组常用的 Unix 工具。它常用于 Docker 镜像中,因为其体积小、启动快,适合用于测试、调试和简单的任务执行。

成功的 init 容器示例

配置文件

apiVersion: v1
kind: Pod
metadata:
  name: init-container-success-example
spec:
  containers:
  - name: main-app
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh''-c''echo The main app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh''-c''echo Init container is running! && sleep 5']

这个示例展示了一个成功的 init 容器配置。init-myservice 容器运行一个简单的命令,打印 "Init container is running!" 并休眠 5 秒钟。由于这个命令能够成功完成,init 容器也会成功完成。之后,主应用容器 main-app 将启动并运行。

失败的 init 容器示例

配置文件

apiVersion: v1
kind: Pod
metadata:
  name: init-container-failure-example
spec:
  containers:
  - name: main-app
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh''-c''echo The main app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh''-c''exit 1']

这个示例展示了一个失败的 init 容器配置。init-myservice 容器运行一个简单的命令 exit 1,这会导致容器立即退出并返回非零状态码。由于 init 容器必须成功完成才能继续启动主应用容器,因此 Kubernetes 会不断地重启这个 Pod,直到 init 容器成功完成。然而,由于 exit 1 命令始终会失败,这个 Pod 会一直处于重启状态。

如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动。

示例启动方法

  • • 将配置文件内容保存至init-container-failure-example.yaml

  • • 启动运行kubectl apply -f init-container-failure-example.yaml

  • • 查看运行状态kubectl get pods,再次步骤会发现失败的 initC 示例一直再重启。

  • • 删除pod:kubectl delete pod init-container-success-example

通过这两个示例,可以看到 init 容器在 Kubernetes 中的工作方式,以及如何处理成功和失败的情况。如果需要更多详细信息或进一步的解释,请告诉我。

容器探测(探针)

探针类型介绍

  • • startupProbe: 启动探针:开始检测吗?用于在容器启动过程中执行一次的探测,确保容器能够成功启动。如果启动探针失败,那么容器会被始终视为未就绪。

  • • livenessProbe:存活探针:还活着吗?用于检测容器是否仍然在运行。如果存活探针失败,kubelet 会杀死容器,并根据其重启策略决定是否重启容器。

  • • readinessProbe:就绪探针:准备提供服务了吗?用于检测容器是否已经准备好开始运行。如果就绪探针失败,容器将从服务负载均衡器中移除。

探针是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler。有三种类型的处理程序:

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

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

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

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

  • • 成功:容器通过了诊断。

  • • 失败:容器未通过诊断。

  • • 未知:诊断失败,因此不会采取任何行动。

就绪探针(readinessProbe)

介绍:k8s 通过添加就绪探针,解决尤其是在扩容时保证提供给用户的服务都是可用的。选项说明:

  • • initialDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认是 0 秒,最小值是 0。

  • • periodSeconds:执行探测的时间间隔(单位是秒),默认为 10s,单位“秒”,最小值是 1。

  • • timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1s,单位“秒”,最小值是 1。

  • • successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为 1。必须为 1 才能激活和启动。最小值为 1。

  • • failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为 3,最小值是 1。

成功示例

apiVersion: v1
kind: Pod
metadata:
  name: readiness-probe-success
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    readinessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3

在这个示例中,就绪探针检查 TCP 端口 80 的连接,等待 5 秒后开始探测,每 5 秒探测一次,如果 3 次失败则视为未就绪。假设端口 80 正常开放,探针将会成功,容器被认为是就绪的,可以接受流量。

失败示例

apiVersion: v1
kind: Pod
metadata:
  name: readiness-probe-failure
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    readinessProbe:
      tcpSocket:
        port: 9999
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3

在这个示例中,就绪探针检查 TCP 端口 9999 的连接,等待 5 秒后开始探测,每 5 秒探测一次,如果 3 次失败则视为未就绪。由于端口 9999 并未开放,探针将会失败,容器将不会接收流量。

存活探针(livenessProbe)

介绍:k8s 通过添加存活探针,解决虽然活着但是已经死了的问题。选项说明:

  • • initialDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认是 0 秒,最小值是 0。

  • • periodSeconds:执行探测的时间间隔(单位是秒),默认为 10s,单位“秒”,最小值是 1。

  • • timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1s,单位“秒”,最小值是 1。

  • • successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为 1。必须为 1 才能激活和启动。最小值为 1。

  • • failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为 3,最小值是 1。

成功示例

apiVersion: v1
kind: Pod
metadata:
  name: liveness-probe-success
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      failureThreshold: 3

在这个示例中,存活探针执行 cat /tmp/healthy 命令,等待 5 秒后开始探测,每 5 秒探测一次,如果 3 次失败则视为存活失败。假设 /tmp/healthy 文件存在且内容正确,探针将会成功,容器继续运行。

失败示例

apiVersion: v1
kind: Pod
metadata:
  name: liveness-probe-failure
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/non-existent-file
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      failureThreshold: 3

在这个示例中,存活探针执行 cat /tmp/non-existent-file 命令,等待 5 秒后开始探测,每 5 秒探测一次,如果 3 次失败则视为存活失败。由于文件不存在,探针将会失败,kubelet 会杀死容器并根据其重启策略决定是否重启容器。

启动探针(startupProbe)

介绍:k8s 在 1.16 版本后增加 startupProbe 探针,主要解决在复杂的程序中 readinessProbe、livenessProbe 探针无法更好的判断程序是否启动、是否存活。选项说明:

  • • initialDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认是 0 秒,最小值是 0。

  • • periodSeconds:执行探测的时间间隔(单位是秒),默认为 10s,单位“秒”,最小值是 1。

  • • timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1s,单位“秒”,最小值是 1。

  • • successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为 1。必须为 1 才能激活和启动。最小值为 1。

  • • failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为 3,最小值是 1。

成功示例

apiVersion: v1
kind: Pod
metadata:
  name: startup-probe-success
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    startupProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      failureThreshold: 10

在这个示例中,启动探针检查 / 路径上的 HTTP GET 请求,端口为 80,等待 5 秒后开始探测,每 5 秒探测一次,如果 10 次失败则视为启动失败。如果探针成功,容器将继续运行。

失败示例

apiVersion: v1
kind: Pod
metadata:
  name: startup-probe-failure
spec:
  containers:
  - name: nginx
    image: nginx:1.21.6
    startupProbe:
      httpGet:
        path: /invalid-path
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      failureThreshold: 3

在这个示例中,启动探针检查 /invalid-path 路径上的 HTTP GET 请求,端口为 80,等待 5 秒后开始探测,每 5 秒探测一次,如果 3 次失败则视为启动失败。由于路径错误,探针将会失败,导致容器被认为启动失败,kubelet 会不断重启容器直到探针成功,如果超过failureThreshold设置的次数将失败。

Pod Hook(钩子)

Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook。

Hook 的类型包括两种:

  • • postStart:在容器启动后立即执行,可用于初始化任务。

  • • preStop:在容器停止前执行,可用于执行清理操作。

执行的方法分为:

  • • exec:执行一段命令。

  • • HTTP:发送 HTTP 请求。

在 Kubernetes 中,理想的状态是 Pod 优雅释放,但是并不是每一个 Pod 都会这么顺利:

  • • Pod 卡死,处理不了优雅退出的命令或者操作。

  • • 优雅退出的逻辑有 BUG,陷入死循环。

  • • 代码问题,导致执行的命令没有效果。

Kubernetes 的 Pod 终止流程中有一个称为**优雅终止时间(grace period)**的设定,定义在 pod.spec.terminationGracePeriodSeconds 字段,默认值为 30 秒。当我们执行 kubectl delete 命令时,可以通过 --grace-period 参数显式指定这个优雅终止时间,以覆盖 Pod 的默认配置。

如果在设定的优雅终止时间内 Pod 仍未成功终止,Kubernetes 将被迫强制杀死(kill)该 Pod。需要注意的是,preStop Hook 和 SIGTERM 信号的触发与优雅终止时间是并行发生的,Kubernetes 不会等待 preStop Hook 的完成。如果你的应用程序在 terminationGracePeriod 结束之前成功关闭并退出,Kubernetes 将立即进入下一步操作。

PostStart 和 PreStop 钩子

  • • PostStart:在容器启动后立即执行的操作,常用于初始化任务。

    • • 示例:

      apiVersion: v1
      kind: Pod
      metadata:
        name: poststart-example
      spec:
        containers:
        - name: my-container
          image: my-image
          lifecycle:
            postStart:
              exec:
                command: ["/bin/sh""-c""echo Hello from the postStart handler"]
  • • PreStop:在容器停止前执行的操作,常用于清理任务。

    • • 示例:

      apiVersion: v1
      kind: Pod
      metadata:
        name: prestop-example
      spec:
        containers:
        - name: my-container
          image: my-image
          lifecycle:
            preStransform: translateY(
              exec:
                command: ["/bin/sh""-c""echo Goodbye from the preStop handler"]

PreStop 钩子的作用是在容器终止前执行一些清理任务。它在以下情况下生效:

  1. 1. 正常终止容器:当使用 kubectl delete pod 命令删除 Pod 时,Kubernetes 会发送 SIGTERM 信号给容器进程,触发 PreStop 钩子。

  2. 2. 更新或缩减 Pod:在 Kubernetes 进行滚动更新或缩减副本数量时,也会触发 PreStop 钩子。

  3. 3. 容器重启策略:如果容器的重启策略是 Always 或 OnFailure,在容器被重启前也会执行 PreStop 钩子。

但是,如果你使用 kill -9 (SIGKILL) 信号来直接终止容器进程,PreStop 钩子将不会被执行,因为 SIGKILL 信号是不可捕获和无法忽略的。

pod的调度运行流程

pod 的生命周期我们知道了,这里再简单补充一下 pod 的调度流程

K8S Master 组件

  • • ApiServer: 接收用户请求并存储到 ETCD。

  • • ETCD: 保存整个集群的状态。

  • • Controller Manager: 监视 ETCD 并确保 Pod 的期望状态。

  • • Scheduler: 为未分配的 Pod 选择合适的节点。

K8S Node 组件

  • • Kubelet: 在节点上创建并管理容器。

  • • Container Runtime: 实际创建和运行容器。

  • • Kube-Proxy: 管理网络规则,确保服务间通信。

流程总结

  • • 用户通过 ApiServer 提交 Pod 创建请求。

  • • ApiServer 验证请求并存储到 ETCD。

  • • Controller 监视 ETCD,确保 Pod 的期望状态。

  • • Scheduler 选择合适的节点来运行 Pod。

  • • Kubelet 在选定的节点上创建并管理容器。

总结

在 Pod 生命周期中,initContainers、startupProbe、livenessProbe、readinessProbe 和 Lifecycle hooks 都可以使用,你可以根据需求选择全部、部分或完全不使用这些机制。


欢迎关注我的公众号“编程与架构”,原创技术文章第一时间推送。


编程与架构
专注于Java、大数据、AI以及开发运维技术的深入探索与分享。作为一名开源爱好者,致力于分享实战经验和前沿技术动态,帮助更多技术人提升技能。
 最新文章