首页 新闻 会员 周边

Kubernetes Pod 生命周期中的核心组件(Init 容器、主容器、存活探针、就绪探针、生命周期钩子)全部串联

0
[已关闭问题] 关闭于 2026-06-16 21:47
上面的流程分工,你解释的不错,但有两点存在疑点: 第一点: 你是这么说的:在容器刚创建后,会触发 postStart 钩子: 注意:这里的 postStart 钩子是和容器的启动命令(command)并发(并行)执行的 这句话难道意思是容器刚创建不包含容器容器启动命令吗? 第二点:Pod 终止(PreStop 钩子) 当用户执行 kubectl delete pod 删除该 Pod 时 Prestop,Poststart都是在容器下面的子标签,不是pod下面的,不应该是删除容器,而不是删除pod啊,pod里面可能有多个容器
 
 
 
 
*Tesla*的主页 *Tesla* | 小虾三级 | 园豆:1758
提问于:2026-06-16 21:45
< >
分享
所有回答(1)
0

第一点:关于“容器刚创建”与“启动命令”的关系

你的疑惑:如果 PostStart 是和启动命令(command)并行执行的,那“容器刚创建”难道不包含启动命令吗?
底层真相:在 K8s 的底层逻辑中,“容器创建(Create)”和“容器启动(Start)”其实是两个分离的动作。
  1. Create(创建):Kubelet 调用容器运行时(如 Docker/containerd),在宿主机上分配好资源(Cgroups、Namespace、网络、挂载卷等),并把你的 command(即 ENTRYPOINT/CMD)写入容器的配置中。此时,容器处于“已创建但未运行”的状态。
  2. Start(启动):Kubelet 发出启动指令,容器运行时开始执行 command(主进程启动)。就在主进程被拉起的同时,Kubelet 会立刻触发 PostStart 钩子。
结论:PostStart 钩子确实是在“容器刚创建并启动”的瞬间触发的。它与主进程(启动命令)是并发(并行)的,K8s 并不保证 PostStart 会在启动命令执行完毕之前运行。这就是为什么官方强调:不能在 PostStart 中做阻塞主进程启动的耗时操作。

第二点:关于“删除 Pod”还是“删除容器”

你的疑惑:PreStop 和 PostStart 都是写在 containers 子标签下的,Pod 里可能有多个容器。执行 kubectl delete pod 时,难道不应该叫“删除容器”吗?
底层真相:kubectl delete pod 和“删除容器”并不冲突,它们是“整体与局部”的关系。
  1. Pod 是逻辑单元,容器是物理执行单元:
    当你执行 kubectl delete pod 时,你是在向 K8s 的 API Server 发出一个指令:“我要销毁这个 Pod”。API Server 接收到指令后,会将这个 Pod 标记为“Terminating(终止中)”。
  2. Kubelet 的拆解动作:
    节点上的 Kubelet 监听到这个 Pod 被标记为终止后,会去遍历这个 Pod 里的所有容器(包括 Init 容器、主容器等),并对它们逐个执行终止流程。
  3. PreStop 的触发范围:
    因为 PreStop 是定义在具体的某个 container 下的,所以 Kubelet 只会对配置了 PreStop 的那个特定容器触发钩子。
    举个例子:如果 Pod 里有容器 A 和容器 B,只有容器 A 配置了 PreStop。当你 delete pod 时,Kubelet 会先执行容器 A 的 PreStop,然后再发 SIGTERM 给 A;而容器 B 没有 PreStop,Kubelet 会直接发 SIGTERM 给 B。
结论:
你说的完全正确,PreStop 确实是针对“容器”级别的。但是,在 K8s 的设计哲学中,Pod 是调度和销毁的最小基本单元。我们通常不会(也不能直接)去单独删除 Pod 里的某一个容器,只能通过删除整个 Pod,来触发 Kubelet 去执行内部各个容器的清理(PreStop)和销毁动作。
所以,表述为“执行 kubectl delete pod 时,Kubelet 会触发对应容器的 PreStop 钩子”是最严谨的
 
 
docker run 确实是一个“创建并启动”的组合动作,而 Kubernetes 在底层把这两个动作严格拆分了。
我们可以从以下几个维度来彻底理清这个差异:

1. Docker 的 docker run = docker create + docker start

在 Docker 的底层逻辑中,docker run 其实是一个“快捷指令”,它等价于:
  1. docker create:创建容器,分配资源,写入启动命令,但此时容器处于 Created 状态,进程并未运行。
  2. docker start:启动这个已经创建好的容器,主进程开始运行。
因为 Docker 主要是面向单机运行的,它不需要复杂的“钩子”机制,所以把这两步合并,对用户来说就是“一键启动”。

2. K8s 为什么要强行拆分 Create 和 Start?

Kubernetes 作为集群编排系统,需要处理极其复杂的生命周期和状态同步。它必须把 Create 和 Start 拆开,并在中间插入自己的逻辑:
  • Create 阶段(准备期):Kubelet 调用容器运行时(CRI),创建容器的文件系统、网络命名空间(veth pair)、Cgroups 资源限制等。此时容器在底层是存在的,但主进程还没跑起来。
  • Start 阶段(运行期):Kubelet 启动容器主进程。就在主进程启动的同一瞬间,Kubelet 的 Kubelet 进程(而不是容器运行时)会立刻去执行你配置的 postStart 钩子。

3. 为什么要这样设计?(核心目的)

