资讯动态

Kubernetes Pod生命周期全解析:状态、探针、钩子与重启策略

发布时间:2026/10/21 3:35:35 来源:尧图企业网站定制
1. 从“跑起来”到“用起来”为什么必须懂Pod生命周期Kubernetes里有个老生常谈但又特别容易翻车的话题Pod生命周期。很多人刚接触k8s时会用kubectl get pod看到一堆状态一会儿ContainerCreating一会儿又CrashLoopBackOff不知道它到底在干什么。作为系列第三篇这篇文章就是想把这个过程彻底讲透——Pod从被提交到集群到最终被销毁中间到底经历了什么每个阶段启动容器、执行钩子、健康检查的顺序是怎样的以及在什么场景下该用哪种检查方式。这个内容能帮着你解决几类实际问题服务启动慢导致流量打进来就报错、发布新版本时旧Pod还没处理完请求就被杀掉、Pod反复崩溃但看不出原因、依赖服务没就绪导致业务启动后疯狂重试。如果你正准备排查这些问题或者想把k8s的稳定性做得更细这篇文章值得认真过一遍。文章会按这样的顺序展开先梳理Pod从创建到销毁的完整时间线再讲PostStart和PreStop这两个生命周期钩子怎么用接着对比Liveness、Readiness、Startup三种探针的适用场景然后分析三种重启策略的控制逻辑最后用一个真实发布的例子把前面所有知识点串起来。2. Pod的“一生”从创建到销毁的完整时间线2.1 五个核心状态Pending、Running、Succeeded、Failed、UnknownKubernetes官方把Pod的生命周期分为五个阶段kubectl get pod输出的STATUS列就是这五个状态之一。PendingPod已经被集群接受但还没有就绪。这个阶段可能正在调度、正在拉取镜像或者Initalize容器还没跑完。Pod在这个阶段没有真正的工作负载在运行。RunningPod已经绑定到了某个节点而且所有容器至少启动了一个正在运行中。注意“至少一个”这个描述意味着Running不代表业务就绪这一点很多人会踩坑。SucceededPod里所有容器都成功退出退出码为0。这一般是批处理任务或Job的正常结束状态。FailedPod里至少有一个容器以非0退出码退出或者因为系统原因被终止。Unknownkubelet无法向API Server汇报状态比如节点失联、网络分区这时控制平面就把Pod标记为Unknown。只看状态描述可能还是有点飘我一般用“人从叫车到下车”来类比Pending是你在App上点了“呼叫”司机还没接单或者车还在路上Running是车已经在开了但你在车上不一定意味着你已经到目的地了Succeeded是你付钱下车了一整趟都顺利Failed是半路出了事故Unknown则是司机和平台完全失联平台不知道你在哪。2.2 一个Pod从提交到稳定运行的完整时序很多人搞不清Pod内部那些容器到底按什么顺序启动这里把完整流程列一下用户通过kubectl apply创建Pod对象请求到API Server对象被写入etcd。调度器kube-scheduler开始调度选择合适的节点更新Pod的nodeName字段。节点上的kubelet观察到Pod被分配给自己开始处理。如果有Init容器按定义顺序依次初始化只有前一个Init容器成功退出后一个才会启动。初始化完成后kubelet启动主容器同时执行postStart钩子如果配置了的话。容器启动后如果配置了Readiness探针根据探测结果决定Pod是否进入Ready状态。Liveness探针持续运行一旦探测失败kubelet会根据restartPolicy决定是否重启容器。有一个细节容易被忽略postStart和容器主进程是异步执行的。说得直白点postStart钩子不保证一定在容器的ENTRYPOINT之前运行它两个是同时触发的中间存在竞争关系。在JAVA服务启动脚本里读取某个配置文件而postStart恰好要往文件里写内容就可能出现先后顺序不确定的问题这点要格外注意。再补充一个行为细节postStart钩子如果执行失败或者一直卡住容器会被标记为Failed状态并可能被重启。这个机制本意是好的防止配置没生效就启动容器但实际中如果钩子里写了个死循环容器就会一直处于ContainerCreating排查半天找不到问题以为在拉镜像其实是钩子卡住了。2.3 生命周期中的两个关键窗口启动期与优雅退出期在Pod生命周期里有两个阶段容易引发线上故障启动期和退出期。启动期的核心问题是容器启动慢但探针默认立刻开始探测。比如一个JAVA应用要加载大量配置、连接数据库启动可能要20到30秒但Liveness探针在容器启动后马上开始检测前几次失败就会触发重启形成CrashLoopBackOff。解决思路有两个配置startupProbe或者把livenessProbe的initialDelaySeconds调大。从k8s 1.17开始推荐的方案是引入startupProbe因为它在成功之前不会让liveness探针参与判定。退出期的核心问题是优雅停机。默认情况下如果Pod被删除kubelet会向容器发送SIGTERM信号然后等待terminationGracePeriodSeconds默认30秒超时后强制发送SIGKILL。如果应用不处理SIGTERM30秒后就会被强杀正在处理的请求直接断掉。对于流量型业务一般建议在preStop钩子里做流量摘除和排空比如先调用注册中心的注销接口再sleep几秒让网关把流量切走之后再慢慢退出。3. 生命周期钩子实战PostStart与PreStop的配置与坑3.1 PostStart容器启动后的钩子怎么用postStart在容器创建之后立刻执行主要用来做一些“容器内不方便做、但又必须在业务起来之前完成”的初始化操作。常见场景包括修改容器内配置文件比如往Nginx的配置里写入上游地址。把本Pod的IP注册到外部注册中心。预热本地缓存或者建立数据库连接池。配置格式比较简单支持两种写法exec和httpGet。exec执行一段shell命令httpGet请求一个URL。apiVersion: v1 kind: Pod metadata: name: demo-poststart spec: containers: - name: demo image: nginx:1.24 lifecycle: postStart: exec: command: [/bin/sh, -c, echo hello /tmp/startup.log]这个配置会在容器主进程启动的同时执行echo hello /tmp/startup.log。看起来简单但要注意几个问题。第一个问题是postStart和主进程的竞争关系。postStart跟ENTRYPOINT同时触发并不是先后关系如果两边同时操作同一个文件就容易出现“钩子写的内容主进程读不到”的情况。我在实际部署某状态上报服务时就遇到过主进程起来后读配置配置还没写完的问题排查了很久最后定位到是竞态。第二个问题是钩子不能用http://localhost来访问容器的服务。要知道postStart执行时容器主进程可能还没监听端口即使监听了钩子执行的上下文也未必完全等价于容器内的网络命名空间。不是所有网络访问都能像预期一样工作如果在postStart里访问localhost:8080/health可能请求直接被拒绝。第三个问题是钩子执行没有超时控制。如果钩子里命令一直挂在那里Pod会一直卡在ContainerCreating状态。按社区实践postStart里不要放耗时长的任务超过几十秒要怀疑是不是有问题最好用独立进程去完成初始化或者放到业务进程内部的启动逻辑里。3.2 PreStop优雅停机钩子的正确打开方式preStop在容器被终止前执行执行完才发SIGTERM。典型用途是在删除Pod之前把流量从负载均衡上摘掉然后让业务进程有足够时间处理存量请求。apiVersion: v1 kind: Service metadata: name: demo-web spec: selector: app: demo-web ports: - port: 80 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: demo-web spec: replicas: 3 selector: matchLabels: app: demo-web template: metadata: labels: app: demo-web spec: terminationGracePeriodSeconds: 30 containers: - name: demo-web image: registry.example.com/demo-web:v2 lifecycle: preStop: exec: command: [/bin/sh, -c, sleep 10]这里的sleep 10是预留时间让Endpoints里先摘掉这个Pod新的流量不再进来旧请求继续被处理具体时间可以根据业务调整。如果应用本身处理SIGTERM逻辑比较好preStop里的等待时间可以短一些如果应用完全不处理SIGTERM信号那就必须靠preStop拖时间否则强杀。再提醒一个细节preStop的执行时间是被terminationGracePeriodSeconds限制的。kubectl删除Pod时开始计时30秒内必须完成preStop和主进程退出超时就被SIGKILL。如果你的preStop要10秒应用优雅退出要20秒共存期就要留到35秒否则存活请求还是会断。还有一点preStop不会因为业务的请求处理完而自行结束它就是在那里执行命令直到完成。所以不要在里面写无限等待的逻辑一定要能退出。3.3 什么时候不该用钩子钩子不是万能的有些场景用钩子反而添乱。如果你需要的是“先做A再做B”这种严格顺序postStart不适合应该用Init容器。Init容器串行执行前一个成功退出后一个才开始天然有序。如果只是想等一个外部依赖就绪比如数据库、RedispostStart也不合适应该用Readiness探针。postStart阻塞会让容器卡住Readiness探针只是不让服务接流量业务进程本身还能跑状态也能保持可观测。最忌讳的是把postStart当成部署脚本用。部署脚本涉及大量文件复制、依赖安装应该打镜像不该在Pod启动时现场操作。镜像里没有的东西要重新构建镜像。4. 三种探针一次讲透Liveness、Readiness、Startup Probe4.1 三种探针到底在探什么探针是kubelet定期对容器发起检查的机制配置在容器级别。三者定位完全不同简单理解Liveness探针决定要不要重启容器。探测失败kubelet按重启策略杀掉并重启容器。Readiness探针决定要不要把流量打进来。探测失败Pod会从Service的Endpoints里被摘掉业务进程不重启。Startup探针决定要不要给容器“豁免期”。探测成功之前Liveness和Readiness的探测都会暂停。用类比来看Liveness像是人是否活着心跳停了就送急救重启Readiness像是人是否能够接待客户状态不好就先挂“暂停服务”的牌子流量摘除Startup像是给新人一个适应期你不用一上来就判断他是否熬得住先让他顺利度过启动阶段再说。这个机制在配置上的体现如下readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 15 periodSeconds: 20 startupProbe: httpGet: path: /health port: 8080 failureThreshold: 30 periodSeconds: 54.2 参数怎么调才不踩坑每个探针都有几个通用参数先把这些参数讲明白。initialDelaySeconds容器启动后延迟多久才开始第一次探测。这是最容易被忽视的。如果业务启动需要20秒这个值设成3秒前几次探测大概率失败。periodSeconds探测间隔默认10秒。间隔太长故障发现慢间隔太短探测请求对业务有压力还容易误判。timeoutSeconds单次探测超时时间默认1秒。如果接口响应经常超过1秒要调大否则探针请求超时被当成失败。failureThreshold连续失败多少次才触发动作默认3次。Liveness连续失败3次就重启Readiness连续失败3次就摘流量。successThreshold连续成功多少次才认为健康默认1次。Readiness一般保持默认别调高否则Pod恢复接流量的时间变长。实际调参经验可以概括为先摸清业务的启动时间再配探针。比如某服务启动要25秒API接口最坏响应2秒可以这样配startupProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 0 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 30 readinessProbe: httpGet: path: /health port: 8080 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 livenessProbe: httpGet: path: /health port: 8080 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3这样配置的逻辑是startupProbe给了最多150秒的启动窗口业务25秒内启动绰绰有余启动成功后readiness每5秒查一次最多15秒摘流量liveness判断的是运行期活没活间隔10秒比较合理不会过于频繁。4.3 探针选择与常见误区很多人会把Liveness和Readiness混用或者只配一个Liveness这里说说两者的权衡。如果只配Liveness不配ReadinessPod启动过程中流量就会打进来。因为Pod状态是Running且Ready不带readinessProbe时默认容器启动即为Ready而实际业务还没起来请求失败率飙升。正确的做法是业务启动慢或需要依赖外部服务必须配Readiness。如果只配Readiness不配Liveness业务彻底死锁后流量不会进但容器一直占用资源不重启不恢复。所以核心服务的标准配置是startupProbe保证慢启动容器有缓冲readinessProbe保证流量不进不可用PodlivenessProbe保证死锁容器能被回收。还有一个比较容易忽略的地方httpGet探针的返回值处理。只要HTTP状态码在200到399之间就认为成功其他一律失败。如果健康检查接口返回302重定向探针会判定成功但如果重定向到了一个不存在的位置业务层不健康探针却认为正常这会导致流量打到坏的Pod上。5. 重启策略Pod反复崩溃背后的控制逻辑5.1 Always、OnFailure、Never的适用场景restartPolicy是Pod级别的字段控制的是“容器异常退出后是否重启”而不是“整个Pod是否被调走”。Always容器无论以什么方式退出都重新启动。Deployment、StatefulSet这类工作负载默认是Always这也是为什么普通服务崩了之后k8s会不断拉起重启。OnFailure只有容器以非0退出码退出时才重启。适合Job类任务比如批处理失败了自动重跑。Never容器退出后不重启。适合一次性任务第二次跑直接创建新Pod。我见过有人把restartPolicy跟“Pod删除后是否重建”搞混。Pod是受Deployment等控制器管理的重启策略只管Pod内部容器的行为Pod被删掉后是否重新创建那是控制器的事跟restartPolicy无关。5.2 CrashLoopBackOff的逻辑与处理如果容器反复启动又退出会进入CrashLoopBackOff状态。kubelet会指数退避比如第一次隔10秒重启第二次20秒第三次40秒上限300秒。看到CrashLoopBackOff不要急着直接去改配置第一步要去看日志才能定位到根因常见原因可能包括启动命令写错容器起来就退出这个通过kubectl logs基本就能看到。配置缺失或者环境变量传错进程启动时直接panic。Liveness探针配置太激进容器明明还在启动就被判定为失败然后杀掉重启。镜像启动命令里依赖了不存在的文件或者挂载的ConfigMap不完整。提供几个常用排查命令# 查看当前状态 kubectl get pod -o wide # 查看具体事件 kubectl describe pod pod-name # 查看日志 kubectl logs pod-name --tail100 # 查看上一次容器的退出原因 kubectl get pod pod-name -o jsonpath{.status.containerStatuses[0].lastState}顺带说个经验如果容器重启得很快日志里看不到崩溃信息可以查看退出的容器日志命令需要加--previous参数例如kubectl logs pod-name --previous。这是最早被我漏掉的排查方式却很管用。5.3 重启策略与Docker重启策略的区别有Docker基础的人可能自然而然想到--restartalways。两者机制有本质区别Docker的重启策略是Docker守护进程负责执行崩溃后由dockerd拉起不关心容器在哪个宿主机上也不做分布式调度。Kubernetes的重启是由kubelet负责重启只是它的一种处理动作而且会结合探针状态和控制器期望数来决定。更关键的是Kubernetes还有一个机制如果节点本身故障Pod会被重新调度到其他节点这是Docker单机模式不具备的。不要以为容器崩溃重启了就等于服务恢复。重启后的容器进程能起来不代表业务就绪依赖的外部服务没过健康检查CrashLoopBackOff还是会再来。6. 实操复盘一次滚动更新中的Pod生命周期全过程6.1 场景与配置接下来用一个发布场景把所有知识串起来。假设我现在管理一个叫demo-web的服务当前版本是v1准备发布v2版本Deployment副本数为3配置了readinessProbe和livenessProbe同时在preStop里做了流量摘除等待。发布命令很简单kubectl set image deployment/demo-web demo-webregistry.example.com/demo-web:v2或者用apply方式更新镜像。关键是想清楚发布过程中每个Pod如何完成“旧下线、新上线”的交接。6.2 观察生命周期状态变化发布触发后Deployment控制器会创建新的ReplicaSet期望副本数为0逐步增加。发布过程可以随时用kubectl rollout status deployment/demo-web观察。大概率能看到的事件按顺序是这样的新Pod创建状态进入Pending调度中选择可用节点。如果节点资源不足会卡在Pending事件里能看到FailedScheduling通常会给出资源不足的信息。Pending阶段还可能拉镜像尤其是新镜像首次拉取耗时较长。镜像拉取完成后容器开始创建进入ContainerCreating。这个阶段如果配置了postStart也在此时执行。容器启动后进入Running状态但因为配置了readinessProbe一开始Ready列是0/1探针通过后变为1/1。新Pod全部就绪后旧的ReplicaSet开始缩容旧Pod被终止。终止旧Pod时preStop开始执行Sleep等待之后主进程收到SIGTERM开始优雅退出。旧Pod全部终止发布完成。整个过程可以尝试用一个“类似滚动更新记录”的节奏来观察# 以JSON格式实时观察Pod状态 kubectl get pods -w # 观察Endpoints的变化流量是否切换 kubectl get endpoints demo-web -w6.3 发布过程中可能遇到的经典问题发布过程中最容易出问题的点是新Pod还没就绪旧Pod就已经被下线了导致服务空窗。这个问题的根因是Deployment的maxUnavailable和maxSurge参数配置不合理。默认配置里maxSurge25%maxUnavailable25%也就是每次允许额外创建一个新Pod允许一个旧Pod不可用。如果副本数是3滚动过程可能先杀掉一个旧Pod同时创建一个新Pod此时所有副本的总可用Pod最少为2一般不会太慌。但有些团队会为了加速发布把maxUnavailable调成100%那就会先杀掉所有旧Pod再创建新Pod整个过程服务完全不可用。现场如果探针没配好新Pod一直不就绪服务就长时间挂掉。经验是线上流量型服务默认别把maxUnavailable调太大宁可慢一点保证总可用Pod不要低于期望值的60%到70%。另一个经典问题是新Pod启动后readiness一直不通Deployment发布会阻塞旧Pod继续运行表面看起来服务没挂但新版本一直发布不完。这时要立刻查探针配置是不是有问题比如健康检查接口路径配错了或者新版本接口返回状态码异常。6.4 这个场景能带给你哪些判断思路一旦把生命周期状态和发布机制串起来看排障思路会清晰很多。Pod卡在Pending优先看调度事件常见原因是资源不足、污点不容忍、亲和性不匹配。Pod卡在ContainerCreating优先看是否有Init容器、postStart钩子是否卡住、镜像是否能正常拉取。Pod变成Running但Ready为0/1优先查readiness探针看健康检查接口是否正常。Pod反复重启优先查liveness探针和重启策略再看业务日志。Pod删除后一直Terminating优先看preStop是否执行完terminationGracePeriodSeconds是否足够。这些判断本质上都是在问一个问题当前Pod正处在生命周期流程的哪一步它期望的下一步是什么。7. 常见问题速查与经验建议7.1 Pod生命周期相关排查速查表现象可能原因排查命令处理建议一直Pending资源不足、污点、亲和性kubectl describe pod查看Events调整资源或调度配置ContainerCreating超时镜像拉取慢/失败、钩子卡住kubectl describe pod看镜像地址是否正确钩子逻辑是否卡死Running但Ready为0Readiness探针失败kubectl get pod -o yaml检查健康检查接口路径与端口CrashLoopBackOff启动命令错误、探针误杀kubectl logs --previous先看上次退出日志删除Pod卡在TerminatingpreStop执行超时kubectl describe pod检查preStop命令和gracePeriod容器未收到SIGTERM业务忽略信号业务日志接入优雅退出逻辑或通过preStop补足7.2 三条值得沉淀的实战经验第一配置探针时把启动时间想清楚。大多数容器启动慢的问题都可以通过startupProbe解决不要靠调整initialDelaySeconds硬撑。startupProbe失败意味着容器彻底起不来了liveness和readiness探测会暂停它们只关心运行期健康这样的职责划分才清晰。第二preStop里加sleep要适度。有团队直接把sleep固定成20秒完全不看业务实际需要的优雅退出时间。有些请求几百毫秒就处理完了白白等20秒发布一次要多等很久。正确做法是根据业务请求的最坏响应时间去设置同时配合terminationGracePeriodSeconds整体规划。第三做任何涉及Pod生命周期配置的变更都要先看影响面。改探针参数可能影响发布节奏改重启策略可能影响故障恢复改优雅退出时间可能影响发布耗时。要在测试环境完整跑一轮发布再来线上操作。我个人用得比较多的一句检查清单是创建时看Init容器、启动时看钩子、稳定后看探针、退出前看preStop。把Pod的一生按这几个环节切分很多问题都能快速定位到具体环节不会瞎猜。这套思路在维护实际业务系统时帮过我很多次也建议你从今天开始每次发布都用kubectl get pods -w观察一遍New Rs的变化看多了就熟了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