ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Kubernetes Pod 完全指南:从核心概念到排障实战

Kubernetes Pod 完全指南:从核心概念到排障实战

开篇直接说:Pod 这玩意儿,是 Kubernetes 里最小的调度单元,也是大部分人入门 K8s 时第一个真正需要啃下来的概念。你可能已经知道它"相当于一组容器的集合",但真到排障和调优的时候,光有这一层认知远远不够——我见过不少朋友在面试和实际开发里,把 Pod 当成"就是容器"来理解,结果一遇到重启策略、探针失效、容器启动顺序这种实际问题,直接就懵了。这篇文章我准备把自己从懵到通的过程梳理一遍,从最小的概念讲起,一直讲到资源配额、探针配置、调度约束和排障思路,争取让你看完之后,面对一个异常 Pod 时,不再只是会kubectl describe和kubectl logs。

适合谁看?刚接触 K8s 的开发、运维、测试都适合;如果你已经写过几个 Deployment YAML,但说不清 Pod 和 Deployment 的关系、搞不明白探针参数怎么调,那这篇文章同样值得花二十分钟过一遍。

1. Pod 到底是什么:先搞清楚这个最小的调度单元

1.1 为什么不直接调度容器,非要包一层 Pod

Kubernetes 之所以没有直接把单个容器作为调度单位,核心原因在于:有些场景下,多个进程必须部署在同一台机器上,共享网络栈和存储,还要能够互相协作。你写业务的时候可能觉得"一个容器只跑一个进程"很干净,可真到了实际业务里,你会遇到三类非常典型的需求:

第一类是日志代理场景。业务容器把日志写进文件,旁边需要一个 sidecar 容器专门采集日志并转发出去。如果这是两个独立的容器,它们之间的文件系统隔离会让这个组合很难实现——要么把日志通过远程接口传,要么就得引入额外的共享存储方案,复杂度一下子就上来了。

第二类是网络代理场景。比如业务容器监听 localhost,你需要一个代理容器帮它接收外部流量。这类组合要求容器之间能通过 localhost 直接通信,这只有在它们共享同一个网络命名空间时才能实现,而 Pod 提供的正是这一点。

第三类是初始化场景。应用启动前需要拉取配置、初始化数据库结构、等待依赖服务就绪,这些任务做完之后,主容器才开始运行。如果让主容器自己处理,代码就得写一堆 bash 脚本判断依赖;更优雅的做法是让一个专门的初始化容器先跑,跑完退出,主容器再接管。

把这三种需求凑到一起,你会发现"容器组"这个概念几乎是必然产物。Pod 的作用就是给这一组容器提供共享环境:同一个网络命名空间(共享 IP 和端口)、共享的存储卷、同时被调度到同一台节点。说白了,Pod 内部容器之间可以通过 localhost 访问,对外暴露的是一个统一的 IP 地址。

1.2 每个 Pod 里都有个"隐形"的 pause 容器

你如果用docker ps查看节点上的容器列表,会发现每个 Pod 对应一个名字里带pause(或者sandbox)的容器。很多初学者第一次看到会以为是系统自动创建的垃圾容器,其实它是 Pod 的地基。

pause 容器在 Pod 创建时第一个启动,主要负责两件事:一是持有 Pod 的网络命名空间,二是作为"收养孤儿进程"的父进程。业务容器启动时加入的就是 pause 容器创建的网络命名空间,因此整个 Pod 共享同一个 IP 和端口空间。而容器内进程在退出时,如果还有子进程没有回收,这些子进程会被挂到 pause 名下,避免它们变成没人管的僵尸进程。

这个小细节平时用不上,但排查"localhost 访问不到"或者"容器退出后网络异常"的时候,就会回来感谢它——建议你至少在某个节点上手动看一下容器列表,把 Pod 的容器结构理解成"pause + 业务容器 + 其他辅助容器"这个模型,后面很多概念都会顺很多。

在写 YAML 的时候,你不需要手动定义 pause 容器,Kubernetes 会自动注入。你看到的 Pod 定义里通常只有业务容器和辅助容器,实际运行时会多出这个隐形的 pause,这个认知偏差如果不纠正,看监控数据的时候会很困惑。

2. Pod 的生命周期与状态机

2.1 从创建到消失要经过哪些状态

