k8s

pod完整生命周期

背景

下面我将以这个思维导图来总结一下如何对pod生命周期进行管理。

pod完整生命周期

  1. 用户发起创建pod请求到apiserver,apiserver存储pod信息到etcd中。pod还没有经过调度,pod的nodeName是空的,此时pod处于Pending状态。
  2. scheduler监听到 pending状态的pod,经过调度策略(资源请求,tolerations,亲和度等)选择一个节点,此时pod 处于ContainerCreating状态。
  3. node上的kublet调用CRI,CNI,CSI接口启动容器,启动后pod处于Running状态。
  4. 用户删除pod时,pod立即处于Terminating状态。停止容器进程,删除网络,卸载目录挂载,删除finalizer。即删除所有和pod有关的对象(gone)。
  5. 假如pod中的所有容器进程都异常退出了,或者至少有一个容器进程exit非0,且重启策略为Never,那么pod处于Failed状态。
  6. 假如node由于网络原因,无法上报状态,那么pod处于Unknown状态。
  7. 假如pod中的所有容器进程都正常退出了,且重启策略为Never,那么pod处于Successd状态。
  8. 假如pod被kubelet驱逐了,那么pod处于Evicted状态。

pod状态机

下图就是上面pod状态流转步骤的简化。

pod phase

kubectl get pod中的STATUS是由pod中status的conditions和phase计算得来的。

计算细节

常见的比如CrashLoopBackOff:phase状态为running,但是容器进程退出了。
最下面的Outofcpu/OutofMemory:由于kubelet有准入插件,当node的cpu或者内存不足时,就会出现这个状态。

pod高可用

  • 应用要想平稳的运行在k8s上,owner就必须知道应用的资源需求。

    • 设置合理的 resources.memory limits 防止容器进程被 OOMKill;
    • 设置合理的 emptydir.sizeLimit 并且确保数据写入不超过 emptyDir 的限制,防止 Pod 被驱逐。
  • 需要部署多少实例

  • 更新策略:二者不能同时为0。

    • maxSurge:和期望ready的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快。
    • maxUnavailable:和期望ready的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑;

容器可能遇到的进程中断

file

pod数据如何存储

file

qosClass

file

  • Guaranteed

    • Pod 的每个容器都设置了资源 CPU 和内存需求
    • requests 和 limits 是一致的
      file
  • Burstable

    • 至少一个容器制定了 CPU 或内存需求
    • requests 和 limits 可以不一致
      file
  • BestEffort

    • Pod 中的所有容器都未指定 CPU 或内存需求
      file
  • 当计算节点检测到内存压力时,Kubernetes 会按 BestEffort-> Burstable->Guaranteed的顺序驱逐pod。

  • 对于重要应用使用 Guaranteed 类型。

  • 认真考量 Pod 需要的真实需求并设置 Limit 和 resource,这有利于将集群资源利用率控制在合理范围并减少 Pod 被驱逐的现象。

  • 尽量避免将生产 Pod 设置为 BestEffort,但是对测试环境来讲,BestEffort Pod 能确保大多数应用不会因为资源不足而处于 Pending 状态。

  • Burstable 适用于大多数场景。因为它既支持超卖,又锁定一定的资源,可以平衡资源利用率和应用的性能。

基于 taint 的 Evictions

当node状态异常时,nodeLifeCycleController会监听这种node和node上的pod。当node状态为notReady或unreachable时,这个node就会被打上如下的taint。

  • NoExecute:node上的pod被驱逐
  • NoSchedule:不往这个node上调度pod
taints:
- effect: NoSchedule
  key: node.kubernetes.io/unreachable
  timeAdded: "2022-03-09T11:25:10Z"
- effect: NoExecute
  key: node.kubernetes.io/unreachable
  timeAdded: "2022-03-09T11:25:21Z"
- effect: NoSchedule
  key: node.kubernetes.io/not-ready
  timeAdded: "2022-03-09T11:24:28Z"
- effect: NoExecute
  key: node.kubernetes.io /not-ready
  timeAdded: "2022-03-09T11:24:32z"

pod也有对应的tolerations。由于网络可能会抖动,所以需要tolerationSeconds,默认是300s。如果是依赖于本地存储状态的有状态应用应增大 toleration Seconds 比如900s以避免被驱逐。

file

Probe

健康探针类型

  • livenessProbe
    • 探活,当检查失败时,意味着该应用进程已经无法正常提供服务,kubelet会终止该容器进程并按照 restartPolicy 决定是否重启。
  • readinessProbe
    • 就绪状态检查,当检查失败时,意味着应用进程正在运行,但因为某些原因不能提供服务,Pod 状态会被标记为NotReady。
  • startupProbe
    • 应用没有起来,探活和就绪探测是失败的,由于不停的重试会导致应用压力过大。
    • 在初始化阶段(Ready 之前)进行的健康检查,startupPorbe运行后才运行liveness和readness。通常用来避免过于频繁的监测影响应用启动。

