Kubernetes 中 Pod 的生命周期管理


探究 Pod 的启动关闭流程以及快速 DEBUG

Kubernetes 中 Pod 的生命周期管理

探究 Pod 的启动关闭流程以及快速 DEBUG

  • Kubernetes 知识图谱
  • 云原生导航
  • 谷歌搜索
  • Raft 协议动画演示
  • 搬瓦工

    本文我们将从实践者的角度仔细研究整个pod生命周期,包括如何影响启动和关闭行为,并通过实践来理解对应用程序健康状况的检查。

    1. Pod 的生命周期


    Pod phase

    Pod 的 status 在信息保存在 PodStatus 中定义,其中有一个 phase 字段。

    Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇总,也不是为了做为综合状态机。

    Pod 相位的数量和含义是严格指定的。除了本文档中列举的状态外,不应该再假定 Pod 有其他的 phase 值。

    无论你是手动创建 Pod,还是通过 deploymentdaemonsetstatefulset来创建,Pod 的 phase 都有以下几个可能的值:

    • 挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。
    • 运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
    • 成功(Successed):Pod 中的所有容器都被成功终止,并且不会再重启。
    • 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
    • 未知(Unkonwn):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

    下图是 Pod 的生命周期示意图,从图中可以看到 Pod 状态的变化。

    https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/fcachl.jpg
    图片 - Pod的生命周期示意图

    Pod 状态

    Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduledReadyInitializedUnschedulable。status 字段是一个字符串,可能的值有 TrueFalseUnknown

    当你通过 kubectl get pod 查看 Pod 时,STATUS 这一列可能会显示与上述5个状态不同的值,例如 Init:0/1CrashLoopBackOff。这是因为 Pod 状态的定义除了包含 phase 之外,还有 InitContainerStatusescontainerStatuses 等其他字段,具体代码参考 overall status of a pod .

    如果想知道究竟发生了什么,可以通过命令 kubectl describe pod/$PODNAME 查看输出信息的 Events 条目。通过 Events 条目可以看到一些具体的信息,比如正在拉取容器镜像,Pod 已经被调度,或者某个 container 处于 unhealthy 状态。

    2. Pod 的启动关闭流程


    下面通过一个具体的示例来探究一下 Pod 的整个生命周期流程。为了确定事情发生的顺序,通过下面的 manifest 来部署一个 deployment。

    kind:                   Deployment
    apiVersion:             apps/v1beta1
    metadata:
      name:                 loap
    spec:
      replicas:             1
      template:
        metadata:
          labels:
            app:            loap
        spec:
          initContainers:
          - name:           init
            image:          busybox
            command:       ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing']
            volumeMounts:
            - mountPath:    /loap
              name:         timing
          containers:
          - name:           main
            image:          busybox
            command:       ['sh', '-c', 'echo $(date +%s): START >> /loap/timing;
    sleep 10; echo $(date +%s): END >> /loap/timing;']
            volumeMounts:
            - mountPath:    /loap
              name:         timing
            livenessProbe:
              exec:
                command:   ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing']
            readinessProbe:
              exec:
                command:   ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing']
            lifecycle:
              postStart:
                exec:
                  command:   ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing']
              preStop:
                exec:
                  command:  ['sh', '-c', 'echo $(date +%s): PRE-HOOK >> /loap/timing']
          volumes:
          - name:           timing
            hostPath:
              path:         /tmp/loap
    

    等待 Pod 状态变为 Running 之后,通过以下命令来强制停止 Pod:

    $ kubectl scale deployment loap --replicas=0
    

    查看 /tmp/loap/timing 文件的内容:

    $ cat /tmp/loap/timing
    
    1525334577: INIT
    1525334581: START
    1525334581: POST-START
    1525334584: READINESS
    1525334584: LIVENESS
    1525334588: PRE-HOOK
    1525334589: END
    

    /tmp/loap/timing 文件的内容很好地体现了 Pod 的启动和关闭流程,具体过程如下:

    https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/AOQgQj.jpg
    图片 - Pod 的启动和关闭流程

    1. 首先启动一个 Infra 容器(又叫 Pause 容器),用来和 Pod 中的其他容器共享 linux 命名空间,并开启 init 进程。(上图中忽略了这一步)
    2. 然后启动 Init 容器,它是一种专用的容器,在应用程序容器启动之前运行,用来对 Pod 进行一些初始化操作,并包括一些应用镜像中不存在的实用工具和安装脚本。
    3. 4 秒之后,应用程序容器和 post-start hook 同时启动。
    4. 7 秒之后开始启动 liveness 和 readiness 探针
    5. 11 秒之后,通过手动杀掉 Pod,pre-stop hook 执行,优雅删除期限过期后(默认是 30 秒),应用程序容器停止。实际的 Pod 终止过程要更复杂,具体参考 Pod 的终止

    注意

    必须主动杀掉 Pod 才会触发 pre-stop hook,如果是 Pod 自己 Down 掉,则不会执行 pre-stop hook

    3. 如何快速 DEBUG


    当 Pod 出现致命的错误时,如果能够快速 DEBUG,将会帮助我们快速定位问题。为了实现这个目的,可以把把致命事件的信息通过 .spec.terminationMessagePath 配置写入指定位置的文件,就像打印错误、异常和堆栈信息一样。该位置的内容可以很方便的通过 dashboards、监控软件等工具检索和展示,默认路径为 /dev/termination-log

    以下是一个小例子:

    # termination-demo.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: termination-demo
    spec:
      containers:
      - name: termination-demo-container
        image: alpine
        command: ["/bin/sh"]
        args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
    

    这些消息的最后部分会使用其他的规定来单独存储:

    $ kubectl create -f termination-demo.yaml
    
    $ sleep 20
    
    $ kubectl get pod termination-demo -o go-template='{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}'
    
    Sleep expired
    
    $ kubectl get pod termination-demo -o go-template='{{range .status.containerStatuses}}{{.lastState.terminated.exitCode}}{{end}}'
    
    0
    

    4. 参考



    -------他日江湖相逢 再当杯酒言欢-------

    「真诚赞赏,手留余香」

    米开朗基杨

    真诚赞赏,手留余香

    使用微信扫描二维码完成支付


    相关推荐