Pod 的状态不像传统运维里"启动-运行-停止"那么简单,它有一整套状态机。最常见的有五种:Pending、Running、Succeeded、Failed、Unknown。另外还有一个 CrashLoopBackOff,它其实不是 Pod 状态,而是容器状态的展示,很多人会混淆。

Pending 阶段是调度的白热化时期。你提交了一个 Pod 定义,调度器正在为它寻找合适的节点:资源够不够、有没有满足 nodeSelector 的节点、节点是否处于 Ready 状态。如果资源不足或没有匹配的节点,Pod 会一直卡在 Pending。这个阶段最容易踩的坑是:集群里有节点,但节点上标记了污点(taint),而你的 Pod 没有对应的容忍(toleration),调度器会直接无视这些节点。

Running 阶段表示 Pod 已经被成功调度并绑定到了某个节点,至少有一个容器还在运行。注意"Running"不代表你的业务一定正常——容器可能正在反复崩溃重启,但只要它在循环中(比如 CrashLoopBackOff 间歇期),Pod 状态仍然是 Running。我第一次看kubectl get pods看到 STATUS 是 Running 就觉得万事大吉,结果业务方说服务完全不可用,后来才发现容器每三十秒崩一次,这个教训记忆深刻。

Succeeded 和 Failed 都对应"任务型 Pod"(跑完就退出的批处理任务)。如果 Pod 里的所有容器都正常退出,而且退出码为 0,Pod 进入 Succeeded;如果只要有容器以非 0 退出码结束,Pod 就变成 Failed。普通 Web 服务型 Pod 很少主动进入 Succeeded,除非你故意让容器主进程退出。

Unknown 状态比较冷门,它表示 API Server 与节点上的 kubelet 失联了,无法获取 Pod 的最新状态。常见原因包括节点宕机、网络分区或者 kubelet 假死。遇到 Unknown 别慌,先看节点状态和 kubelet 日志,多数情况下是节点的问题而不是 Pod 本身的问题。

2.2 restartPolicy 选错会让你的排障思路绕远路

restartPolicy 是 Pod 级别的字段,可选值只有三个:Always、OnFailure、Never。它决定的是"kubelet 在容器退出后是否帮你重启容器",而不是"应用崩溃后 K8s 怎样恢复整个 Pod"。

默认值是 Always。这意味着即使你的任务型容器跑完并正常退出(退出码 0),kubelet 也会启动一个新的容器实例替代它。如果你写的是 Job,正常情况下应该让 Job 控制器和 restartPolicy 协同配合;如果你只是手动创建了一个裸 Pod 去跑一次性任务而忘了设置 Never,你会发现任务明明完成了,Pod 却一直在那里反复启动新容器,资源白白消耗。

OnFailure 意味着只有退出码非 0 才重启,经常和 Job 配合使用。Never 则完全不重启,适合真正的任务型场景:失败了就让记录留在那里,后续由人工或上层控制器处理。

实际项目里,托管型工作负载(Deployment 管理的 Pod)几乎都是 Always,因为控制器需要保证副本数;Job 型任务通常用 OnFailure 或 Never。有一个很容易搞混的点是:restartPolicy 只影响当前节点上 kubelet 的重启行为,不会触发 Pod 重新调度。如果节点本身挂了,Pod 是否被重新创建在其他节点上,取决于它有没有被 Deployment、StatefulSet 这类控制器管理。

3. 从零创建一个 Pod:最常用的三种方式

3.1 kubectl run 的快速上手与局限

如果你只是想在测试集群里快速起一个 Nginx,kubectl run nginx --image=nginx是最快的方式。它会在当前命名空间创建一个 Deployment(新版 kubectl 默认行为),然后帮你生成一个副本。

但这种方式有个明显的坑:它产生的 YAML 是隐式生成的,你几乎无法控制探针、资源限制、环境变量等关键配置。我建议你把kubectl run当成"冒烟测试"工具来用,真正的业务 Pod 一定要写 YAML,哪怕一开始从简单开始,也要把配置项显式化。另一个实用技巧是:用kubectl run nginx --image=nginx --dry-run=client -o yaml生成初始 YAML,导出来再往里面补充配置,这样比手写全量 YAML 要省事得多。

3.2 用 YAML 定义 Pod:最小可运行模板

下面这个 YAML 是创建单个裸 Pod 的最小模板,我建议你把它存成自己的脚手架文件:

