2026/10/21 14:30:38

Kubernetes核心资源原理:Pod、Service、ConfigMap等七类资源的生命周期与依赖关系

Kubernetes核心资源原理:Pod、Service、ConfigMap等七类资源的生命周期与依赖关系 1. 这不是“背概念”而是搞懂Kubernetes怎么真正干活的底层逻辑你翻过Kubernetes官方文档里那几十页的Resource定义也照着教程跑过kubectl create deployment但当Pod突然变成Pending、Service死活不通、ConfigMap改了却没生效时你心里是不是还卡着一层雾这层雾不来自命令记不熟而来自对“Kubernetes核心资源到底在系统里扮演什么角色、彼此之间靠什么咬合运转”的模糊认知。我带过不少刚从Docker Compose转过来的开发者他们最常问的一句话是“为什么我删掉一个Deployment它下面的ReplicaSet和Pod会自动重建谁在背后盯着”——答案不在kubectl命令里而在Kubernetes这套声明式资源模型的设计哲学中。今天这篇不讲API版本怎么写、不列YAML字段大全只聚焦一件事把Pod、Node、Namespace、Service、ConfigMap、Secret、PersistentVolume这七类最常打交道的核心资源拆开来看它们的存在意义、生命周期边界、控制关系链路、以及调度与绑定发生的物理位置。比如你可能知道Service是“服务发现”但未必清楚它的ClusterIP根本不是真实网卡地址而是kube-proxy在每个Node上用iptables或IPVS规则“伪造”出来的流量入口你也可能用过ConfigMap挂载配置但未必意识到它的内容在Pod启动时被一次性注入到容器文件系统之后即使ConfigMap更新容器内文件也不会自动刷新——这个行为差异直接决定了你该用subPath挂载单个文件还是用envFrom注入环境变量。这些不是“高级技巧”而是你每天调试集群故障、设计应用部署策略、评估资源隔离强度时必须踩在脚下的地基。无论你是刚考完CKA想补原理还是在生产环境天天处理OOMKilled和NodeNotReady的老兵只要还想让Kubernetes不只是一个“黑盒调度器”这篇就是为你写的。2. 资源不是孤立的零件而是一张有向依赖网Kubernetes所有资源对象本质上都是etcd里的一条JSON记录但它们绝非平铺直叙地躺在数据库里。当你执行kubectl apply -f nginx-deployment.yaml系统内部发生的是一个多层级、有方向、带触发条件的资源依赖链激活过程。理解这张网的拓扑结构比记住每个资源的spec字段重要十倍。2.1 从最底层开始Node与Namespace是整个世界的“物理容器”和“逻辑围栏”Node是Kubernetes集群的基石但它在API对象层面其实是个“半托管”资源。你几乎不会手动创建Node对象除了极少数自建集群场景它由kubelet在启动时自动向API Server注册生成。关键点在于Node对象本身不承载业务负载它只是为Pod提供运行载体的“物理身份证明”。它的status字段里记录的CapacityCPU、内存总量和Allocatable实际可分配量是调度器做决策的唯一依据。这里有个极易被忽略的细节Allocatable ≠ Capacity。kubelet会主动预留一部分资源给系统守护进程如dockerd、kubelet自身、SSHD这部分预留量由--system-reserved和--kube-reserved参数控制。如果你在Node上部署了大量非K8s管理的进程或者忘记配置预留值就会出现“明明Node显示还有2核CPU空闲新Pod却因资源不足Pending”的诡异现象。我亲眼见过某次线上事故就因为运维同学在Node上偷偷装了个监控Agent占用了1.5G内存而--system-reservedmemory1Gi的配置没同步更新导致调度器误判资源充足最终引发雪崩。Namespace则是另一重维度的隔离。它不消耗任何物理资源纯粹是API Server层面的逻辑分组标签。但它的威力极大所有非集群级资源Pod、Service、ConfigMap等都必须归属某个NamespaceRBAC权限、ResourceQuota配额、NetworkPolicy网络策略全部以Namespace为作用域。一个典型误区是认为“不同Namespace的Pod默认网络互通”。错。NetworkPolicy默认是“全放行”但一旦你在某个Namespace里定义了一条policyTypes: [Ingress]的策略它就只对该Namespace内的Pod生效其他Namespace的Pod不受影响。这种“作用域精确性”正是Namespace作为逻辑围栏的核心价值——它让你能在一个物理集群里安全地并行运行开发、测试、预发、生产四套完全独立的环境连DNS域名service.namespace.svc.cluster.local都天然隔离。2.2 Pod唯一能被“看见”的运行单元也是所有调度与生命周期的终点如果说Node是土地Namespace是地块编号那么Pod就是这块地上唯一被Kubernetes“承认”的、能种庄稼的最小单位。注意这里说的“最小单位”是指调度、网络、存储绑定的最小粒度而不是容器的最小粒度。一个Pod可以包含多个容器通常是一个主应用容器若干Sidecar它们共享同一个Network Namespace所以能用localhost通信、同一个PID Namespace能看到彼此进程、以及同一个Volume挂载点。这种“紧耦合”设计直接决定了Kubernetes的调度逻辑调度器永远只决定“这个Pod整体放在哪个Node上”而不会去拆解Pod里的每个容器分别调度。Pod的生命周期状态机是理解K8s稳定性的关键。从Pending到Running再到Succeeded或Failed每一步背后都有明确的控制器在驱动。Pending状态最常见的原因有三类资源不足调度器找不到满足Request的Node、镜像拉取失败ImagePullBackOff、节点污点不匹配Taint/Toleration。而CrashLoopBackOff则意味着容器启动后立刻退出此时必须看kubectl logs pod-name --previous才能定位是应用崩溃还是健康检查失败。这里有个硬核经验永远不要只看Pod状态一定要配合kubectl describe pod name输出的Events列表。Events里会按时间倒序记录所有关键事件比如“Successfully assigned to node node-01”、“Pulling image nginx:1.20”、“Created container nginx”这些才是Pod真实的生命轨迹。我曾帮一个团队排查持续数小时的部署失败最终发现Events里有一条被忽略的警告“Warning FailedScheduling 3m (x15 over 12m) default-scheduler 0/3 nodes are available: 3 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didnt tolerate.”——原来他们忘了给master节点打tolerationPod被永远拒之门外。2.3 控制器模式Deployment、StatefulSet、DaemonSet的本质差异Pod本身是“脆弱”的它没有自愈能力。真正让Kubernetes具备生产可用性的是围绕Pod构建的一系列控制器Controller。它们像一群不知疲倦的管家持续观察etcd中Pod的状态并与你声明的期望状态Desired State做比对一旦发现偏差就立即采取行动Reconcile。Deployment、StatefulSet、DaemonSet就是三种最典型的管家类型它们的差异完全由其设计目标决定。Deployment的核心诉求是无状态应用的滚动更新与副本伸缩。它不直接管理Pod而是通过一个中间层——ReplicaSet来间接控制。当你更新Deployment的镜像版本时API Server会创建一个新的ReplicaSet并逐步将旧RS的副本数减到0新RS的副本数加到目标值。这个“双RS并存”的过渡期就是滚动更新的实现基础。关键点在于Deployment对Pod的“身份”完全不在乎它只关心数量。所以新旧Pod的名称是随机生成的如nginx-7c85b649d-2zq9p且重启后IP地址必然变更。这决定了它只适合HTTP API、Web前端这类天然支持IP漂移的无状态服务。StatefulSet则专为有状态应用而生比如MySQL主从、Elasticsearch集群、ZooKeeper。它解决了三个Deployment无法解决的问题稳定的网络标识每个Pod有固定hostnamestatefulset-name-0,statefulset-name-1、稳定的存储卷每个Pod独享一个PVC删除Pod不会丢失数据、有序的部署与扩缩容0号Pod必须先Running1号才开始创建。它的实现原理在于为每个Pod分配一个唯一的、不可变的“序号”Ordinal并把这个序号深度集成到Pod名称、Service DNS解析、PVC命名中。比如一个名为mysql的StatefulSet其Headless ServiceclusterIP: None会为每个Pod生成一条DNS记录mysql-0.mysql.default.svc.cluster.local解析到Pod-0的IPmysql-1.mysql.default.svc.cluster.local解析到Pod-1的IP。这种确定性是分布式数据库选主、节点发现的基石。DaemonSet则走的是另一个极端确保每个或选定的Node上都运行且仅运行一个该Pod的副本。它不关心副本总数只关心“全覆盖”。典型场景是日志采集Fluentd、监控代理Prometheus Node Exporter、网络插件Calico Felix。DaemonSet的调度逻辑非常简单粗暴只要Node满足其nodeSelector或affinity条件且未被unschedulable标记它就一定会在这个Node上启动一个Pod。这里有个重要提示DaemonSet创建的Pod默认不参与常规的资源调度竞争。它的Pod会被打上controller.kubernetes.io/pod-deletion-cost: 0注解意味着在Node资源紧张需要驱逐Pod时它会被优先保留。这是为了保障集群基础设施的稳定性。2.4 Service服务发现与负载均衡的“虚拟中枢”如果说Pod是血肉那么Service就是骨骼和神经。它不运行任何代码却为整个集群提供了统一的服务访问入口和流量分发能力。理解Service必须抓住两个核心ClusterIP的虚拟性和Endpoint的动态性。ClusterIP是一个纯粹的虚拟IP它不存在于任何物理网卡上。当你创建一个Service时API Server会为其分配一个集群内部的IP如10.96.1.100但这个IP只存在于kube-proxy维护的网络规则里。在iptables模式下kube-proxy会在每个Node上生成一堆KUBE-SERVICES链规则将发往10.96.1.100:80的流量DNAT目标地址转换到后端某个Pod的真实IP和端口上。在IPVS模式下则是通过Linux内核的IPVS模块建立更高效的哈希表进行转发。无论哪种模式ClusterIP都只是一个“路由指针”真正的流量路径是由Endpoint对象决定的。Endpoint对象是Service与Pod之间的“桥梁”。它由Endpoint Controller自动创建和维护其内容就是一组Pod IP:Port的列表。这个列表的生成完全依赖于Service的selector字段与Pod的labels是否匹配。Endpoint Controller会持续监听Pod事件一旦有Pod被创建且label匹配就将其IP加入Endpoint一旦Pod被删除或label不再匹配就将其IP从Endpoint中移除。这就是服务发现的实时性来源。但这也带来一个经典陷阱如果你的Pod因为Readiness Probe失败而被标记为NotReady它虽然还在Running状态但Endpoint Controller会立即将其IP从Endpoint列表中剔除导致Service流量不再转发给它。所以Readiness Probe不是可选项而是服务高可用的必选项。我见过太多团队把Liveness Probe当成万能药结果应用启动慢、健康检查超时Pod被反复重启而Readiness Probe缺失导致流量持续打到“半死不活”的Pod上用户体验雪崩。3. 核心资源间的绑定与交互从声明到落地的完整链条Kubernetes的魅力不在于单个资源有多强大而在于这些资源如何像乐高积木一样通过清晰、可预测的绑定规则组合成一个健壮的应用交付系统。下面我们以一个真实的、稍复杂的Nginx应用部署为例完整追踪从YAML声明到集群内资源联动的全过程。3.1 场景设定一个需要配置热更新、HTTPS证书、持久化日志的Nginx服务假设我们要部署一个Nginx服务要求使用ConfigMap管理Nginx配置nginx.conf使用Secret管理TLS证书tls.crt,tls.key将访问日志写入一个PersistentVolumeClaimPVC实现日志持久化通过Ingress暴露HTTPS服务配置Readiness/Liveness Probe保证健康对应的YAML资源清单会包含至少5个对象ConfigMap、Secret、PersistentVolumeClaim、Deployment、Service。现在我们一步步拆解它们如何协同工作。3.2 ConfigMap与Secret配置与密钥的“只读快照”ConfigMap和Secret的设计哲学高度一致它们都是键值对的集合用于解耦配置与镜像。但它们的存储方式和安全性有本质区别。ConfigMap的内容以明文形式存储在etcd中而Secret的内容会先经过Base64编码注意这不是加密只是编码并在API Server层面有额外的访问控制如system:authenticated组默认无权list secrets。当Deployment的Pod模板中声明挂载ConfigMap时例如volumeMounts: - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: nginx-config-mapkubelet在Pod启动前会执行以下操作向API Server查询名为nginx-config-map的ConfigMap对象提取其data字段中nginx.conf键对应的值将该值写入Node本地的一个临时文件路径类似/var/lib/kubelet/pods/pod-uid/volumes/kubernetes.io~configmap/nginx-config/..data/nginx.conf在容器启动时通过Linux的bind mount机制将这个临时文件“映射”到容器内的/etc/nginx/nginx.conf路径。这个过程的关键点在于挂载动作发生在Pod启动时且内容是“快照”。这意味着如果后续你更新了ConfigMap的内容已运行的Pod内的文件不会自动更新。要让新配置生效必须触发Pod重建如滚动更新Deployment。这也是为什么生产环境强烈推荐使用subPath挂载单个文件而非整个ConfigMap目录——避免因挂载整个目录导致Pod因ConfigMap中某个无关键值变更而被意外重启。Secret的挂载流程与ConfigMap几乎完全相同唯一的区别是kubelet会从Secret对象的data字段Base64编码或stringData字段明文由API Server自动编码中提取值。对于TLS证书这种敏感信息最佳实践是使用kubectl create secret tls命令创建它会自动将证书和私钥放入Secret的tls.crt和tls.key字段并设置正确的kubernetes.io/tls类型方便Ingress Controller识别。3.3 PersistentVolume与PersistentVolumeClaim存储的“申请-供给”契约Kubernetes的存储模型采用了经典的“申请者PVC-供给者PV”分离架构。这与计算资源CPU/Memory的调度逻辑截然不同。计算资源是“弹性”的Pod声明需要多少调度器就尝试找一个Node满足而存储资源是“静态”的PV代表集群中一块已经准备好的、具体的存储如NFS共享目录、AWS EBS卷、Ceph RBD镜像它必须先存在PVC才能去“申请”它。一个典型的PVC声明如下apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-logs-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: standard当这个PVC被创建后PersistentVolume Controller会执行以下步骤筛选查找所有处于Available状态、accessModes兼容ReadWriteOnce表示单Node读写、storageClassName匹配standard、且capacity.storage 10Gi的PV绑定一旦找到符合条件的PV比如一个名为pv-nfs-01的PVController会立即将PVC的spec.volumeName字段设置为pv-nfs-01并将PV的spec.claimRef字段指向该PVC完成双向绑定供给如果找不到现成的PV且集群配置了Dynamic Provisioning动态供给Controller会根据storageClassName关联的StorageClass调用对应的Provisioner如kubernetes.io/aws-ebs自动创建一个新的PV。这个绑定过程是一次性且不可逆的。一旦PVC绑定了某个PV除非PVC被删除否则这个绑定关系永久有效。这也是为什么PVC的accessModes和storageClassName必须谨慎选择——选错了可能导致永远无法绑定到期望的存储后端。我曾遇到一个案例团队为日志PVC错误地设置了ReadOnlyMany而他们的NFS PV只支持ReadWriteOnce结果PVC状态卡在Pending查了两天才发现是模式不匹配。3.4 Deployment与Service的联动从副本管理到流量入口Deployment和Service的联动是K8s应用交付的黄金搭档。它们的连接点就是Label Selector。Deployment的Pod模板中会定义一组labels如app: nginx, version: v1。而Service的spec.selector字段必须精确匹配这组Label。例如apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx version: v1 ports: - protocol: TCP port: 80 targetPort: 80这个selector就是Endpoint Controller的“寻人启事”。它会持续扫描集群中所有Pod只要发现一个Pod的Labels同时包含appnginx和versionv1并且该Pod的phase是Running、conditions.Ready为True就会将该Pod的IP和targetPort这里是80加入到名为nginx-service的Endpoint对象中。这个过程是异步且最终一致的。这意味着从Deployment创建Pod到Pod Ready再到Endpoint更新再到kube-proxy在Node上刷新iptables规则中间存在毫秒级的延迟。在高并发场景下如果你的应用在Pod启动后几秒内就收到了大量请求而此时Endpoint尚未更新这些请求就会被Service丢弃表现为Connection Refused。解决方案有两个一是优化应用启动速度二是为Pod配置合理的initialDelaySeconds让Readiness Probe在应用真正就绪后再报告Ready。3.5 Ingress七层路由的“智能门卫”Ingress是Kubernetes中专门负责HTTP/HTTPS七层路由的资源。它本身不提供流量转发能力而是一个配置抽象层需要一个Ingress Controller如Nginx Ingress Controller、Traefik来实际执行。你可以把Ingress想象成一份“路由说明书”而Ingress Controller就是那个严格按照说明书去配置Nginx或Traefik反向代理的“工程师”。一个Ingress资源核心是rules和tls字段apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-ingress spec: tls: - hosts: - example.com secretName: nginx-tls-secret # 指向前面创建的Secret rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: nginx-service # 指向前面创建的Service port: number: 80当这个Ingress被创建后Ingress Controller会监听API Server获取所有Ingress、Service、Endpoint、Secret对象根据Ingress的rules.host和rules.http.paths生成对应的Nginxserver块和location块根据tls.secretName从Secret中提取证书和私钥配置Nginx的SSL证书根据backend.service.name查询对应Service的ClusterIP和Endpoints将其配置为Nginx的upstream后端。这个过程再次体现了K8s的声明式哲学你只需声明“我要什么路由规则”具体的Nginx配置生成、证书加载、后端健康检查全部由Controller自动完成。但这也意味着Ingress的排错必须分两层先看Ingress资源本身是否正确kubectl get ingress -o wide再看Ingress Controller的日志确认它是否成功加载了该Ingress的配置。4. 实操避坑指南那些文档里不会写的血泪教训纸上得来终觉浅绝知此事要躬行。在Kubernetes的生产实践中有太多“看似合理、实则致命”的操作它们往往不会在第一次部署时报错而是在某个特定压力、某个特定时间点突然引爆。以下是我和团队踩过的、最痛的几个坑每一个都附带了可立即验证的诊断命令和修复方案。4.1 坑一ConfigMap/Secret更新后应用配置不生效你以为是缓存其实是挂载方式错了现象你修改了一个ConfigMapkubectl apply -f configmap.yaml后kubectl get cm显示内容已更新但Pod里的配置文件还是老的重启Pod也不管用。根因分析这是最经典的误解。很多人以为ConfigMap挂载是“实时链接”其实它是“启动时快照”。但更隐蔽的坑在于subPath的使用。如果你的挂载是这样写的volumeMounts: - name: config mountPath: /etc/config # 错误这里漏掉了subPath volumes: - name: config configMap: name: my-config那么kubelet会将整个ConfigMap的data目录作为一个Volume挂载到/etc/config。此时ConfigMap的更新确实会触发Pod的自动重启因为Volume内容变化被视为Pod Spec变更。但如果你的挂载是这样volumeMounts: - name: config mountPath: /etc/config/app.conf subPath: app.conf # 正确只挂载单个文件 volumes: - name: config configMap: name: my-config那么ConfigMap的更新不会触发Pod重启/etc/config/app.conf文件内容也不会自动更新。因为subPath挂载的是一个“硬链接”到Node上某个具体文件而ConfigMap更新后那个源文件变了但容器内的链接指向的还是旧inode。诊断命令# 查看Pod的Volume挂载详情 kubectl get pod pod-name -o yaml | grep -A 10 volumeMounts # 进入Pod检查文件的inode号两次对比 kubectl exec -it pod-name -- ls -li /etc/config/app.conf修复方案方案A推荐放弃subPath改为挂载整个ConfigMap目录然后在应用内监听文件变化如Nginx的nginx -s reload。方案B接受“不自动更新”在更新ConfigMap后手动滚动更新Deploymentkubectl rollout restart deployment/deployment-name。方案C使用第三方工具如Reloader它能监听ConfigMap/Secret变更并自动触发相关Deployment的滚动更新。4.2 坑二PersistentVolumeClaim状态卡在Pending你查遍了StorageClass却忘了看PV的AccessMode现象PVC状态一直是Pendingkubectl describe pvc pvc-name显示Waiting for a volume to be created, either by external provisioner or manually created by system administrator。你确认了StorageClass存在Provisioner也正常运行但就是不创建PV。根因分析Dynamic Provisioning失败最常见的原因不是Provisioner挂了而是你的PVC请求的accessModes与StorageClass所支持的provisioner所能提供的PV的accessModes不匹配。例如你创建了一个ReadWriteMany的PVC但你的StorageClass关联的是kubernetes.io/aws-ebs而AWS EBS卷只支持ReadWriteOnce。Provisioner看到这个不支持的模式会直接静默失败不会报错。诊断命令# 查看PVC的详细事件 kubectl describe pvc pvc-name # 查看StorageClass的详细信息重点关注provisioner和parameters kubectl get sc storageclass-name -o yaml # 查看集群中所有PV的状态确认是否有Available的PV以及它们的accessModes kubectl get pv修复方案方案A修改PVC的accessModes使其与StorageClass支持的模式一致通常是ReadWriteOnce。方案B更换StorageClass选择一个支持ReadWriteMany的Provisioner如nfs-client或rook-ceph。方案C临时手动创建一个符合要求的PV然后让PVC去绑定它。4.3 坑三Service ClusterIP能通NodePort不通你抓包到一半发现防火墙在“捣鬼”现象你的Service类型是NodePortkubectl get svc显示端口是30080:30080/TCP但在集群外部用curl http://node-ip:30080返回Connection refused。而curl http://node-ip:30080在Node本机上却能通。根因分析NodePort的流量路径是外部请求 - Node的IP:30080 - kube-proxy的iptables规则 - Service ClusterIP - Endpoint Pod。问题出在第一步和第二步之间。Node的30080端口必须对集群外部开放。这涉及到两层防火墙云厂商安全组Security Group这是第一道关卡。你必须在云控制台里为Node所在的ECS实例的安全组添加一条入站规则允许TCP协议端口范围30000-32767NodePort默认范围源IP为你的客户端IP或0.0.0.0/0。Node操作系统防火墙如firewalld/ufw这是第二道关卡。很多Linux发行版默认开启了firewalld它会拦截所有非白名单端口的入站连接。kubectl get nodes -o wide拿到Node的IP后SSH登录进去执行sudo firewall-cmd --list-all你会发现30080端口不在ports:列表里。诊断命令# 在Node上检查端口监听情况应看到kube-proxy在监听 sudo ss -tlnp | grep :30080 # 在Node上检查iptables规则应看到KUBE-NODEPORTS链 sudo iptables -t nat -L KUBE-NODEPORTS -n # 在Node上检查firewalld状态和开放端口 sudo firewall-cmd --state sudo firewall-cmd --list-ports修复方案方案A云环境登录云厂商控制台为Node安全组添加NodePort端口段的入站规则。方案BOS防火墙在Node上执行sudo firewall-cmd --add-port30000-32767/tcp --permanent sudo firewall-cmd --reload。方案C终极在生产环境永远不要直接暴露NodePort。应该使用LoadBalancer类型的Service由云厂商自动创建SLB并将流量转发到NodePort这样既安全又标准。4.4 坑四Ingress返回502 Bad Gateway你查了后端Service却发现Endpoint是空的现象Ingress Controller日志里满屏upstream connect error or disconnect/reset before headerskubectl get endpoints service-name显示ENDPOINTS列为空。根因分析Endpoint为空意味着Endpoint Controller没有找到任何一个匹配的Pod。这通常有三个原因Pod的Labels与Service的Selector不匹配最常见手抖写错一个字母。Pod的Phase不是Running比如卡在ContainerCreating镜像拉取慢、Pending资源不足。Pod的Readiness Probe失败Pod是Running状态但kubectl get pod显示READY 0/1说明Readiness Probe一直返回失败Endpoint Controller会将其IP从Endpoint中剔除。诊断命令# 1. 确认Service的selector kubectl get service service-name -o yaml | grep -A 5 selector # 2. 确认Pod的labels kubectl get pod pod-name -o wide --show-labels # 3. 检查Pod的详细状态和Events kubectl describe pod pod-name # 4. 检查Pod的Readiness Probe配置和历史结果 kubectl get pod pod-name -o wide kubectl logs pod-name --previous # 查看上次崩溃日志修复方案方案A严格比对Labels确保完全一致。方案Bkubectl describe node node-name查看Node资源状况或kubectl get events --sort-by.lastTimestamp查看集群级事件。方案C这是最需要经验的。kubectl describe pod输出中Conditions部分会显示Ready: False而Events部分会有一条Warning Unhealthy告诉你Probe失败的具体原因如Get http://10.244.1.5:8080/health: dial tcp 10.244.1.5:8080: connect: connection refused。此时你需要进入Pod检查应用是否真的在监听该端口kubectl exec -it pod-name -- netstat -tlnp | grep :8080。5. 常见问题速查表从“是什么”到“怎么办”的即时响应面对一个陌生的Kubernetes集群故障经验丰富的SRE往往不是从头开始分析而是有一套标准化的“快速诊断路径”。下面这张表格总结了生产环境中最高频的10个问题每一行都包含了问题现象、核心诊断命令、关键判断依据、以及最快捷的修复动作。你可以把它打印出来贴在显示器边框上作为日常排障的“急救手册”。问题现象核心诊断命令关键判断依据最快捷修复动作Pod状态为Pendingkubectl describe pod pod-nameEvents中FailedScheduling原因0/3 nodes are available: 3 node(s) had taint或Insufficient cpu/memory1.kubectl taint node node-name keyvalue:effect-移除污点2.kubectl edit node node-name修改allocatable或capacity3. 扩容NodePod状态为ContainerCreatingkubectl describe pod pod-nameEvents中Failed to pull image或MountVolume.SetUp failed1.kubectl get nodes -o wide确认Node网络2.kubectl get secret pull-secret-name确认镜像仓库密钥3.kubectl get pv,pvc确认存储可用Pod状态为CrashLoopBackOffkubectl logs pod-name --previous日志末尾显示panic:...或java.lang.OutOfMemoryError1.kubectl exec -it pod-name -- top查看内存/CPU2.kubectl edit deployment dep-name增加resources.limits.memory3. 检查应用启动脚本Service无法访问ClusterIPkubectl get endpoints svc-nameENDPOINTS列为空1.kubectl get pod -l selector-labels确认Pod存在2.kubectl get pod pod-name -o wide确认READY为1/13.kubectl describe pod pod-name检查Readiness ProbeService无法访问NodePortcurl -v http://node-ip:nodeport外部Connection refusedNode本机curl成功1.sudo firewall-cmd --list-ports检查OS防火墙2. 云控制台检查安全组入站规则3.sudo ss -tlnp | grep nodeport确认kube-proxy监听Ingress返回502/503kubectl get endpoints backend-svc-nameENDPOINTS为空或kubectl logs ingress-pod显示upstream connect error1.kubectl get pod -l backend-selector确认后端Pod2.kubectl get pod backend-pod -o wide确认READY3.kubectl describe ingress ingress-name检查Rules和TLS配置ConfigMap/Secret更新后不生效kubectl exec pod-name -- cat /path/to/file文件内容仍是旧的1.kubectl get pod pod-name -o yaml | grep -A 5 volumeMounts确认是否用了subPath2.kubectl rollout restart deployment/dep-name强制滚动更新PVC状态为Pendingkubectl describe pvc pvc-nameEvents中no persistent volumes available for this claim and no storage class is set1.kubectl get sc确认StorageClass存在2.kubectl get pv确认有Available状态PV3.kubectl patch pvc pvc-name -p {spec:{storageClassName:sc-name}}手动指定SCNode状态为NotReadykubectl describe node node-nameConditions中Ready: FalseEvents中KubeletNotReady1. SSH登录Nodesudo systemctl