如果 K8s 也像 Docker 那样直接“创建并启动”,Kubelet 就无法控制 PostStart 钩子的触发时机。
K8s 把 Create 和 Start 拆开,就是为了在“容器环境准备完毕(Create)”和“业务进程开始运行(Start)”之间,硬生生地插入一个“钩子执行点”。这样 K8s 就能确保:只要容器一启动,PostStart 钩子就会立刻被触发(比如去注册中心注册自己),而不需要业务代码自己去写这些启动逻辑。

总结

你看到的 docker run 是 Docker 封装好的用户态快捷操作;而 Kubernetes 中的 Create 和 Start,是 Kubelet 在底层编排时为了精准控制生命周期而做出的架构拆分。
*Tesla* | 园豆:1758 (小虾三级) | 2026-06-16 21:47
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-pod
  labels:
    app: lifecycle-pod
spec:
  containers:
  - name: busybox-container
    image: wangyanglinux/tools:busybox
    command: ["/bin/sh","-c","touch /tmp/live ; sleep 600; rm -rf /tmp/live; sleep
3600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/live"]
      initialDelaySeconds: 1
      periodSeconds: 3
    lifecycle:
      postStart:
        httpGet:
          host: 192.168.66.11
          path: index.html
          port: 1234
      preStop:
        httpGet:
          host: 192.168.66.11
          path: hostname.html
          port: 1234
  - name: myapp-container
    image: wangyanglinux/myapp:v1.0
    livenessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3
      timeoutSeconds: 3
    readinessProbe:
      httpGet:
        port: 80
        path: /index1.html
      initialDelaySeconds: 1
      periodSeconds: 3
  initContainers:
  - name: init-myservice
    image: wangyanglinux/tools:busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: wangyanglinux/tools:busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']这个给我分析下到底怎么执行
这份 YAML 资源清单非常经典,它把 Kubernetes Pod 生命周期中的核心组件(Init 容器、主容器、存活探针、就绪探针、生命周期钩子)全部串联在了一起。
按照 Kubernetes 的底层运行逻辑,这个 Pod 的完整执行时间线如下:
第一阶段:Init 容器串行执行(启动前的“门卫”)
Pod 创建后,不会直接启动业务容器,而是严格按照 YAML 中定义的顺序,串行执行 initContainers:
执行 init-myservice:运行 nslookup myservice。如果找不到,就会一直循环等待(每 2 秒重试一次)。只有当 DNS 解析成功(退出码为 0),它才会结束。
执行 init-mydb:在第一个 Init 容器成功后,才开始运行 nslookup mydb。同样,必须等待 DNS 解析成功才会结束。
注:如果这两个 Init 容器一直失败,Pod 会卡在 Pending 状态,主容器永远不会启动。
第二阶段:主容器并行启动(业务“员工”打卡上班)
当两个 Init 容器都顺利完成后,Kubelet 会并行启动 containers 下定义的两个主容器:
启动 busybox-container
启动 myapp-container
第三阶段:生命周期钩子触发(入职/离职手续)
在容器刚创建后,会触发 postStart 钩子:
注意:这里的 postStart 钩子是和容器的启动命令(command)并发(并行)执行的,不保证谁先完成。
在这个例子中,busybox-container 启动时,会立刻向 192.168.66.11:1234/index.html 发送一个 HTTP GET 请求。
第四阶段:探针接管(“医生”与“质检员”开始工作)
容器启动后,Kubelet 开始根据配置进行健康检查:
1. busybox-container 的存活探针(Liveness Probe):
机制:延迟 1 秒后开始,每 3 秒执行一次 test -e /tmp/live(检查 /tmp/live 文件是否存在)。
业务联动:它的启动命令是 touch /tmp/live ; sleep 600; rm -rf /tmp/live; sleep 3600。这意味着前 600 秒内,文件存在,探针成功;600 秒后文件被删除,探针将连续失败。
结果:存活探针失败会触发重启机制,因此这个容器会在运行 600 多秒后被 K8s 强制重启,重启后又重新创建文件,进入下一个 600 秒的循环。
2. myapp-container 的存活探针与就绪探针:
存活探针(Liveness):延迟 1 秒,每 3 秒请求一次 80/index.html。如果失败,容器会被重启。
就绪探针(Readiness):延迟 1 秒,每 3 秒请求一次 80/index1.html。如果这个探针成功,Pod 才会被加入 Service 的 Endpoints 列表,开始接收外部流量;如果失败,流量会被切断(但不会重启容器)。
第五阶段:Pod 终止(PreStop 钩子)
当用户执行 kubectl delete pod 删除该 Pod 时:
在容器被发送 SIGTERM 信号强制关闭之前,会触发 preStop 钩子。
busybox-container 会向 192.168.66.11:1234/hostname.html 发送 HTTP GET 请求,用于执行优雅下线前的清理或注销动作。
💡 核心总结
这个 YAML 完美演示了 K8s 的分工:
Init 容器负责阻塞等待依赖(数据库/服务)就绪。
PostStart 负责启动时的外部通知。
Readiness 探针决定 Pod 什么时候能接客(接收流量)。
Liveness 探针决定 Pod 什么时候该“死而复生”(比如 busybox 的 600 秒定时自杀重启)。
PreStop 负责被杀前的优雅告别。
支持(0) 反对(0) *Tesla* | 园豆:1758 (小虾三级) | 2026-06-16 21:50
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册