apiVersion: v1 kind: Pod metadata: name: web-demo labels: app: web-demo env: dev spec: containers: - name: web image: nginx:1.25 ports: - containerPort: 80 name: http restartPolicy: Always

把这个文件存成pod.yaml,执行kubectl apply -f pod.yaml即可创建。这里有几个细节值得注意:

第一,metadata.labels不是可选项。虽然不写也能创建,但后续 Service、Deployment、监控系统都靠标签来选择 Pod,没有标签等于把 Pod 丢进了"无法被管理"的角落。

第二,ports.containerPort只是声明,不是端口映射。容器监听 80 端口并不是因为你写了这段配置,而是因为 Nginx 本身就监听 80。这段声明的作用更接近"元数据",方便其他人读 YAML 了解意图,某些云平台也会用它生成负载均衡规则。很多新手误以为不写这段就不会暴露端口,其实 Pod 的网络模型是从共享网络命名空间继承的,端口本来就通。

第三,kubectl apply和kubectl create有区别。apply是声明式的,会记录上次应用的配置,适合反复更新同一个文件;create是命令式的,对已存在的同名资源会直接报错。日常使用优先apply。

3.3 为什么实际项目很少直接创建裸 Pod

裸 Pod 有一个致命问题:如果它所在的节点挂了,kubelet 无法把它迁移到别的节点上,这个 Pod 就永远消失了。Kubernetes 的设计哲学是让控制器来管理 Pod 的生命周期,而不是让人直接操作 Pod。

所以实际项目中,你几乎不会用kind: Pod直接创建工作负载,而是用 Deployment。Deployment 管理 ReplicaSet,ReplicaSet 维护期望的副本数并创建 Pod。它们的层级关系是:Deployment → ReplicaSet → Pod。你在 YAML 里写的spec.template就是 Pod 模板,Deployment 用这份模板生成 ReplicaSet,ReplicaSet 再根据模板实例化 Pod。

这个三层结构不是多余的设计,它解决了一个核心问题:滚动更新。当你更新 Deployment 的镜像版本时,Deployment 会创建一个新的 ReplicaSet,逐渐增加新副本数、减少旧副本数,整个过程对你配置的 Pod 模板完全透明。如果你操作的是裸 Pod,想做到不停机更新只能手动一个接一个地删建,效率和安全性都差很多。

4. Pod 实战进阶:健康检查探针的正确打开方式

4.1 三种探针,功能边界必须分清楚

K8s 提供了三种探针:livenessProbe(存活探针)、readinessProbe(就绪探针)、startupProbe(启动探针)。它们分别回答三个不同的问题:容器还活着吗?容器能接收流量吗?容器启动完成了吗?

livenessProbe 失败会让 kubelet 杀掉容器并按 restartPolicy 重启。它的职责是兜底"死循环、死锁"这类进程还在但业务已经不可用的问题。比如你的 Java 应用发生线程池耗尽,进程本身还活着,但已经无法处理请求,这时候 liveness 探针能把它拉回来。

readinessProbe 失败不会重启容器,而是把 Pod 从 Service 的 Endpoints 列表中摘除,让流量不再打到它。等探针恢复成功,Pod 会自动重新加入流量池。这个探针特别适合"应用已启动但依赖的数据库还没连上"的场景——进程起来了,但你不能让它接流量。

startupProbe 是后加入的探针,专门解决慢启动应用的存活误杀问题。如果你的应用需要 60 秒才能完成启动,而 liveness 探针的第一轮检查在启动 10 秒后就执行了,还没等应用就绪就把它杀了。正确的做法是给慢启动应用配置一个 startupProbe,在它成功之前,liveness 和 readiness 探针都不会执行,等 start 完成后再交棒给另外两个探针。

理解了三者的分工,配置顺序就清楚了:先配 startupProbe 兜住慢启动,再配 readinessProbe 控制流量接入,最后用 livenessProbe 做兜底防护。

4.2 探针参数:从一次误杀事故说起

我曾遇到过一个服务在高峰期频繁重启,查到最后发现是 livenessProbe 的超时时间设置得太短。HTTP 请求在高峰期响应要 3 秒,而探针的timeoutSeconds只给了 1 秒,kubelet 每次请求都超时,连续三次失败就把容器杀了。这个案例说明,探针参数不能随便抄模板。

