1. 为什么“Kubernetes 开发者指南”不是给运维看的说明书很多人点开标题叫《Kubernetes 开发者指南》的文章第一反应是“哦又是教我怎么装 kubelet、配 kubeadm、拉 etcd 镜像的”——结果翻两页发现全是kubeadm init --pod-network-cidr10.244.0.0/16这类命令心里一沉这不还是运维手册吗我一个写 Java 微服务、跑 CI/CD 流水线、天天改 Deployment YAML 的人真需要亲手在 Ubuntu 22.04 上从零搭控制平面不是。这篇指南的起点就卡在“开发者”三个字的定义上。它不面向要管理 500 节点集群的 SRE也不面向刚考完 CKA 想刷题的备考者它面向的是你——那个早上 9:15 收到 PR 合并通知、10:00 就得把新版本推上测试环境、下午 3 点被 QA 打电话问“为什么 /healthz 返回 503”的后端工程师是你——那个在本地用 Docker Compose 跑通了三体服务一上 Kubernetes 就发现 ConfigMap 不生效、Secret 挂载路径权限报错、Liveness Probe 频繁重启 Pod 的前端Node.js 全栈是你——那个在 GitLab CI 里写了 17 行kubectl set image脚本却始终搞不清kubectl rollout status和kubectl wait --forconditionavailable到底该用哪个才算真正“部署完成”的 DevOps 轻量使用者。所以“开发者”在这里有明确定义你不需要知道 etcd 的 WAL 日志如何刷盘但必须清楚 Pod 的 terminationGracePeriodSeconds 是怎么和 Spring Boot 的 shutdown hook 协同工作的你不必手写 Admission Webhook 的证书签发流程但得能看懂 MutatingWebhookConfiguration 里 rules 字段匹配的是哪个 API Group/Version/Resource你不用调优 kube-scheduler 的 predicate/priority 插件链但得明白为什么把 resources.requests.cpu 设成 100m 而 limits.cpu 设成 500m会导致你的 Python Flask 应用在压测时被 OOMKilled而日志里只显示 “Exit Code 137”。这背后是一条被长期模糊的分界线Kubernetes 的“使用层”和“管理层”彻底分离了。过去三年我带过 12 个业务团队落地 K8s最常听到的抱怨不是“集群起不来”而是“我改了 YAML为啥服务没更新”、“我加了 HPACPU 用了 80%Pod 就是不扩”、“我本地 curl 通进容器里 netstat -tuln 却看不到端口监听”——这些问题90% 出现在开发者对 Kubernetes 的“运行时契约”Runtime Contract缺乏系统性认知而非不会敲命令。所谓“运行时契约”就是 Kubernetes 对你写的代码、配置、镜像、网络、存储所做出的隐含承诺以及它要求你必须履行的对应义务。比如它承诺只要你把容器进程的主 PIDPID 1保持运行Pod 就算“就绪”它要求你必须让这个进程能响应 SIGTERM并在收到信号后优雅退出否则 terminationGracePeriodSeconds 一到直接 SIGKILL它承诺只要你声明了 readinessProbe它就会定期调用并根据返回状态决定是否将流量导入该 Pod它要求你的 probe 必须足够轻量、超时时间必须短于 failureThreshold × periodSeconds否则会触发误判驱逐。这些不是“最佳实践”是 Kubernetes 内核级的行为逻辑。忽略它们就像用 MySQL 却不理解事务隔离级别表面能跑一到高并发就出幻读。本系列的第一篇就从这里切进去——不装集群不配 RBAC不碰 Helm Chart只聚焦一个核心问题当你执行kubectl apply -f deployment.yaml的那一刻Kubernetes 内部到底发生了什么你的代码、配置、镜像是如何被翻译成真实运行的 Pod 的理清这条链路你才能从“命令执行者”变成“行为预判者”。提示本文所有实操均基于标准上游 Kubernetes v1.28非 OpenShift、非 Rancher 封装版环境为 Ubuntu 22.04 LTS containerd 1.7.x kubectl v1.28.x。所有命令、YAML 片段均可直接复现无需额外安装插件或魔改组件。2. 从kubectl apply到真实 Pod一条被拆解的完整生命周期链路很多开发者以为kubectl apply是个“原子操作”输入 YAML回车服务就活了。其实它是一条横跨客户端、API Server、Scheduler、Kubelet 的多跳链路每一跳都藏着影响你开发体验的关键决策点。我们以一个最简 Deployment 为例逐层拆解# demo-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo spec: replicas: 2 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /readyz port: 80 initialDelaySeconds: 5 periodSeconds: 102.1 客户端校验kubectl在你按下回车前就做了什么kubectl apply并非直连 API Server。它首先在本地完成三重校验Schema 校验kubectl内置了所有 Kubernetes 原生资源的 OpenAPI v3 Schema。它会检查apiVersion是否合法apps/v1存在、kind是否拼写正确Deployment不是Depolyment、字段类型是否匹配replicas必须是整数不能是2字符串。如果你用kubectl apply -f加载一个字段名写错的 YAML错误信息会明确告诉你error: error validating demo.yaml: error validating data: ValidationError(Deployment.spec): unknown field replcias in io.k8s.api.apps.v1.DeploymentSpec—— 这个提示来自客户端根本没发请求。Dry-run 预演kubectl apply --dry-runclient会模拟整个 apply 流程生成最终提交给 API Server 的对象含默认值注入但不实际发送。执行它你会看到kubectl apply -f demo-deployment.yaml --dry-runclient -o yaml输出中spec.replicas会显式写出2即使你没写默认值也会补全spec.template.spec.containers[0].imagePullPolicy会变成IfNotPresent这是image字段的默认策略。这个能力极其重要它让你在真正改动集群前先看清 Kubernetes 会“自动帮你填什么”。Server-side ApplySSA元数据注入从 v1.23 起kubectl apply默认启用 SSA。它会在对象 metadata 中注入managedFields记录每个字段由谁manager: kubectl-client-side-apply、何时time、通过什么字段集fieldsType: FieldsV1管理。这意味着如果你用kubectl edit直接修改 Pod 的 labelmanagedFields会标记该 label 由kubectl-edit管理下次kubectl apply时它只更新自己管理的字段如spec.replicas不会覆盖kubectl edit修改的 label这解决了传统kubectl replace或patch导致的“字段覆盖冲突”问题是开发者协作安全的底层保障。注意managedFields是 SSA 的核心但它也带来一个经典坑——当你用kubectl apply创建资源再用kubectl patch修改同一字段后续apply可能因字段所有权冲突失败。解决方案是要么全程用apply要么patch后手动清理managedFields不推荐或改用kubectl apply --server-side --force-conflictsv1.27。2.2 API Server 接收对象准入与默认值注入的“第一次变形”当kubectl通过 HTTPS POST 将对象发送到https://apiserver/apis/apps/v1/namespaces/default/deploymentsAPI Server 的处理远不止“存数据库”那么简单。它启动一个严格的准入Admission流水线阶段关键动作对开发者的影响Authentication验证 client 证书/Bearer Token 是否有效绑定到某个 ServiceAccount若你用kubectl --tokenxxx访问Token 过期则报Unauthorized若用~/.kube/config则取决于 context 中 user 的凭据有效性Authorization检查该用户是否有create deployments权限RBAC最常见报错Error from server (Forbidden): error when creating demo.yaml: deployments.apps is forbidden: User system:anonymous cannot create resource deployments说明你没配 kubeconfig 或 SA 权限不足Mutating Admission注入默认值、修改字段如spec.template.spec.containers[].securityContext.runAsNonRoot: true若集群启用了 PodSecurity Admissionspec.template.spec.dnsPolicy: ClusterFirst强制覆盖spec.template.spec.containers[].resources.limits.memory若配置了 LimitRange这是“为什么我明明没写 limitsdescribe pod 却显示有默认值”的根源。你必须kubectl get limitrange -o yaml查看命名空间默认限制Validating Admission拒绝非法请求如spec.replicas 1000超出集群设置的maxReplicasspec.template.spec.containers[].ports[].hostPort若禁用 HostPortspec.template.spec.volumes[].persistentVolumeClaim.claimName指向不存在的 PVC报错如admission webhook validation.webhook.example.com denied the request说明有自定义 Webhook 拦截需联系平台团队关键洞察kubectl apply提交的 YAML和最终存入 etcd 的对象很可能不同。默认值注入发生在 Mutating 阶段且不可逆。例如你删掉 YAML 中的livenessProbeAPI Server 不会给你补上因为这不是默认值但如果你删掉spec.template.spec.dnsPolicy它大概率会被补成ClusterFirst。验证方法kubectl get deployment nginx-demo -o yaml对比原始文件重点看spec.template.spec下哪些字段被自动添加了。2.3 Controller Manager 介入Deployment 控制器的“二次编排”API Server 只负责“存”和“转”真正的“业务逻辑”由 Controller Manager 承担。当你创建 Deploymentdeployment-controller立即开始工作生成 ReplicaSetDeployment 本身不直接管理 Pod它通过创建一个 ReplicaSetRS来间接管理。控制器会生成一个 RS 名称如nginx-demo-7c8b9d4f5其spec.replicas Deployment 的spec.replicasspec.selector Deployment 的spec.selectorspec.template Deployment 的spec.template但会加上controller-revision-hash: 7c8b9d4f5注解。滚动更新策略实现当你修改 Deployment 的spec.template.spec.containers[0].image控制器不会直接更新现有 RS。它会创建一个新 RS如nginx-demo-5f6b8c7d9replicas初始为 0按spec.strategy.rollingUpdate.maxSurge默认 25%和maxUnavailable默认 25%计算扩缩比例逐步将旧 RS 的replicas减 1新 RS 的replicas加 1直到旧 RS 为 0新 RS 达到目标副本数关键点整个过程 Pod IP 是变化的因为每个 RS 管理独立的 Pod 集合而 Pod IP 由 CNI 分配每次新建 Pod 都会获得新 IP。这就是为什么你不能在代码里硬编码其他服务的 Pod IP。状态同步与回滚控制器持续对比status.replicas实际运行的 Pod 数、status.updatedReplicas已更新到新版本的 Pod 数、status.availableReplicas就绪且可用的 Pod 数。kubectl rollout status deployment/nginx-demo就是轮询这三个字段直到availableReplicas replicas。如果更新失败如新镜像拉不到控制器会停止滚动并将status.conditions中的Progressing设为FalseReason为ProgressDeadlineExceeded。实操技巧想立刻看到 Deployment 的滚动过程执行kubectl rollout status deployment/nginx-demo --watch它会实时打印Waiting for deployment nginx-demo rollout to finish: 1 out of 2 new replicas have been updated...。比kubectl get pods -w更精准因为它关注的是控制器状态而非单纯 Pod 状态。2.4 Scheduler 与 KubeletPod 的“出生”与“落地”当 ReplicaSet controller 创建了一个新 Pod 对象如nginx-demo-7c8b9d4f5-abcde它只是个“待产通知书”。Pod 真正变成运行中的容器还需 Scheduler 和 Kubelet 协作Scheduler 选节点它监听未调度的 Podspec.nodeName为空根据nodeSelector、affinity、tolerations、资源请求requests.cpu/memory等规则选择一个满足条件的 Node。注意requests是调度的唯一依据limits不参与调度只用于 cgroups 限流。如果你设requests.cpu100m, limits.cpu500mScheduler 只看 100m 是否有空闲但运行时 CPU 使用超 500m 会被 throttled。Kubelet 拉起容器被选中的 Node 上的 Kubelet 发现一个新 Pod 被分配给自己开始执行拉取镜像按imagePullPolicy创建 sandbox 容器pause 容器提供 Pod 网络命名空间启动业务容器nginx挂载 volumes、注入 secrets/configmaps执行探针initialDelaySeconds后开始readinessProbe成功后将 Pod 状态设为ReadyService Endpoints 才会加入该 Pod IPlivenessProbe失败则重启容器。这里有个致命细节readinessProbe成功是流量进入的前提livenessProbe失败是容器重启的触发器。二者目的完全不同绝不能混用我见过太多人把/healthz同时配给两者结果健康检查慢一点如 DB 连接超时livenessProbe频繁重启形成雪崩。3. 开发者必知的五个“隐形契约”那些不写在文档里却决定成败的细节Kubernetes 文档浩如烟海但真正影响日常开发效率的往往是那些散落在各处、没有单独章节、却无处不在的“隐形契约”。它们不构成错误但一旦违背就会导致行为不可预测、调试成本飙升。以下是我在 12 个团队中反复验证的五大核心契约3.1 容器进程必须是 PID 1且必须能响应 SIGTERM这是 Kubernetes 生命周期管理的基石。当你docker run -d nginx容器内实际运行的是nginx:master process它是 PID 1。Kubernetes 要求你的容器入口点entrypoint必须是长期运行的前台进程且能捕获SIGTERM信号。反例与后果用sh -c java -jar app.jar启动 Java 应用sh是 PID 1java是子进程。SIGTERM发给shsh默认忽略java进程收不到terminationGracePeriodSeconds一到Kubelet 直接SIGKILLSpring Boot 的PreDestroy方法根本没机会执行DB 连接池来不及关闭连接泄漏。用nohup java -jar app.jar nohup启动后台进程让 shell 退出容器立即结束因为 PID 1 的sh退出了Pod 状态变为Completed。正确做法Java用exec java -jar app.jarexec替换 shell 进程让 java 成为 PID 1或使用 jib 构建镜像它默认生成正确的 entrypoint。Node.jsCMD [node, server.js]确保server.js中监听process.on(SIGTERM, ...)。PythonCMD [gunicorn, --bind, 0.0.0.0:8000, app:app]gunicorn 会处理信号。验证方法进容器ps aux确认你的应用进程 PID 是 1然后kill -TERM 1观察应用是否优雅退出日志应有 shutdown 信息而非立即消失。3.2 ReadinessProbe 是“准入证”LivenessProbe 是“重启开关”二者不可互换很多开发者认为“反正都是健康检查配一个就行”。这是最大误区。它们解决的是完全不同的问题维度readinessProbelivenessProbe目的告诉 Kubernetes“我现在准备好接收流量了吗”告诉 Kubernetes“我现在还活着吗如果死了请重启我。”失败后果Pod 从 Service Endpoints 移除不再接收新流量已有连接保持容器被 kill 并重启所有连接中断适用场景启动慢的应用Spring Boot 初始化耗时、依赖外部服务DB 连接池建立、批量加载数据完成前应用卡死死锁、内存泄漏导致 OOM、无限循环无法响应 HTTP典型错误配置# 错用同一个 endpoint 处理两种需求 readinessProbe: httpGet: path: /healthz port: 8080 livenessProbe: httpGet: path: /healthz port: 8080当/healthz因 DB 连接超时返回 500readinessProbe失败流量被切走但livenessProbe也失败容器被重启——重启后又连不上 DB再次失败形成“重启风暴”。正确姿势/readyz检查应用自身状态 关键依赖DB 连接池、Redis 连接超时时间可稍长如timeoutSeconds: 10failureThreshold: 3。/healthz只检查应用进程是否存活如return 200不连外部服务超时极短timeoutSeconds: 1periodSeconds: 5。Spring Boot Actuator 用户/actuator/health/readiness和/actuator/health/liveness是官方分离的 endpoint直接用。3.3 ConfigMap/Secret 的热更新不是“实时生效”而是“下次挂载时生效”开发者常抱怨“我kubectl edit cm my-config改了配置应用里怎么还是老值”——因为 ConfigMap/Secret 以文件形式挂载到容器时是一次性拷贝。修改 ConfigMap 后已运行的 Pod 中的文件内容不会自动更新。真相只有以下情况挂载的文件才会刷新Pod 重建如 Deployment 滚动更新手动删除 Podkubectl delete pod xxx由控制器重建使用subPath挂载单个 key 时即使 ConfigMap 更新该文件也不会更新这是设计缺陷v1.28 仍未修复。解决方案推荐用volumeMounts整个挂载 ConfigMap不指定subPath。这样当 ConfigMap 更新Kubelet 会检测到 hash 变化在几秒内默认 60s可调--sync-frequency更新挂载点下的所有文件。进阶应用内监听文件变更如 Linux inotify读取新内容或使用 Reloader 这类 Operator监听 ConfigMap 变更并自动 rollout Deployment。避坑绝对不要在envFrom中引用 ConfigMap因为环境变量在容器启动时注入永远不变。实操验证kubectl exec -it pod -- ls -l /etc/config/查看文件 inode改 ConfigMap 后再执行inode 号变了说明文件已更新。3.4 Service 的 ClusterIP 是“虚拟 IP”它的负载均衡发生在 kube-proxy 层而非应用层很多开发者以为curl http://my-service:8080是直接连到某个 Pod。其实中间隔着一层透明代理。kube-proxy 有三种模式userspace已废弃、iptables、ipvs。当前主流是 ipvs它在 Node 上创建一个虚拟 IPVIP并将流量通过 IPVS 规则转发到后端 Pod 的 IP:Port。关键影响连接复用失效HTTP/1.1 Keep-Alive 连接在经过 kube-proxy 时可能被转发到不同 Pod。因为 IPVS 默认是 rr轮询或 lc最少连接不保证同一 TCP 连接始终打到同一 Pod。客户端看到的源 IP 是 Node IP除非 Service 设置externalTrafficPolicy: Local否则客户端真实 IP 会被 SNAT 成 Node IP后端应用request.remote_addr拿到的是 Node 地址不是用户真实 IP。Headless Service 例外clusterIP: None的 Service 不走 kube-proxyDNS 直接解析为 Pod IP 列表客户端可自行做负载均衡如 gRPC 的 round_robin 策略。调试技巧查看 kube-proxy 规则ipvsadm -ln | grep service-cluster-ip能看到后端 Pod IP 列表测试连接复用用curl -H Connection: keep-alive http://my-service:8080多次kubectl logs pod看日志如果请求分散在多个 Pod说明复用被打破了。3.5 Pod 的 DNS 解析依赖于dnsPolicy和search域而非宿主机配置在容器里ping mysql能通ping mysql.default.svc.cluster.local也能通但ping mysql.prod.svc.cluster.local却不通这往往不是网络问题而是 DNS 解析策略。Kubernetes 为 Pod 设置了默认的dnsPolicy: ClusterFirst意味着所有 DNS 查询先发给集群 DNS 服务CoreDNSCoreDNS 的search域按顺序尝试namespace.svc.cluster.local→svc.cluster.local→cluster.local所以ping mysql会被自动补全为mysql.default.svc.cluster.local假设 Pod 在 default 命名空间ping mysql.prod会被补全为mysql.prod.default.svc.cluster.local错误而非mysql.prod.svc.cluster.local。解决方案跨命名空间访问必须用 FQDNmysql.prod.svc.cluster.local或在 Pod 的dnsConfig中自定义search域dnsConfig: searches: - prod.svc.cluster.local - default.svc.cluster.local验证 DNS进容器cat /etc/resolv.conf确认nameserver是 CoreDNS 的 ClusterIP如10.96.0.10search域是否符合预期。4. 本地开发与集群调试一套不依赖kubectl exec的高效工作流作为开发者你不可能每次改一行代码就kubectl apply一次。高效的本地-集群协同需要一套绕过“构建-推送-部署”循环的调试方案。以下是我验证过的、真正提升日均迭代速度的组合拳4.1 Skaffold让kubectl apply变成CtrlS的自动化引擎Skaffold 是 Google 开源的 Kubernetes 开发工具核心价值是将本地文件变更、镜像构建、部署、日志流聚合封装成一键流水线。它不是替代kubectl而是让kubectl的调用变得“无感”。典型skaffold.yaml配置apiVersion: skaffold/v4beta1 kind: Config metadata: name: nginx-demo build: artifacts: - image: localhost:5000/nginx-demo context: . docker: dockerfile: Dockerfile local: push: false # 本地开发用不推镜像仓库 deploy: kubectl: manifests: - k8s/deployment.yaml - k8s/service.yaml portForward: - resourceType: service resourceName: nginx-demo port: 80 localPort: 8080工作流skaffold dev启动Skaffold 监听Dockerfile和src/目录变更你改代码保存Skaffold 自动docker build -t localhost:5000/nginx-demo .kubectl set image deployment/nginx-demo nginxlocalhost:5000/nginx-demokubectl rollout status deployment/nginx-demokubectl port-forward service/nginx-demo 8080:80自动维护你浏览器打开http://localhost:8080看到最新代码效果。优势镜像不推远程仓库节省时间portForward持久化不用每次kubectl port-forward日志自动聚合skaffold dev输出中所有 Pod 日志按 namespace/pod 名分组显示比kubectl logs -f deployment/nginx-demo清晰十倍。注意local.push: false要求集群的 containerd 或 dockerd 能拉取localhost:5000的镜像。Ubuntu 22.04 上需在 containerd config 中添加untrusted_workload_registry [localhost:5000]并重启 containerd。4.2 Telepresence把本地进程“注入”到集群网络Skaffold 解决了“改代码-看效果”但有些场景仍需真集群环境比如你的服务要调用集群内的 Kafka、Elasticsearch或者依赖 Istio 的 mTLS。此时localhost:5000的镜像无法访问集群内部服务。Telepresence 是开源的“本地-集群网络桥接”工具。它的工作原理是在集群中部署一个双向代理traffic manager将你的本地进程注册为集群中的一个“虚拟 Pod”所有发往该 Pod 的流量如 Service DNS 解析都被重定向到你的本地进程。实操步骤telepresence connect建立本地到集群的 VPN 连接telepresence intercept nginx-demo --port 8080拦截nginx-demoService 的所有流量转发到本地localhost:8080本地启动你的开发服务器npm run dev监听 8080集群内其他服务如curl http://nginx-demo:80的请求全部打到你本地进程你本地进程调用http://elasticsearch:9200DNS 解析为集群内 ES 的 ClusterIP流量正常发出。关键价值本地调试享受集群网络Service、DNS、Ingress无需构建镜像、无需部署 Pod支持多语言Java、Python、Node.js、Go 均可可同时 intercept 多个服务构建本地微服务联调环境。避坑Telepresence 会修改本地/etc/hosts和 DNS 配置telepresence leave后务必检查是否恢复避免影响其他网络工具。4.3 kubectl debug不侵入式诊断运行中 Pod 的终极手段当线上 Pod 行为异常CPU 飙高、内存泄漏、网络不通kubectl exec -it pod -- sh是第一反应。但很多生产环境出于安全考虑禁用了execsecurityContext.allowPrivilegeEscalation: false或 PodSecurityPolicy 限制。此时kubectl debug是救星。kubectl debug的原理是在目标 Node 上启动一个全新的、临时的、特权容器ephemeral container共享目标 Pod 的网络、IPC、PID 命名空间从而获得对原 Pod 的完全可观测性。典型用法# 为 nginx-demo-7c8b9d4f5-abcde 创建调试容器 kubectl debug -it nginx-demo-7c8b9d4f5-abcde --imagenicolaka/netshoot --targetnginx--imagenicolaka/netshoot使用预装tcpdump、nslookup、strace、iftop的调试镜像--targetnginx指定调试容器共享 nginx 容器的命名空间必须是 Pod 内容器名进入后ps aux看 nginx 进程netstat -tuln看端口tcpdump -i any port 80抓包strace -p 1跟踪主进程系统调用。优势无需修改原 Pod 配置不重启不依赖exec权限只要create ephemeralcontainersRBAC 权限即可调试容器退出后自动清理不留痕迹。权限配置集群管理员需授予开发者kubectl create clusterrolebinding debug-role --clusterrolesystem:debugger --useryour-user。5. 从“能跑”到“稳跑”开发者视角的可观测性基建清单Kubernetes 的抽象带来了便利也带来了黑盒。当服务不可用你最先看什么是kubectl get pods还是kubectl describe pod抑或直接去 Grafana 看指标一个成熟的开发者应该建立自己的“可观测性反射弧”——看到现象立刻知道该查哪一层。以下是我为业务团队梳理的、按排查优先级排序的“五层诊断清单”每层对应一个命令或工具10 秒内给出答案5.1 第一层Pod 状态层 ——kubectl get pods -o wide这是最快速的“生命体征扫描”。重点关注三列STATUSRunning是常态Pending表示调度失败kubectl describe pod看 EventsContainerCreating表示镜像拉取或 volume 挂载慢CrashLoopBackOff表示容器启动后立即退出kubectl logs pod --previous看上一次日志READY1/1表示 1 个容器全部就绪0/1表示 readinessProbe 未通过NODE确认 Pod 被调度到哪个 Node为下一层排查定位机器。速查命令# 查看所有 Pod 状态按状态分组统计 kubectl get pods --all-namespaces | awk {print $3} | sort | uniq -c | sort -nr # 查看特定 Pod 的详细状态和事件 kubectl get pod nginx-demo-7c8b9d4f5-abcde -o wide5.2 第二层事件层 ——kubectl describe pod和kubectl get events --sort-by.lastTimestampkubectl describe是 Kubernetes 的“病历本”它聚合了 Pod 的 Spec、Status、Events。其中Events部分位于输出底部是黄金信息源记录了从调度、拉镜像、挂载 volume 到容器启动失败的完整时间线。关键事件解读FailedScheduling调度失败原因在Message字段如0/3 nodes are available: 3 node(s) didnt match pod affinity/anti-affinity rules.Failed容器启动失败Message显示Error: failed to start container nginx: Error response from daemon: OCI runtime create failed: ...BackOff容器反复重启Message显示Back-off restarting failed container需结合kubectl logs --previousStarted/Created