这次聊到K8S系列的第七篇,前面的篇幅里我们把集群初始化、工作节点加入、网络方案选型都讲完了,紧接其后的自然是Pod。很多刚接触Kubernetes的人会把Pod和容器画等号,这个理解不算错,但会把后面一系列控制器、调度器、服务发现的行为逻辑全想歪。所以在进入Deployment、StatefulSet这些上层机制之前,先把Pod这个最基础也最关键的调度单元彻底吃透,后面所有编排操作都会顺很多。
这篇文章不只准备讲概念,还会把一份Pod配置从零写到能用,带你手把手过一遍字段、资源限制、探针、多容器共享网络这类高频实战点。同时会结合踩过的几个典型报错,比如failed to create pod sandbox、ImagePullBackOff这类现场,复盘排查思路。不管你是刚装好集群准备跑第一个Pod,还是已经在用Deployment但想回头把底层逻辑补完整,这篇文章都值得花十几分钟读完。
1. Pod到底是什么:别把它当成"一个容器"
1.1 为什么Kubernetes非要再包一层
很多人在理解Pod时卡住,是因为觉得容器已经是"独立运行的最小单位"了。单从运行时角度看确实是这样,但在一个分布式系统里,有些容器之间需要共享同一份网络配置、同一份存储数据,甚至需要彼此通过localhost直接通信——这种情况下如果每个容器都完全独立,反而会增加沟通成本。
Pod这个概念就是为了解决"哪些容器应该被当作一个整体来管理"的问题。Kubernetes把一组共享网络命名空间、共享存储卷、共享IP地址的容器打包成一个调度单位,这个单位就是Pod。你可以把它类比成"同一台物理机上的一组进程",这些进程可以互相用localhost访问,可以读写同一块磁盘,它们共同对外提供一个完整的能力。
一个Pod里的容器共享同一个网络命名空间,意味着它们共享同一个IP、同一组端口空间。也就是说,Pod内不同容器不能监听同一个端口号,这跟两个独立容器各自有IP的情况完全不同。共享同一个存储卷则意味着这些容器可以协同处理同一份数据,比如日志容器写文件、业务容器读文件,这种模式后面会细讲。
1.2 "基础架构容器"才是Pod真正的主心骨
Pod里那些业务容器大家都很熟悉,但很多人没注意到,每个Pod其实还有一个沉默的底色——pause容器,也就是常说的"基础架构容器"或"infra容器"。你在节点上执行docker ps或者crictl ps,总能看见一个名字里带pause的容器,它就是干这个用的。
pause容器的职责非常专一:它在Pod创建时最先启动,负责创建并持有这个Pod的网络命名空间,然后在容器之间共享。除了这点之外它几乎什么也不做,常年处于暂停状态。只要pause容器存活,整个Pod的网络栈就还在,业务容器即使挂掉再重启也不会影响Pod跟外部的连接关系。
这也解释了为什么Pod生命周期和容器生命周期并不等价。Kubernetes真正关心的是Pod能提供对外服务,而不是某个容器是否长期存活。只要Pod还在,容器重启多少次都属于"内部事务"。理解这一点,再去理解后面的存活探针和重启策略就顺理成章了。
1.3 从管理视角看Pod的设计逻辑
从运维角度看,Pod的存在让Kubernetes的资源调度变得高效。调度器只需要根据Pod声明的资源需求,在节点上找到足够的内存和CPU即可,它根本不用关心Pod里面是几个容器、各自用什么镜像。容器数量再多,只要共享同一份资源配额,调度决策就不会失控。
Pod还有一个特性值得特别注意——它是"临时"的、可被替换的。官方文档里用了一个很贴切的词叫"可抛弃的",意思是Pod可以随时被销毁,并且在销毁后由控制器创建新的副本。这套设计带来的好处是,当节点故障、资源紧张或镜像更新时,系统可以无损地通过"删旧建新"完成故障转移。
这个特性也意味着,任何需要持久化的数据都不应该写在Pod内部文件系统里,而是应该通过存储卷挂载到外部。很多新手在Pod里写文件、重启后数据丢失,然后一脸懵,根因就在这。Kubernetes的哲学是:Pod是牛,不是宠物,坏了换一头,而不是拼命救活这头。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三类Pod创建方式与发布模型
2.1 自主式Pod:最原始的创建方式
用一条kubectl run命令或者一个最简YAML文件直接创建的Pod,称为"自主式Pod"。字面意思就是它没人管,生命周期完全由自己决定。如果它因为某种原因被删除或节点故障导致消失,Kubernetes不会自动帮你重建一个。
bash复制kubectl run nginx --image=nginx:1.25 --port=80
这条命令会在当前命名空间里创建一个名为nginx的Pod。看起来很方便,但在生产环境里几乎没人这么用,因为在没有控制器管理的情况下,Pod一旦意外宕机,业务就断了,得不到任何自动修复。自主式Pod更多用在本地调试、临时测试或一次性任务里。
2.2 管理式Pod:控制器与自愈机制
真正承载生产业务的Pod,几乎都是通过控制器创建的。控制器就像一支施工队,它的职责是确保现场始终有指定数量的Pod在运行。Deployment就是最常见的一位"工头",你告诉它"我要3个nginx副本",它会持续盯着,无论哪个副本挂掉还是节点失联,它都会想办法补齐。
其他常见控制器分工各不相同:DaemonSet保证每个节点上都有一个Pod副本,适合日志采集类组件;StatefulSet专门管理有状态应用,给每个Pod稳定网络标识和独立存储;Job和CronJob处理一次性任务和定时任务。这些控制器的存在让Pod从"临时工"变成了"有编制的人员"——挂掉会自动补位,焕然一新。
2.3 通过Dashboard发布一个新Pod服务
不少人在Kubernetes Dashboard上找了半天,没找到"创建Pod"的按钮,然后来问我是不是版本问题。实际上Dashboard确实不推荐你直接创建自主式Pod,而是让你通过创建Deployment来间接生成Pod。在Dashboard的右上角有个"+"号按钮,点开后选择"From form",在里面填入应用名称、容器镜像、服务端口,然后点击部署,系统会自动帮你生成一个Deployment,进而创建Pod副本。
想直接用YAML文件操作也可以,同样点"+"号,选"From file",把写好的Deployment YAML贴进去或者上传文件,Dashboard会调用API创建资源。创建完成后,在"工作负载"菜单里就能看到新Pod的状态,如果镜像拉取中,会有进度提示。
个人建议是,除非你在只有控制台权限的受限环境里,否则尽量用命令行或GitOps方式管理Pod。Dashboard适合查看状态,不适合作为日常变更入口——原因很现实,YAML文件可以版本化、审计、回滚,控制台点击操作却很难追溯历史。
3. 手写一份Pod配置:字段拆解与参数取舍
3.1 一个最简可用的Pod定义
直接看个实际可用的例子,这是我从一个Nginx演示环境里摘出来的:
yaml复制apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
namespace: default
labels:
app: nginx
env: dev
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
protocol: TCP
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
restartPolicy: Always
这里面每个字段都有讲究。apiVersion和kind不用说,声明资源类型。metadata里的labels尤其关键,Service、Deployment都是通过labels来关联Pod的,选错标签后面服务发现全乱套。举例来说,Service的selector如果是app: nginx,而Pod标签写的是nginx: app,那这个Service就永远选不中这个Pod。
spec.containers数组里可以写多个容器,但实战中绝大多数Pod只跑一个主容器。resources字段建议从一开始就养成习惯写全,不写的话Pod虽然能创建,但调度器缺少决策依据,而且容易发生资源争抢导致节点过载。restartPolicy只有三个可取值:Always、OnFailure、Never,默认是Always。需要注意的是restartPolicy针对的是Pod内的所有容器,不是针对Pod本身,所以即使容器退出码是0,只要策略是Always它也会被重启。
3.2 imagePullPolicy与镜像拉取策略
imagePullPolicy控制的是kubelet什么时候去镜像仓库拉取镜像。它有三个值:IfNotPresent表示本地有镜像就用本地的、没有再去拉;Always表示每次都重新拉取;Never表示只允许用本地镜像,拉不到就报错。
有一个很坑的默认行为是:如果镜像tag不是latest,那么imagePullPolicy默认是IfNotPresent;如果是latest或没写tag,默认就是Always。也就是说,你在YAML里写的image是nginx:1.25,但某次测试时要升级镜像,手动把节点上的nginx:1.25本地镜像替换了,Pod并不会主动去仓库拉新版本,而是继续用本地旧镜像。这个行为坑过不少人。
解决方式很简单:要么明确写imagePullPolicy: Always强制每次拉取最新,要么通过修改镜像tag来触发更新,比如从1.25改成1.26。生产环境里我习惯给镜像打具体版本号tag,并搭配IfNotPresent,这样可以减少不必要的网络请求,同时保证环境一致性。唯一例外是调试阶段频繁改代码,用latest + Always会更省心。
3.3 启动命令、探针与优雅退出
在容器的配置里,command和args经常被混淆。command会覆盖Docker镜像里的ENTRYPOINT,args会覆盖CMD。所以如果你只想追加启动参数,就不要写command,只写args即可;一旦写了command,镜像里原有的入口点就会失效,这是个非常常见的坑。
探针是Pod配置里最能体现运维水平的部分。livenessProbe决定容器需不需要重启,readinessProbe决定流量要不要打到这个Pod上。很多人两个探针全配liveness,结果应用启动慢,还没就绪就被探针判定失败,Pod陷入反复重启的恶性循环。
一个稳妥的配置方式是这样的:
yaml复制livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
initialDelaySeconds必须大于应用实际启动时间,否则探测永远失败。readiness探测更频繁一些是合理的,因为它不影响Pod运行,只影响流量接入。至于优雅退出,Pod被删除时默认会先发SIGTERM信号给主进程,然后等待终止宽限期(terminationGracePeriodSeconds,默认30秒)结束后强制SIGKILL。如果你的应用需要做清理工作,记得在代码里处理SIGTERM,并把等待时间调大。
3.4 initContainers的实用场景
initContainers是Pod里的"前置准备容器",它在主容器启动之前按顺序执行,执行完就退出。它和主容器共享存储卷和网络,因此很适合做初始化任务。
一个典型的场景是:主应用启动前需要从Git仓库拉取最新的配置文件到共享卷,或者需要等待数据库就绪。用initContainers写一段shell脚本执行等待逻辑,会比在主容器里内置这些步骤干净得多。另一个常见用法是做数据库迁移,让迁移容器在主容器启动前完成schema变更,这样就不会出现"应用启动太快、数据库表还没建好"的经典问题了。
yaml复制initContainers:
- name: wait-for-db
image: busybox:1.36
command:
- sh
- -c
- >
until nc -z db-service 5432; do
echo "waiting for db...";
sleep 2;
done
- name: run-migration
image: myapp-migrator:1.0
需要注意,initContainers不支持readinessProbe和livenessProbe,它只有成功执行完毕才会进入下一步。如果某个initContainer一直失败,Pod会不断重启initContainer,主容器永远不会启动。排查问题时,kubectl describe看到Pod停在Init:CrashLoopBackOff状态,基本就是initContainer出错。
4. 多容器Pod:共享网络与存储的实际玩法
4.1 Sidecar容器模式
多容器Pod通常不是为了把多个业务塞进一个Pod里,而是围绕主容器扩展附加能力。最常见的模式叫Sidecar,即给主容器配一个或多个辅助容器,辅助容器负责日志收集、流量代理、配置同步这类横切关注点。
比如一个Web应用容器,自身只负责处理HTTP请求,日志采集器作为Sidecar容器放在同一Pod里,通过共享日志目录读取日志并转发到日志平台。好处很明显:主容器镜像可以保持精简,日志采集逻辑可以独立升级而不影响主业务。这个模式天然契合Pod的共享网络、共享卷特性,完全没理由不用。
4.2 多容器共享网络的实际效果
因为同一个Pod里的容器共享同一个网络命名空间,它们之间用localhost就能通信。这个特性在很多代理模式下很有用。比如服务网格里常见的Envoy代理容器与业务容器共存于一个Pod,业务流量先进Envoy,再由Envoy转发给业务容器,两个容器之间就是通过localhost访问的。
多个容器共享同一个Pod IP,所以对外发布服务时,只需要给Pod绑定Service,流量就能按需路由到Pod里合适的端口。需要注意的是,多容器Pod的端口不能冲突,两个容器都监听80就会直接起不来。设计多容器Pod时,每个容器监听不同端口,或者在YAML里显式声明containerPort,便于阅读和排查。
4.3 存储卷在容器间的共享与权限
多容器Pod共享存储卷是另一个杀手级应用。你可以在Pod级别声明一个emptyDir卷,然后挂载到不同容器的不同路径。emptyDir的生命周期和Pod一致,Pod删了它也就没了,非常适合日志中转、临时缓存这类场景。
如果希望数据持久化,则需要使用PersistentVolumeClaim。比如一个容器写数据到PVC挂载目录,另一个容器读取同一份数据做离线分析,两个容器通过卷实现数据交换,互不干预。这种设计的优点是:存储从容器中解耦,换节点、换镜像都不会丢数据。
权限问题在共享卷里容易被忽略。如果两个容器以不同用户运行,对挂载目录的读写权限不一致,会导致一个容器可以写、另一个容器读不了或写不了。遇到这种问题,先确认两个容器运行的用户ID,再决定卷目录的owner和mode,必要时在securityContext里调整fsGroup。
5. 控制器如何影响Pod的生命周期和调度
5.1 Deployment滚动更新对Pod的影响
Deployment创建Pod的方式不是直接创建,而是通过ReplicaSet间接管理。每当你更新Deployment的镜像版本或配置,Deployment会新建一个ReplicaSet,然后逐步增加新副本、缩减旧副本。这个过程中,Pod的标签、名字、IP全都会变,所以Service不能依赖Pod IP,只能依赖标签选择器。
滚动更新的细节会影响Pod的可用性。maxSurge表示允许超出期望副本数的临时副本数,maxUnavailable表示允许不可用的副本数。生产环境建议maxSurge设为25%,maxUnavailable设为0,保证更新过程中始终有足够副本在服务。但这么配置的代价是更新速度变慢,因为必须先起来新副本,再销毁旧副本。
5.2 Pod调度:节点选择与亲和性
调度器决定Pod放哪个节点。默认情况下,它会根据节点资源余量、污点容忍度、Pod需求等条件自动选择。当你需要把某些Pod固定到特定节点(比如GPU节点、存储型节点),可以通过nodeSelector或nodeAffinity控制。
nodeSelector最粗糙也最简单:在Pod的spec里写上nodeSelector: gpu-type: a100,就能精准调度到带对应标签的节点。nodeAffinity功能更强,支持硬性要求和软性偏好,可以组合多个条件。比如要求节点必须在特定可用区(硬性),同时优先选择CPU型号更新的节点(软性)。
yaml复制affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/gpu
operator: Exists
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: cpu-model
operator: In
values:
- icelake
调度策略一旦写错,Pod会一直处于Pending状态。这时用kubectl describe pod查看Events,能清楚看到调度器给出的失败原因,比如不满足节点亲和性,或者节点资源不足。
5.3 静态Pod这个容易被忽视的点
静态Pod不看YAML文件从API Server创建,而是节点上的kubelet自己根据本机目录下的清单文件拉起运行的。静态Pod的典型应用场景是控制平面组件自身,比如kube-apiserver、kube-controller-manager都是通过静态Pod方式部署的。
如果你想在某个节点上跑一个与集群管理强相关的组件,又不想经过API Server认证和授权,静态Pod是个灵活方案。只要把定义Pod的JSON或YAML文件放到kubelet配置文件指定的staticPodPath目录里,kubelet就会自动拉起并持续监控。但这个Pod只在那个节点上存在,API Server能看到它但无法删除它,除非把对应文件从目录里移走。
排查静态Pod问题时,用kubectl get pod -n kube-system能看到名字以节点名结尾的Pod,比如kube-apiserver-172.16.1.10,这类就是静态Pod。修改静态Pod的配置就是修改节点上的清单文件,改完kubelet会自动重新创建Pod使配置生效。
6. 常见报错与排查实录:从Pod事件到容器日志
6.1 failed to create pod sandbox:这个报错到底是怎么回事
这个报错在社区里出现频率极高,报错样式类似failed to create pod sandbox: rpc error: code = Unknown desc = failed to create pod sandbox。它表示kubelet尝试调用运行时(containerd或CRI-O)创建Pod沙箱时失败了。换句话说,Pod还没走到启动容器这一步就挂了。
排查这个报错,我建议按这个顺序来:
先看kubelet日志。在Systemd系统上执行journalctl -u kubelet -f,越靠后的日志越有价值。常见原因之一是节点磁盘空间不足,kubelet或containerd因此在拉取镜像或解压层时失败。执行df -h查看/var/lib/containerd和/var/lib/kubelet所在分区剩余空间,如果低于10%,先清理无用镜像再继续排查。
再看CNI网络插件是否正常。Pod沙箱创建需要网络插件配合分配IP地址,如果Calico或Flannel的Pod一直没起来,或者网络插件版本和Kubernetes版本不兼容,也会在这个环节报错。执行kubectl get pods -n kube-system确认网络插件Pod都在Running状态,然后查看它自己的日志确认没有API连接故障。
还有一类原因是内核模块未加载。containerd依赖overlayfs,需要系统加载overlay和br_netfilter模块。执行modprobe overlay和modprobe br_netfilter,再把配置写进/etc/modules-load.d/里确保重启后仍生效。遇到报错就加载模块试试,模块缺失导致的失败经常只在重启或新节点上出现。
如果以上都正常,检查runc或containerd版本是否太旧。老版本containerd在某些内核版本上创建沙箱时会出现兼容性问题,升级到较新版本通常会解决。
6.2 ImagePullBackOff与ErrImagePull
镜像拉取失败时,Pod事件里会交替出现ErrImagePull和ImagePullBackOff。前者是拉取失败,后者是kubelet在退避重试。这俩状态不是致命错误,kubelet会持续重试,但一直拉不到镜像的话Pod永远无法就绪。
排查时先确认镜像名称和tag写没写错,尤其注意私有镜像仓库地址前缀。如果镜像在私有仓库里,一定要在Pod所在的命名空间创建imagePullSecret,然后在Pod的spec中通过imagePullSecrets引用它:
yaml复制imagePullSecrets:
- name: my-registry-secret
在节点上手动执行crictl pull <镜像地址>能快速判断是节点网络问题还是认证问题。能在节点上手动拉取成功的镜像,放到Pod里却拉取失败,重点检查imagePullPolicy和Secret配置。
6.3 CrashLoopBackOff:容器一直重启
容器启动后立刻崩溃、然后又重启、又崩溃,最终kubelet进入退避状态,就叫CrashLoopBackOff。退避时间从10秒开始指数增长,上限300秒,之后策略重置。这个状态本身是保护机制,防止节点被反复启动的容器拖垮。
排查步骤就两步:第一步看Pod事件,kubectl describe pod
最容易被忽视的情况是,容器启动时依赖的配置项缺失,或者环境变量指向了错误的Service地址。应用启动失败、退出码非0,但日志里只有一些不明显的警告。这时候需要进容器手动执行命令探测依赖项是否可达,比如kubectl exec -it
6.4 网络插件问题导致的Pod网络异常
Pod创建成功、状态Running,但Pod IP ping不通,或者Service访问不到Pod,这类问题的根源通常在CNI。Calico依赖BGP或VXLAN,Flannel通常用VXLAN或host-gw,不同插件对内核模块、网络端口的要求都不一样。
最直接的排查方法是进入Pod内部测试网络,kubectl exec -it
节点上的iptables规则出问题时,一个暴力但有效的办法是重启网络插件Pod,让它们重建规则。很多网络故障在重启对应插件后都能恢复,因为规则被重新生成了。不过这只能解决一时之痛,根治还是要找到为什么会丢规则,优先确认节点防火墙是否拦截了插件需要的端口。
6.5 日志查看与Debug容器
日常排错离不开三个命令:kubectl describe、kubectl logs、kubectl exec。describe看事件,logs看容器输出,exec进容器交互。但前两类命令在一些边缘情况下不够用,比如容器里没装bash、curl,甚至主进程刚启动就崩溃无法exec。
遇到这种情况,可以临时创建一个调试容器挂到目标Pod上。这个功能在较新版本的kubectl里直接支持debug子命令,可以用一个带完整工具链的镜像,共享目标Pod的网络命名空间:
bash复制kubectl debug -it <pod-name> --image=nicolaka/netshoot:latest --target=<container-name>
调试容器能访问目标Pod的网络栈,可以直接排查DNS解析、路由、端口监听这些网络问题,而不影响目标Pod的运行。这是我个人强烈推荐的排错手段,比从节点上ssh过去看靠谱得多。
7. 面向企业实战的资源控制和设备扩展
7.1 安全上下文:控制Pod和容器的权限
企业中安全是硬性要求,Pod默认以容器镜像声明的用户运行,风险很大。更好的实践是通过securityContext显式指定运行用户、组、权限限制,让容器以非root身份运行。
yaml复制securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
runAsNonRoot: true
capabilities:
drop:
- ALL
capabilities控制Linux内核能力,drop ALL再按需添加,可以最大程度缩小攻击面。fsGroup用于控制挂载卷在Pod内的访问权限,尤其当多个容器共享卷时,统一设置fsGroup可以避免读写冲突。还有一个常被忽略的配置是allowPrivilegeEscalation,设为false可以防止容器内的进程获得比父进程更高的权限,很多安全审计要求这项必须关闭。
另外提醒一句,Kubernetes的匿名访问和未授权访问风险一直是企业安全巡检的重点。生产环境务必保证kube-apiserver开启了认证授权,关闭匿名访问,为ServiceAccount分配最小权限的RBAC角色,别让集群的控制面裸奔在网络上,这类安全事故的代价极高。
7.2 requests和limits的资源语义
requests是调度依据,limits是运行时限制。调度器看requests判断节点能不能放得下;运行时看limits限制容器能用到多少。两者之间可以存在差值,这个差值就是"可突发资源",但如果节点超卖严重,Pod可能面临CPU节流和内存被回收的隐患。
对于CPU,limits设太高不会导致Pod被杀,只会被节流。但对于内存,一旦容器用量超过limits,内核会触发OOM Killer,容器直接被杀死重启。所以内存limits要留出约20%-30%的余量,避免Java这类堆外内存占用大户触发OOM。
实际管理时,我习惯先不设limits,只设requests,跑一段时间观察实际用量,再根据监控数据把limits调到合理值。一上来就拍脑袋设limits,容易低估应用内存峰值,导致线上容器频繁被杀,这是很多新手都会踩的坑。
7.3 device plugin与GPU/NPU资源扩展
普通资源的调度方式扩展到硬件设备时,Kubernetes提出了device plugin架构。GPU厂商(NVIDIA)、NPU厂商(华为昇腾)会把设备抽象成扩展资源,注册给kubelet,然后Pod通过limits声明需要多少设备资源,调度器会据此分配Pod到相应节点。
yaml复制resources:
limits:
nvidia.com/gpu: 1
声明了nvidia.com/gpu之后,Pod就会被调度到带有GPU的节点,并且运行时会把GPU设备挂载进容器。这套机制在企业AI推理场景中已经非常成熟,多卡训练任务也靠它在多个节点间分配资源。需要留意的是,device plugin必须在每个GPU节点上以DaemonSet方式运行,否则该节点的设备不会出现在资源清单里。
同样的思路也能扩展到FPGA、SR-IOV网卡、加密卡等设备。理解了device plugin,你就能举一反三,把Kubernetes从纯容器编排平台扩展成统一调度异构硬件基础设施的控制面。
8. 实操心得与一个小技巧
整个K8S系列写到这里,我对Pod最深的体会是:它带来的不仅仅是技术能力,更是一套"以不变应万变"的思维方式。所有上层机制——自愈、滚动更新、弹性伸缩——都建立在"Pod可以随时重建"这个前提上。当你从心底接受Pod是"用完即走"的,很多排查问题的思路就通透了。相反,如果总把Pod当成一台需要精心维护的虚拟机,遇到节点故障就想进去抢救,那才是逆着Kubernetes的设计哲学在干活。
最后分享一个小技巧,创建Pod之后用kubectl get pod -o wide看它在哪个节点、分配了什么IP,再配合kubectl describe pod查看Events,基本能覆盖90%的初级排错需求。如果你还没装kubectl的自动补全,强烈建议在bashrc或zshrc里加上source <(kubectl completion bash)这一行,日常输入命令能省太多事了。