常用的探针参数包括:

  • initialDelaySeconds:容器启动后等多长时间才开始第一次探测,给应用留启动窗口。
  • periodSeconds:探测频率,默认 10 秒。
  • timeoutSeconds:单次探测的超时时间,默认 1 秒。
  • successThreshold:连续成功多少次才认为容器健康,默认 1 次。
  • failureThreshold:连续失败多少次才触发对应动作(杀容器或摘流量),默认 3 次。

一个比较稳妥的 HTTP 探针配置长这样:

livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 15 timeoutSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 1

这里有个经验点:readiness 的failureThreshold设置成 1 意味着一次探测失败就摘流量,适合对抖动敏感的服务;而 liveness 的阈值通常要稍微宽松一点,因为误杀一个健康容器比短暂保留一个亚健康容器代价更高。

我最早踩过的另一个坑是探针路径。很多人把 liveness 和 readiness 指向同一个接口,看起来省事,实际上违背了它们的设计意图:liveness 应该是一个"只要进程活着就能返回 200"的极简接口,readiness 才应该检查依赖资源(数据库、缓存、下游服务)是否可用。如果你让两个探针都去检查数据库,数据库抖动时 liveness 失败导致容器被反复重启,问题会被放大而不是被缓解。

5. 多容器 Pod 与 Init 容器的正确用法

5.1 Sidecar 模式:为什么要把两个容器放在同一个 Pod

多容器 Pod 最常见的就是 sidecar 模式。主容器负责业务逻辑,sidecar 容器负责辅助能力。除了最典型的日志采集,还有几个很实用的场景值得提一下:

文件同步场景。一个容器把远程文件同步到本地共享目录,另一个业务容器消费这些文件。它们共享同一个 volume,通过文件系统协作,主容器代码里不需要任何远程调用的逻辑。

代理转发场景。业务容器只监听 localhost,sidecar 容器承担对外端口监听和转发。比如一个 Python 应用不擅长处理并发连接,旁边放一个 Nginx sidecar 做反向代理,整个 Pod 对外只暴露 Nginx 的端口。

安全加固场景。主容器里不放 SSH 和调试工具,sidecar 容器单独承担调试和运维操作。这样攻击面缩小了,主镜像可以做到尽量精简,调试工具集中在 sidecar 里。

多容器共享同一 IP 和端口空间意味着端口冲突是真实存在的:两个容器不能用同一个端口监听 localhost。设计时要提前约定好每个容器使用的端口范围,避免 A 容器写了 8080,B 容器也想用 8080 的情况。

5.2 Init 容器:先决条件由它来完成

Init 容器是排在业务容器之前运行的专用容器,它的特点是:必须全部执行成功,Pod 才会进入后续的容器启动流程;任何一个 Init 容器失败,整个 Pod 会被重启或重建。

典型的用法包括:等待依赖服务就绪(比如轮询数据库端口);拉取配置并写入共享卷;执行数据库迁移脚本;把某个目录的权限和所有权改好。Init 容器跑完后就退出,不会长期占用资源。

Init 容器的特殊之处在于,如果集群里已经有正在运行的主容器,你新增或修改 Init 容器配置,Pod 不会热更新,而是会被重建,因为 Init 容器必须在主容器启动前执行。这个行为像一个隐性约束,改 Init 容器配置前要有"Pod 会重启"的心理准备。

另外要注意,Init 容器的资源申请和主容器是分开计算的。调度器在判断节点容量时,会把所有 Init 容器中最大的资源请求值与主容器资源请求相加,所以 Init 容器如果写得贪心,可能导致 Pod 无法被调度到合适的节点。我之前见过有人给 Init 容器申请了 8 核 16G,而主容器只需要 2 核 4G,结果整个集群没有节点放得下这个 Pod,排查了半天才发现是 Init 容器吃掉了调度预算。

6. Pod 的资源管理与调度约束

6.1 requests 和 limits:理解配额得先弄清这三个概念

Kubernetes 的资源模型里,requests 和 limits 分别代表"最低保障"和"最高上限"。requests 是调度依据,调度器会检查节点剩余的可用资源是否满足所有 Pod 的 requests 总和;limits 是运行时上限,超出会触发 CPU 节流或 OOM 杀死容器。

