【Kubernetes从入门到精通】第08篇:Pod的前世今生——K8s最小调度单元深度解析

发布时间:2026/8/2 18:48:08

【Kubernetes从入门到精通】第08篇:Pod的前世今生——K8s最小调度单元深度解析 上一篇【第07篇】K8s架构全景图——Master和Node的天作之合下一篇【第09篇】Pod的YAML从入门到精通——每个字段都是干什么的摘要K8s不直接管理容器而是管一个叫Pod的东西——每个Pod里可以有一个或多个容器它们共享网络、共享存储、甚至共享PID。这设计一开始让很多人抓狂Docker Compose里几个容器各跑各的不香吗非搞个壳子包起来干啥等你用K8s跑多了就会明白Pod这个画蛇添足的设计恰恰是K8s最天才的决策之一。本文从如果K8s直接管容器会怎样讲起层层递进把Pod的前世今生、Pause容器的秘密、单容器和多容器Pod的区别、以及Sidecar等三大容器模式全部讲透。读完这篇你对Pod的理解会从一个跑容器的盒子升级到一个精心设计的进程组抽象。一、为什么K8s不直接管容器——如果它管了会怎样我们先做个思想实验假设K8s最小调度单元就是单个容器不要Pod这一层了。# 假设的无Pod版K8s配置这玩意不存在apiVersion:v1kind:Container# ❌ 没有这种东西metadata:name:nginxspec:image:nginx:1.25---# 那nginx的日志收集容器怎么办# 它得跟nginx共享文件系统才能读到日志啊# 如果没有Pod做共享容器组这俩容器就得手动绑在一起问题来了有些服务天然需要一组亲密无间的容器一起运行——它们共享网络、共享存储、生死与共。这就是Pod的设计起点。【如果K8s直接管容器 vs 管Pod】 ❌ 直接管容器 ✅ 管Pod ┌────────┐ ┌────────┐ ┌─────────────────────┐ │nginx │ │fluentd │ │ Pod │ │容器1 │ │容器2 │ │ ┌───────┐┌───────┐ │ └────────┘ └────────┘ │ │nginx ││fluentd│ │ │ │ │ │:80 ││收集日志│ │ │ ? 怎么共享网络 ? │ │ └───┬───┘└───┬───┘ │ │ │ │ │ │ │ ┌───┴────────────┴───┐ │ 共享IP共享存储卷 │ │ 需要额外的粘合层... │ └─────────────────────┘ └────────────────────┘ Pod 解决了亲密容器组 调度粒度难以表达一组亲密容器 的调度问题 的共生关系和共享资源 • 同一Pod内的容器一定在同一台 Node上 • 它们共享 localhost 网络 • 它们共享 Volume 挂载 • 它们共享 PID 命名空间可选要点Pod不是K8s多此一举的设计而是对真实世界需求的精准抽象。很多应用需要多个亲密协作的进程Pod就是把它们打包成一个最小调度单元的机制。你从来不会调度半个Pod——调度器永远以Pod为单位做决策。二、Pod里到底能共享什么——三个共享空间Pod里的容器共享三样东西这是Pod区别于几个独立容器的关键2.1 共享网络命名空间——“同屋同网”同一Pod里的所有容器共享同一个IP地址、同一个端口空间。这意味着容器A可以通过localhost访问容器B的端口容器A和容器B的端口不能冲突都在同一个127.0.0.1上它们共享同一个网络设备网卡、路由表【Pod内网络共享示意图】 ┌─────────────────────────────────────────────┐ │ Pod │ │ IP: 10.244.1.5 │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ nginx 容器 │ │ log-agent 容器 │ │ │ │ 监听 :80 │ │ 需要访问 :80 │ │ │ │ │ │ 它怎么访问 │ │ │ │ │ │ 直接 localhost │ │ │ │ 共享网络栈 ◄────┼─┼── :80 ! │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ 同一个 lo 网卡, 同一个 eth0, 同一个 IP │ └─────────────────────────────────────────────┘ 对外Pod 只有一个 IP流量先到 Pod 再路由到具体容器 对内容器之间用 localhost:port 即可通信# 在同一个Pod的两个容器里分别执行# 容器Anginx监听80# 容器Blog-agentcurl localhost:80# ✅ 完美通因为它们共享网络命名空间curllocalhost:802.2 共享存储卷——“共用一个冰箱”Pod里可以定义VolumePod内的所有容器都能挂载这个Volume。这就是日志收集、文件共享等场景的实现基石# 经典组合nginx写日志 Filebeat收日志apiVersion:v1kind:Podmetadata:name:nginx-with-log-collectorspec:volumes:-name:nginx-logsemptyDir:{}# Pod生命周期内存在的临时卷containers:-name:nginximage:nginx:1.25volumeMounts:-name:nginx-logsmountPath:/var/log/nginx# nginx把日志写到这里-name:log-collectorimage:filebeat:8.0volumeMounts:-name:nginx-logsmountPath:/logs# filebeat从这里读日志readOnly:true# 只读挂载安全要点emptyDir是Pod生命周期内存在的临时卷——Pod删了它就没了。同一Pod内的容器挂载同一个emptyDir就能共享文件。这正是Sidecar模式实现日志收集等技术的基础。2.3 共享PID命名空间可选——“你知道我在干啥”默认情况下Pod里的容器看不到彼此的进程。但你可以通过shareProcessNamespace: true让它们共享PID命名空间apiVersion:v1kind:podspec:shareProcessNamespace:true# 开启PID共享containers:-name:appimage:myapp:latest-name:debugimage:busyboxcommand:[sleep,3600]# 在debug容器里直接看app容器的进程kubectlexec-itapp-with-debug-cdebug --psaux# PID USER COMMAND# 1 root /pause # Pause容器后面讲# 7 root /usr/local/bin/myapp# 15 root sleep 3600# 可以相互看到进程甚至可以发信号三、Pause容器——Pod里最低调的主角每个Pod启动时第一个创建的容器不是你定义的那些业务容器而是一个叫Pause容器也叫Infrastructure Container或Sandbox Container的小玩意儿。它默默地躲在Pod里不干别的只做一件事撑起Pod的网络命名空间。【Pause容器——Pod网络的房梁】 ┌─────────────────────────────────────────────┐ │ Pod看作一个小房子 │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ Pause 容器房梁/框架 │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ 我创建了网络命名空间 │ │ │ │ │ │ 所有业务容器都加进来 │ │ │ │ │ │ 我一直活着你们挂了我还在 │ │ │ │ │ └─────────────────────────────┘ │ │ │ └─────────────────────────────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │ nginx │ │ log-agent│ │ │ │ 容器 │ │ 容器 │ │ │ └──────────┘ └──────────┘ │ │ │ │ │ │ └───────┬───────┘ │ │ ▼ │ │ 都加入Pause的网络命名空间 │ └─────────────────────────────────────────────┘Pause容器的镜像极小大约700KB它做的事情极其简单——运行一个永远不退出的小程序本质上就是个sleep infinity。但就是这个小东西解决了Pod网络共享的核心问题要点Pause容器是Pod网络栈的房东。它先创建网络命名空间并配置好IP然后所有业务容器通过Docker/containerd的--netcontainer:pause_id模式加入这个命名空间。这样不管业务容器怎么启停Pod的IP都不会变——因为IP是挂在Pause容器上的。用代码来理解Pause容器的极简生活# Pause容器做的事情伪代码whiletrue;dosleep30# 啥也不干就活着done# 它的价值不在于做了什么而在于提供了什么——# 提供网络命名空间、IPC命名空间、PID命名空间可选四、单容器Pod vs 多容器Pod——什么时候多放一个4.1 单容器Pod——“一个人住”这是最常见的情况。一个Pod里只有一个业务容器apiVersion:v1kind:Podmetadata:name:single-container-podspec:containers:-name:nginximage:nginx:1.25ports:-containerPort:80要点即使Pod里只有一个容器Pause容器依然在那你用docker ps在Node上看会看到一个/pause容器和一个你的业务容器。这是正常的别以为是Bug。4.2 多容器Pod——“合租模式”下面这张表帮你判断这个额外的容器应该放在同一个Pod里还是应该单独搞一个Pod判断维度放同一个Pod单独建Pod需要共享localhost网络✅ 同一Pod内共享❌ 各Pod独立IP需要共享存储卷✅ Pod内Volume共享❌ 得用PVC/NFS等生命周期必须绑定✅ 同生共死❌ 各活各的需要独立扩缩容❌ Pod是最小调度单元✅ 可以各自scale需要独立健康检查❌ Pod级健康✅ 独立探针【判断流程图】 这个辅助容器需要跟主容器共享网络吗 │ ┌──┴──┐ │ 是 │──► 放在同一个 Pod 里 ✅ └─────┘ │ ┌──┴──┐ │ 否 │ └─────┘ │ 它们需要严格的生命周期耦合吗 │ ┌──┴──┐ ┌──────────┐ │ 是 │──► 同一个Pod │ 否 │──► 分开建 Pod ✅ └─────┘ └──────────┘五、多容器Pod的三大经典模式——Sidecar/Ambassador/AdapterK8s社区总结出了多容器Pod的三种经典设计模式理解这三个模式你就能看懂99%的多容器Pod了。5.1 Sidecar模式——“主服务 小跟班”最最常见的模式。主容器干正事Sidecar容器打辅助——收日志、同步文件、热加载配置等等。【Sidecar 模式】 ┌─────────────────────────────────────────────┐ │ Pod │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ 主容器 │ │ Sidecar │ │ │ │ 干正事 │ │ 打辅助 │ │ │ │ nginx:80 │ │ 收日志/热重载 │ │ │ └──────────────────┘ └──────────────────┘ │ │ ▲ ▲ │ │ │ 共享卷 │ │ │ └──────────────────────┘ │ │ │ │ 经典组合 │ │ • nginx filebeat日志收集 │ │ • app config-reloader配置热加载 │ │ • app istio-proxy服务网格代理 │ │ • app vault-agent密钥注入 │ └─────────────────────────────────────────────┘# Sidecar 实战应用 Istio 服务网格代理apiVersion:v1kind:Podmetadata:name:app-with-sidecarannotations:sidecar.istio.io/inject:true# Istio自动注入spec:containers:-name:myappimage:myapp:v2.0ports:-containerPort:8080-name:istio-proxy# Istio自动注入的Sidecarimage:istio/proxyv2:1.20# istio-proxy 劫持所有出入流量实现服务网格功能# 你的应用不需要任何改动就能拥有熔断、限流、追踪能力5.2 Ambassador模式——“外交官代理”主容器不直接跟外部服务打交道而是通过一个Ambassador容器做代理——类似外交部发言人的角色。【Ambassador 模式】 外界请求 │ ▼ ┌─────────────────────────────────────────────┐ │ Pod │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Ambassador │ │ 主容器 │ │ │ │ 对外打交道 │ │ 只管内部逻辑 │ │ │ │ localhost:6379 │ │ 连 localhost │ │ │ │ ↓ │ │ 不关心外部差异 │ │ │ │ 实际连Redis集群 │ │ │ │ │ └──────────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────┘ 你的应用始终连 localhost:6379 Ambassador 负责把请求转发到真正的 Redis 地址 换 Redis 集群地址改 Ambassador 就行应用代码不用动# Ambassador 实战应用通过Twemproxy连Redis集群apiVersion:v1kind:Podmetadata:name:app-with-ambassadorspec:containers:-name:myappimage:myapp:v1.0env:-name:REDIS_HOSTvalue:localhost# 永远连localhost-name:REDIS_PORTvalue:6379-name:twemproxy# Ambassador代理Redis请求到真实集群image:twemproxy:latestports:-containerPort:6379env:-name:REDIS_SERVERSvalue:redis-cluster-1:6379,redis-cluster-2:63795.3 Adapter模式——“翻译官/格式转换器”Adapter模式做的是数据格式转换——把主容器的输出翻译成下游能理解的格式。【Adapter 模式】 ┌─────────────────────────────────────────────┐ │ Pod │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ 主容器 │ │ Adapter │ │ │ │ 输出自定义格式 │ │ 翻译成标准格式 │ │ │ │ metrics: :9090 │ │ http://:8080 │ │ │ │ 特殊日志格式 │ │ /metrics │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ ▲ │ │ │ 共享网络 │ │ │ └──────────────────────┘ │ │ │ │ Prometheus 从 Adapter 拉标准格式的指标 │ │ 主容器不需要知道自己跑在Prometheus环境里 │ └─────────────────────────────────────────────┘要点这三种模式的共同思想是——让每个容器只做一件事通过Pod把它们粘合在一起。这遵循了Unix哲学Do one thing and do it well也让每个容器镜像更小、更专注、更安全。六、Pod的一生——从出生到死亡的状态机Pod不是什么永恒的存在它有自己完整的生命周期【Pod 生命周期状态机】 kubectl create / apply │ ▼ ┌──────────┐ │ Pending │ ← 我存在了但还没跑起来 │ │ 可能在等调度、拉镜像中 └────┬─────┘ │ Scheduler分配了Node 镜像拉成功 ▼ ┌───────────────┐ │ Running │ ← 我跑起来了 │ │ 至少一个容器在运行 └───┬───────┬───┘ │ │ 容器退出 │ │ Pod 被删除 可重启 │ │ (kubectl delete pod) ▼ ▼ ┌──────────┐ ┌──────────┐ │ 等待重启 │ │Terminating│ ← 正在收尾... │(CrashLoop)│ └────┬─────┘ └──────────┘ │ │ ▼ ▼ ┌──────────┐ 回到 Running │ 删除完成 │ (重新启容器) │ 彻底消失 │ └──────────┘# 查看所有Pod以及它们的状态kubectl get pods-owide# 看某个Pod的详细状态kubectl describe pod nginx-pod# 看Pod生命周期中的关键事件kubectl get events --field-selectorinvolvedObject.namenginx-pod要点Pod的Running状态只代表至少有一个容器在运行不保证所有容器都健康。要精确判断容器是否正常得靠健康探针Liveness/Readiness Probe——这个会在后续文章里详细讲。本篇小结Pod是K8s里最基础也最容易被忽视的设计精华为什么要有Pod因为容器太细了——有些进程组天然就该一起跑Pod就是给它们包的外卖盒子Pause容器是隐形的房东它创建并持有Pod的网络命名空间业务容器来来去去但Pod的IP稳如泰山多容器Pod的三大模式Sidecar打辅助、Ambassador代理外部、Adapter翻译格式——记住这三个模式你就能看懂任何多容器Pod一个Pod一个IP容器间用localhost通信——这是理解Pod网络的基础下一篇咱们实战拆解Pod的YAML配置把每一行字段的作用都给你讲明白——从此写Pod YAML不再靠猜。上一篇【第07篇】K8s架构全景图——Master和Node的天作之合下一篇【第09篇】Pod的YAML从入门到精通——每个字段都是干什么的

相关新闻