探测方法

  • Exec:在容器内部运行指定命令,当返回码为◎时,探测结果为成功。
  • TCPSocket:由 kubelet 发起,通过TCP 协议检查容器 1P 和端口,当端口可达时,探测结果为成功。
  • HTTPGet:由 kubelet 发起,对Pod 的IP 和指定端口以及路径进行HTTPGet 操作,当返回码为 200-400 之间时,探测结果为成功。

file

探针属性

  • initialDelaySeconds 表示延迟5s开始第一次探测,默认值是0,最小值是0。
  • timeoutSeconds 表示每次探测的超时时间,5s后如果没返回结果就认为超时失败,默认值是1,最小值是1。
  • successThreshold 表示在探测失败后,最小的连续成功被认为是成功的,默认值是1,最小值是1。
  • failureThreshold 表示当探测失败时,Kubernetes将在认为失败前尝试次数。默认值是3,最小值是1。
  • periodSeconds 表示多久进行一次探测,默认是10S,最小值是1。

重启策略

  • Always: 当容器终止退出后,总是重启容器,默认策略
  • Onfailure: 当容器异常退出后(退出码非0)时,才重启容器
  • Never: 当容器终止退出时,不重启容器

ReadinessGates

  • 使用了ReadinessGates,即使readiness就绪返回成功了,pod也不算就绪。
  • ReadinessGates condition status需要为 True 状态后,加上内置的 Conditions,Pod 才可以为就绪状态。
  • 该状态应该由某控制器修改。

比如pod需要配置额外的dns,那么就需要有一个额外的controller,配置好dns后,将condition中的status由false改为true,pod才ready。

file

健康检查注意

  1. 所有提供服务的容器都应该加上readinessProbe,不通过就视为 Pod 不健康,然后会自动将不健康的 Pod 踢出去,避免将业务流量转发给异常 Pod。
  2. 探测结果一定要真实反映业务健康状态,通常是业务程序提供 HTTP 探测接口比如/health。
  3. 不要轻易使用livenessProbe,因为探测失败就可能会导致pod重启。
  4. 即使要使用livenessProbe,也要注意:
    • 将failureThreshold 设置得更大一点,避免因为太敏感导致pod重启;
    • 而且要在应用完全启动后再开始探测,如果版本是1.18以下,可以将initialDelaySeconds 加大一点;版本是1.18以上,那么可以用startupProbe。
  5. 探测逻辑里不要有外部依赖,比如依赖外部的db,由于网络抖动,探测失败,容易造成级联故障。
  6. 避免使用 TCP 探测,因为TCP 探测结果并不能真实反映业务真实情况:
    • 程序 hang 死时, TCP 探测仍然能通过 (TCP 的 SYN 包探测端口是否存活在内核态完成,应用层不感知)。
    • 当程序正在优雅退出的过程中,端口监听还在,TCP 探测会成功,但业务层面已不再处理新请求了。

Post-start 和 Pre-Stop Hook

  • Post-start:容器启动后还需要做一些额外的操作,可以执行Post-start script。无法保证script和容器的 Entrypoint谁先运行。
  • Pre-Stop:用户删除容器时,会先运行Pre-Stop script,这里面可以定义优雅终止。然后kubelet会发kill -SIGTERM不再接受新的连接然后再发kill -SIGKILL。
    file

terminationGracePeriodSeconds

  • 当删除pod到kubelet强制kill容器进程的时间是terminationGracePeriodSeconds。默认是30s。可以防止应用突然被kill掉。
  • 如果30s内还没有执行完成,那么pod会被强制删除。
    file
    file

Terminating Pod 的误用

bash/sh 会忽略 SIGTERM 信号量,因此 kilL -SIGTERM 会永远超时,若应用使用 bash/sh 作为Entrypoint,则应避免过长的 grace period。
file

如果不关心 Pod 的终止时长,那么无需采取特殊措施;
如果希望快速终止应用进程,那么可采取如下方案:

  • 在preStop script 中主动退出进程;
  • 在主容器进程中使用特定的初始化进程。

优雅启动和终止

优雅的初始化进程应该:

  • 正确处理系统信号量,将信号量转发给子进程;
  • 在主进程退出之前,需要先等待并确保所有子进程退出;
  • 监控并清理孤儿子进程。
    可以使用tini作为容器镜像的entrypoint。
    参考链接:https://qithub.com/kralin/tini
分类: k8s
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x