CPU 和内存的行为完全不同,很多人在这里栽跟头。CPU 是可压缩资源:超过 limits 时容器会被限流(throttling),表现为性能下降但不会死。内存是不可压缩资源:超过 limits 时 kubelet 会触发 OOM Kill,直接干掉容器。所以在配置内存时,limits 一定不能设置过低,否则容器会在达到真正危险水位之前被系统杀掉。

一个在实际项目中验证过多次的建议:如果不知道怎么配,先把 requests 设成你观测到的稳定使用量的 70%~80%,把 limits 设为 requests 的 1.5~2 倍,但要留出明确业务特征需要更精细配置的空间。设置过高的 limits 意味着承诺了超出实际需要的资源上限,节点上的资源会被 Pod 的"额度"占住,调度器按 requests 而不是实际使用量来判断容量,所以 requests 设置比实际需要高得多,是集群资源利用率低的头号原因。

6.2 调度约束:nodeSelector、亲和性与污点容忍

Pod 调度到哪个节点,默认由调度器按资源余量和评分策略决定。但有些场景需要你主动干预,比如:某些节点有 GPU,某些节点挂了 SSD,某些业务要求和其他业务物理隔离。

最基础的是 nodeSelector,它要求 Pod 只调度到包含指定标签的节点。比如给 GPU 节点打标签gpu=true,Pod 里写上nodeSelector: gpu: "true"就行。它简单直接,但表达不了复杂逻辑,比如"尽量调度到 GPU 节点,但 GPU 节点满了也可以放到普通节点"。

更灵活的是 nodeAffinity。它支持 requiredDuringScheduling 和 preferredDuringScheduling 两种语义,前者是硬性要求,后者是软性偏好。硬性要求和 nodeSelector 类似,但表达式丰富得多;软性偏好则在调度评分阶段给满足条件的节点加分,不满足也不会调度失败。实际项目里,preferred语义用得很多,因为它能提高调度命中率,又不会因为个别节点标签不一致导致 Pod 无法调度。

污点(Taint)和容忍(Toleration)是另一对概念。污点标记在节点上,表示"这个节点有特殊属性,普通的 Pod 不要来";容忍写在 Pod 上,表示"我能接受这个污点,我可以来"。这对机制常用来实现节点独占:给专用节点打上污点,只有带对应容忍的 Pod 才能调度过去。常见的污点效果有三种:NoSchedule(不调度新 Pod)、PreferNoSchedule(尽量不调度)、NoExecute(不仅不调度,还会驱逐已经运行在上面的 Pod)。NoExecute 会影响正在运行的 Pod,修改时一定要谨慎,否则一个误操作会让节点上的存量 Pod 全部被赶走。

7. 常见问题与排查技巧实录

7.1 ImagePullBackOff:镜像拉取失败的完整排查路径

如果你看到一个 Pod 卡在 ImagePullBackOff,第一反应别急着kubectl delete,先执行kubectl describe pod <pod-name>。在 Events 一段里,会明确显示失败原因。最常见的几个原因分别是:镜像名或标签打错、私有仓库需要认证而 Pod 没有配置 imagePullSecret、仓库地址从集群节点网络不可达。

镜像标签错误是最容易自查的,直接在节点上手动docker pull验证一下。私有仓库认证问题,需要给 Pod 的spec.imagePullSecrets配置对应的 Secret。网络不可达的问题,要检查节点是否能访问镜像仓库域名。如果集群用的容器运行时是 containerd,记得用crictl images而不是docker images去查节点上的镜像缓存,二者管理的数据不一致。

ImagePullBackOff 还有一个隐藏细节:拉取失败的次数会有一个指数退避的过程,一开始可能是几秒,慢慢增加到几分钟。如果你只是修改了 imagePullSecrets 之后马上重试,会发现状态仍然没有立刻变化,有一部分原因是回到了退避周期。此时可以等待,或者重新创建 Pod 来跳过等待。

7.2 CrashLoopBackOff:别再只会看 logs

CrashLoopBackOff 表示容器启动了又退出,反反复复。很多人第一步就是kubectl logs,但这个路径其实有盲区:如果容器根本没有成功启动到业务日志输出的阶段,logs 可能是空的,这时候你要去看的是上一轮启动失败的具体原因。

排查顺序建议是:先看kubectl describe pod的 Events 和 State,确认退出码;再看 logs 和 Previous 日志(kubectl logs <pod> --previous);如果要看容器进程是被 OOM 杀死的,在 describe 输出里会有OOMKilled: True的提示。退出码是一个重要的信号:137 通常是 OOM 或者容器被外部 kill,127 是命令找不到,1 或 2 一般是你自己业务代码的非 0 退出。把退出码和日志结合起来看,比盲目看日志高效得多。

常见的一个坑是:探针配置不当导致容器被杀。你的业务进程其实运行正常,但 liveness 探针路径返回的不是 2xx,kubelet 判断存活失败就杀容器。这种崩溃是"自杀式"的——日志里看不到业务异常,只有探针失败的痕迹,所以排查时别忘了把探针配置纳入检查范围。

7.3 Pod 一直 Pending:调度器的无声拒绝

Pod 停在 Pending 通常意味着调度器没有为它找到合适的节点。kubectl describe pod的 Events 里会写 "0/5 nodes are available",后面跟着一串原因。最常见的原因是:节点资源不足、节点标签不满足 nodeSelector、节点存在无法容忍的污点、PVC 没有绑定成功。

资源不足时,Events 会告诉你哪个节点资源不够,以及缺口多大。此时要么释放节点资源,要么缩减 Pod 的 requests。被污点挡住时,Events 会列出 "matched node" 但标注有未容忍的污点,解决方式是给 Pod 添加对应的 toleration,或者确认这个节点确实不该被使用。PVC 未绑定也是容易被忽视的对象:Pod 引用了 PVC,但如果 StorageClass 不存在或卷没创建成功,调度器也不会真正开始调度。

一个比较隐蔽的 Pending 原因是调度器本身的问题。如果你自建集群,曾经遇到过scheduler组件没有正常工作,结果是所有新 Pod 全部 Pending,describe 里连调度失败的提醒都没有——因为调度器压根没处理这些 Pod。排查到这里的时候,需要看一眼控制平面组件的健康状况。

8. 几个踩过坑之后沉淀下来的实用习惯

最后分享几个我在实际项目中反复用到的习惯,不一定写在文档里,但真的能帮你省很多事。

第一个习惯:改动配置之前先导出当前 YAML。kubectl get pod <name> -o yaml > backup.yaml在做任何调整前做一份备份。尤其是 Pod 已经被上层控制器管理的情况下,手动修改 Pod 字段基本都会被控制器覆盖回去,这时候你需要的不是改 Pod,而是改 Deployment。先看这个 Pod 是哪个控制器管着的,kubectl get deploy -A里搜一下或查看 Pod 的metadata.ownerReferences,这个字段直接告诉你它的"老板"是谁,避免白做一顿操作。

第二个习惯:善用kubectl get pod -o wide查看 Pod 被调度到了哪个节点,以及它的内部 IP。排查网络问题时,这个 IP 能帮你快速判断是集群网络的问题还是业务本身的问题。如果你发现 Pod 频繁在节点之间漂移,而又没有明确的调度需求,很可能是某台节点出现了资源碎片,最直接的观测工具是kubectl describe node里的资源统计,别靠猜。

第三个习惯:给 Pod 加上描述性标签。业务、环境、版本、负责人,至少四个维度。成本核算、监控告警、故障分群,都依赖标签。有很多团队一开始图省事不写标签,等规模上来之后,想通过kubectl get pods -l按业务线筛选却发现根本筛选不了,只能在全量列表里人肉找,那个体验相当酸爽。

第四个习惯:探针配置要像代码一样做版本管理。探针参数看起来只是几个数字,但它直接决定服务的可用性。我见过最典型的问题是:上线前所有探针都正常,上线后高峰期因为 timeoutSeconds 设置太短,一小时内连续触发多次重启,服务水平直接腰斩。探针参数应该随应用发布一起评审,把它当成业务代码的一部分来看待。

最后想说的是,Pod 虽然是最小调度单元,但真正把它用明白,需要你理解它在你整个工作负载体系里的位置,以及它和控制器、探针、调度器、资源模型之间的协作方式。这篇文章写的这些内容,都是我实际踩坑之后的总结,希望能让你在学习和排障的路上少走几个坑。如果你在动手过程中遇到了这上面没有覆盖到的怪问题,建议先把kubectl describe pod的输出完整贴给身边同事或社区,很多问题在描述信息里已经有答案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进