面向 P6-P7+ 工程师 体系架构 + 问题推导 · 长文 约 9,000–11,000 字 信息截止 2026-08

K8s 核心资源理解:
从 Pod 可替换性到 Service 稳定入口的架构判断力

这不是「背 YAML 字段」的入门课,而是给已经能起 Deployment、却仍把 Pod 告警当成服务故障的工程师: 弄清谁管副本、谁管流量、谁管配置,以及为什么单实例重启在正确模型下不应等于业务不可用。

主线风格:问题现场 + 体系架构 版本假设:Kubernetes 1.28+ 证据等级:官方文档优先,标注推断与生产经验
问题现场 · 复合场景

1Pod 重启了两次,为什么成功率几乎没掉?

复合场景 · 综合多起值班对话,非指代具体单一事件 TYPICAL SCENARIO

某次发布后的周一早晨,值班同学收到告警——订单服务的 Pod 在凌晨重启了两次,监控大盘上「Pod 重启次数」飘红。 业务方立刻追问:「服务是不是挂了?要不要回滚?」同学执行 kubectl get pods,看到旧 Pod 已终止、新 Pod 处于 Running, 却说不清对外流量是否中断;再查 Service,ClusterIP 一直存在,却不清楚请求打到了哪个后端。网关侧 HTTP 成功率几乎没有明显下降—— 矛盾信息加剧了「要不要半夜叫醒架构师」的决策焦虑。

同一周的另一次事故则相反:ClusterIP 可解析、Service 对象健康,但调用超时。排查发现新版本把标签键从 app 改成了 app.kubernetes.io/name,Service selector 仍匹配旧键,Endpoints 为空。两侧都在「看对象是否存在」, 却没有先回答同一个架构问题:当前观测信号属于实例层、副本层,还是流量发现层?

这类困惑的根源,是把 Pod(最小调度与运行单元)误当成服务本身。在 Kubernetes 1.28+ 的控制面模型里,Pod 是可被替换、可被重建的实例; 真正对外承诺可用性的,是 Deployment(期望副本与发布策略)与 Service(稳定访问入口 + 就绪后端集合)的组合。本篇从上述复合场景切入, 目标不是罗列资源名词,而是建立可辩护的分层判断:现象落在哪一层、该改哪一类对象、哪些信号值得吵醒人。

若你已有 Docker 经验,可以把 Pod 类比为「绑在一起跑的一组容器」,但不要把 docker run 的心智原样搬到集群: Docker 管单机进程,Kubernetes 管跨节点的期望态与自愈。Deployment 回答「世界应该长什么样」,各控制器与 Kubelet 让实际状态逼近该描述; Pod 只是描述里的一个可替换实例。探针、配额、优雅停机与 PDB 的生产细则见系列 T17;本篇只在边界点明联动。

分享结构:第二章给出控制面与资源分层地图;第三至六章分别拆 Pod、Deployment、Service、ConfigMap/调度; 第七章划清适合与不适合;第八章给值班可用的排障序;第九章收束为决策矩阵与相邻知识地图。机制描述以官方文档为机制共识, 时序窗口与告警经验标注为生产经验;云厂商 LoadBalancer 实现差异仅讨论通用语义。

读者对象不是「第一次听说 Pod」的新手,而是已经能提交工作负载、却在故障归因上反复踩层的工程师。若你仍需要逐字段解释 apiVersion,建议先完成官方教程再读本文;若你已经写过滚动发布却解释不清「为何成功率没掉」,本文就是为你准备的。目标产出是可在评审会上辩护的判断,而不是又一份字段速查。

证据等级约定如下。标注为机制共识的内容,对应 Kubernetes 1.28+ 文档中的对象语义与控制器行为;标注为生产经验的内容,来自多起值班与发布复盘的常见模式,数量级与时序窗口必须用你自己的集群复测;标注为作者解释的内容,是把机制串成决策语言的推理,允许被更严证据修正。文中不编造无来源的默认值性能数字。

版本基线固定在 1.28+:EndpointSlice 为默认切片机制、拓扑分布约束已广泛落地、不可变 ConfigMap 可用于流程治理。若你的集群低于该基线,仍可借用分层判断,但个别 API 细节请以所在版本文档为准,不要把本文字段示例直接当生产模板粘贴。生产清单与探针组合请以 T17 为准继续下钻;日常排障命令链则可衔接到 T18,形成完整学习闭环。

常见误区

把「Pod 重启次数上升」直接等同于「服务故障」,或把「Service 对象存在」等同于「有可用后端」。前者忽略副本与滚动策略,后者忽略 selector 与 Readiness。

本文刻意把「入门罗列」压到最低:不会逐一背诵所有字段默认值,而会反复训练同一肌肉——看见现象时先分层,再选对象,再谈命令。字段可以查文档,分层一旦错了,查得越仔细越会在错误方向上自信。

还可以把冲突翻译成三个可验证问题。第一,同一时刻就绪副本数是否仍满足 Deployment 合同?若合同仍满,实例更替多半是正常收敛。第二,Service 的 EndpointSlice 是否为空或后端数骤降?若为空,问题在发现层而非「Pod 名字变了」。第三,业务 SLI(成功率、延迟)是否同步恶化?若 SLI 平稳而只有 restartCount 飘红,应降噪告警而不是回滚版本。三个问题都答得清楚,值班会议才会从情绪升级变成证据升级。

团队协作上常见另一种割裂:应用研发只盯业务错误码,平台组只盯控制器事件,网关组只盯入口成功率。三者不同步时,最容易出现「对象看起来都健康,但用户已经在报障」或相反的「对象在抖动,用户无感」。因此本文强调可辩护:任何半夜决策都要能同时解释副本合同、就绪后端与业务 SLI,而不是只解释某一个面板。

体系总览 · 概念地图

2声明式回路:谁声明期望,谁逼近实际

在动手改 YAML 之前,先回答:控制面里有哪些角色、各自解决什么约束?可以把日常工作负载理解成四层分工—— Workload 控制器管「要几个、怎么升级」;Pod 模板描述「每个实例长什么样」;Service 提供「稳定名字与负载均衡」; ConfigMap / Secret 把配置从镜像剥离。Scheduler 在创建时决定「落在哪个 Node」。命名空间提供软隔离,标签是对象之间的胶水: selector 只认 labels,annotations 不参与匹配。

从体系视角看,Kubernetes 是声明式 API + 控制器回路:你写期望状态,控制器持续对比期望与观测,通过创建、更新、删除收敛差距。 因此你很少「直接操作 Pod 做发布」,而是改 Deployment 等上层对象。读懂 kubectl get events 的前提,是先判断事件属于调度、拉取、探针还是控制器收敛。

声明式带来的心智转变是:你描述结果,而不是编排步骤。命令式思维会问「先删哪个再起哪个」;声明式思维问「期望副本与模板是什么,当前差在哪里」。滚动更新、自愈与扩缩都是收敛过程的不同表现。若团队仍用「登录节点重启进程」的方式操作集群,会不断与控制器打架——你删掉的 Pod 会被立刻补回,你手工改过的容器文件系统会在重建后消失。理解回路,是停止无效操作的第一步。

图 1 · 核心资源归属链与流量发现路径
自制示意图
控制面对象(API Server / Controllers) Deployment replicas / strategy ReplicaSet 维护匹配 Pod 数 Service ClusterIP + selector ConfigMap env / volume 注入 数据面实例(Node / Kubelet) Pod A Pod B Pod C EndpointSlice / Endpoints 仅收录 Ready 的 Pod IP:Port selector 匹配 挂载 / env kube-proxy / CNI 数据面 把 VIP 规则同步到节点
关键点:副本归属链(Deployment→ReplicaSet→Pod)与流量发现链(Service→EndpointSlice→就绪 Pod)是两条回路;值班时先分清信号落在哪条链。
来源:Kubernetes Concepts · Workloads / Services(workloads · service)。版本假设 1.28+。

对象分层与不变量

下表是后续排障的框架锚点——遇到现象时,先判断属于哪一层。

层级典型资源核心不变量常见误操作
工作负载Deployment / StatefulSet / DaemonSet声明期望副本与更新策略直接改裸 Pod 当发布
实例Pod一次调度、一组共享网络的容器把 Pod 名 / IP 当长期主机标识
网络入口Service / Ingress稳定 DNS / VIP,selector 匹配标签selector 漂移导致无 Endpoints
配置ConfigMap / Secret配置与镜像解耦,更新语义视注入方式大配置塞 env 导致启动失败
调度Affinity / Taint / TopologySpreadPod 必须落到满足约束的 NodePending 误判为「服务挂了」
已核验事实

Kubernetes 1.28+ 默认以 EndpointSlice 承载后端切片信息,同时仍维护兼容用的 Endpoints 对象。排障时 kubectl get endpointskubectl get endpointslice 择一即可,后者信息通常更完整。

作者解释

「Pod 重启不等于服务不可用」成立的前提是:副本充足、滚动策略不挖空容量、Readiness 真正代表可接流量。任一前提缺失,实例更替就会变成用户可感知故障——那是配置问题,不是 Pod 模型缺陷。

控制面与数据面:观测信号为何会打架

控制面通常包含 API Server、etcd、Scheduler 与各类 Controller Manager。你的 kubectl apply 先到 API Server,持久化后由 Deployment 控制器创建或调整 ReplicaSet,再由 ReplicaSet 控制器创建 Pod;Scheduler 绑定 Node;Kubelet 拉镜像并启动容器。数据面则是各 Node 上的 Kubelet、容器运行时、kube-proxy 或 CNI——它们执行「跑容器、转流量」。Pod 重启次数来自 Kubelet 上报,是否对外提供 Service 取决于 Endpoint 控制器与探针状态。两者分属不同观测面,不能混读成一个红灯。

命名空间提供多租户软隔离:Service DNS 形如 svc.ns.svc.cluster.local,同名 Deployment 在不同命名空间互不影响。标签与注解的职责必须拆开:selector 只认 labels;annotations 适合放发布原因、变更单号、构建号,不参与匹配。团队若在项目中期才统一标签键,往往已经积累一批「Service 还在、后端漂移」的隐性债。把标签规范当作接口合同,比事后口头对齐便宜得多。

从组织语言看,很多事故复盘写成「K8s 不稳定」,至少要拆成四类:调度失败(Pending)、实例崩溃(CrashLoop)、发现断裂(无 Endpoints)、容量合同被挖空(滚动或驱逐过猛)。前三类对应不同对象与不同修复动作;最后一类往往要同时看 maxUnavailable、PDB 与节点维护窗口。把复盘标题写精确,是架构判断力的一部分。

核心机制 · Pod

3Pod:可替换实例,不是服务身份

Pod 是调度的最小单位,不是「一个容器」的同义词:同一 Pod 可运行多个容器,它们共享网络命名空间(localhost 互通)、IPC,并可挂载相同 Volume。 Pause / infra 容器持有网络栈,业务容器与 sidecar 随后加入。Pod IP 在重建后会变化,因此应用层硬编码 Pod IP 是反模式;稳定访问应走 Service DNS。

图 2 · Pod 内共享网络命名空间与卷
自制示意图
Pod(共享 netns / 可选共享 Volume) 单一 Pod IP · 端口空间全局于 Pod app 容器 监听 :8080 业务主进程 sidecar 127.0.0.1:8080 日志 / 代理 init 容器 顺序执行完毕 后启动业务容器 Volume:配置文件 / emptyDir / PVC(可在容器间共享)
关键点:同 Pod 内容器靠 localhost 协作;跨 Pod 通信必须走 Service 或明确的网络策略,不要假设 Pod IP 稳定。
来源:Kubernetes · Pod Lifecycle(pod-lifecycle)。

生命周期:phase 粗、Ready 细

phase 是粗粒度汇总;能否进 Service 后端,看 conditions 里的 Ready / ContainersReady。 Running 不等于可接流量。Deployment 管理的 Pod 失败后通常被重建,而不是长期停留 Failed——那是 Job 类工作负载的常态。

阶段 / 状态含义值班关注点
Pending已接受,尚未调度或镜像拉取中Events:资源不足、污点、镜像拉取失败
Running至少一个容器在运行不等于可接流量,看 Ready
Succeeded / Failed批处理终止态长期服务失败后应被控制器替换
Unknown与 Node 通信丢失结合 Node Ready 与网络分区判断

容器重启 vs Pod 重建

restartCount 累计的是容器级重启:同 Pod 内容器崩溃后由 kubelet 按 restartPolicy 拉起,IP 往往不变。 Pod 级重建则是对象删除后新建,IP 变化。前者多指向应用 bug 或探针误杀;后者多指向滚动发布、节点驱逐或人为删除。 仅凭「重启次数大于零」不能推出服务不可用,必须同时看就绪副本数与 Endpoints 后端数量。

生产经验

手动 kubectl delete pod 后 Deployment 会拉起新实例,这是预期自愈,不是缩容。正确缩容应改 spec.replicas。未配置 PDB 时,节点 drain 可能同时驱逐多个副本,容量缺口会被误读成「K8s 不稳定」。

控制器边界也要提前划清:需要稳定网络标识与卷绑定用 StatefulSet;每节点一份守护进程用 DaemonSet;一次性任务用 Job / CronJob。 选错控制器,运维动作与存储语义都会偏离预期。本篇以 Deployment 为主干,其余类型只需建立「何时换控制器」的判断阈值。

Init、重启策略与 QoS 直觉

spec.initContainers 按顺序执行,全部成功后业务容器才启动,常用于等待依赖、执行迁移或下载配置包。Init 失败会触发 Pod 重启,受 restartPolicy 约束:Deployment 管理的 Pod 默认为 Always。OnFailure 或 Never 更常见于 Job。不要把 Job 的终止语义套到长期服务上,否则你会把「应该被替换」的失败误读成「任务成功结束」。

每个容器可声明 resources.requestslimits。未设 requests 的 Pod 可能被堆到已繁忙节点;limits 过低会 CPU 节流或 OOMKill。QoS 类(Guaranteed / Burstable / BestEffort)影响内存压力下谁先被驱逐——这与 T17 生产稳定性直接相关。本篇只需建立直觉:Pod 规格不仅影响「能不能跑」,也影响「压力来临时谁先死」。容量规划若只看副本数、不看 requests,会在节点打满时突然暴露。

共享网络意味着端口冲突发生在 Pod 内而不是节点上的「偶然重名」。sidecar 若与主容器争用同一端口,表现是启动失败或连接错乱,而不是 Service 层的负载不均。排障时先问「故障容器是否与主容器同 Pod」,再问「是否跨 Pod 通信」。这条分流能避免把 sidecar 问题误判成集群网络故障。

还有一类高频混淆:把「容器退出」理解成「服务下线」。在多副本且探针正确时,容器退出触发的是替换,服务身份仍由 Service 持有。真正危险的是退出风暴——新实例同样快速失败,就绪集合被掏空。此时 restartCount 与错误率会一起上升,决策应转向版本回滚或配置修复,而不是安慰「重启是正常的」。正常与事故的分界,是就绪集合是否还能履行容量合同。

核心机制 · Deployment

4Deployment:期望副本与滚动窗口的合同

Deployment 面向无状态应用,通过管理 ReplicaSet 间接管理 Pod。你改模板(镜像、环境变量等),控制器按策略滚动替换,而不是一次性清空全部实例。 spec.replicas 是合同;ReplicaSet 持续把匹配标签的 Pod 数收敛到期望值。selector 应视为不可随意改动的契约——发布流程改 template,不改 selector。

图 3 · RollingUpdate:新 RS 接管、旧 RS 缩容
自制示意图
时间 → 更新镜像 tag 创建新 ReplicaSet 新 Pod Ready 旧 Pod 终止 期望 replicas=3 · maxUnavailable=0 · maxSurge=1 窗口内最多短暂出现 4 个 Pod(3 就绪目标 + 1 surge) 旧 RS 逐步降副本,新 RS 逐步升副本,直至旧 RS=0 Service selector 不变 → Endpoints 随 Ready 集合滑动更新
关键点:滚动发布是「容量合同」在时间轴上的展开;卡住时根因多在新 Pod 无法 Ready,而不是 Deployment 对象损坏。
字段作用无状态 HTTP 经验起点
strategy.typeRecreate 或 RollingUpdate默认 RollingUpdate
maxSurge允许超出期望的临时副本25% 或 1(小副本数)
maxUnavailable允许不可用的副本上限0(发布中不挖空容量)
revisionHistoryLimit保留旧 RS 数量10,便于 rollout undo

Recreate 会先杀光旧 Pod 再起新 Pod,存在零副本窗口,只适合可停机维护。单副本时即使 maxUnavailable: 0, 配合 maxSurge: 1 也会短暂出现新旧并存——这是正常现象。回滚 kubectl rollout undo 本质是再次改模板指回旧 revision, 不能撤销已在数据库执行的迁移;兼容性策略必须在应用层单独设计。HPA 只改 Deployment 的 replicas 字段,Pod 创建仍走 ReplicaSet 回路。

把滚动参数当成容量合同来读,会比背默认百分比更有用。maxUnavailable: 0 承诺的是「更新过程中就绪副本尽量不跌破期望」; maxSurge 承诺的是「允许用额外资源换更平滑的替换」。集群资源紧张时,surge 起不来会导致发布变慢甚至卡住,这时问题在节点容量,不在 Deployment「坏了」。评审发布策略时,应同时看应用副本合同与集群剩余可调度资源,否则合同写得很漂亮,执行时无处落子。

常见误区

rollout status 长时间卡住时,优先查新 Pod 的 ImagePullBackOff、Readiness 永不通过、配额打满。把时间花在「重启 Deployment 控制器」通常是错层操作。

Revision 语义与「回滚不能回数据库」

每次 Pod 模板变更会触发新 ReplicaSet。Deployment 的 status.observedGenerationmetadata.generation 对齐,表示控制器已处理该版本。回滚解决的是镜像或配置版本问题,不能撤销已在数据库执行的迁移,也不能撤销已发出的外部副作用。因此「可以 rollout undo」不等于「可以任意向前不兼容」。应用层必须提供兼容窗口:先扩读、再切换写、最后清理旧字段。

发布失败判读应有固定顺序:新 Pod 是否拉起;是否长期 Pending;是否 Running 但不 Ready;Ready 后错误率是否上升。前三步卡在工作负载与实例层,第四步才进入业务与依赖层。很多团队在第四步才第一次看日志,却已经在群里讨论回滚——时间线反了。把顺序写进值班手册,比增加更多面板更有效。

HorizontalPodAutoscaler 基于 CPU、内存或自定义指标自动改 replicas,仍由 Deployment 执行扩缩。新手常误以为 HPA 直接创建 Pod。扩缩期间 Service selector 不变,Endpoints 随副本增减更新,对外 DNS 保持稳定。这也再次说明:稳定的是服务身份,变化的是实例集合。容量事件与发布事件在监控上可能长得很像,要用 rollout 状态与 HPA 事件区分。

核心机制 · Service

5Service:稳定名字背后的就绪集合

Service 提供稳定的 ClusterIP(除非 Headless)、DNS 名与端口映射。它本身不「转发业务逻辑」——通过 spec.selector 关联 Pod, 由 Endpoints / EndpointSlice 控制器维护「就绪 Pod IP:Port」列表,再由 kube-proxy 或 CNI 数据面编程规则。 Service 如何找到 Pod? 答案是标签完全匹配 + Ready 为真,而不是「同命名空间就自动发现」。

图 4 · 集群内请求:DNS → VIP → 就绪 Pod
自制示意图
客户端 Pod curl / gRPC CoreDNS svc.ns.svc ClusterIP 稳定 VIP:Port 数据面 iptables/IPVS Pod Ready Pod Ready NotReady NotReady / 标签不匹配的实例不会进入 EndpointSlice,VIP 仍在但可能无后端
关键点:稳定的是名字与 VIP;变化的是后端集合。单 Pod 重启时,列表动态摘除与纳入,服务身份保持不变。
来源:Kubernetes · Service · EndpointSlices(service · endpoint-slices)。
类型访问方式典型场景选型注意
ClusterIP仅集群内 VIP + DNS微服务互调、Ingress 后端默认类型;外部不可直达
NodePort每节点静态高端口开发测试、无云 LB 的边缘默认端口段 30000–32767,需防火墙
LoadBalancer云 LB 指向节点/后端公网 API 入口实现因云而异;常配合 Ingress

Headless(clusterIP: None)不分配 VIP,DNS 返回就绪 Pod A 记录,适合 StatefulSet 或客户端自选实例。 sessionAffinity: ClientIP 可做粘滞,但有超时与扩缩容失效窗口。externalTrafficPolicy: Local 可保留源 IP, 却要求本节点存在就绪 Pod,否则流量被丢——这是架构权衡,不是实现 bug。

端口、协议与「通了但不对」

即使 selector 正确、Pod 也 Ready,仍可能出现「通了但业务不对」。最常见的是 targetPort 与容器监听端口不一致: 健康检查若打在另一个端口上,Ready 为真,业务端口却无进程;或容器监听 8080,Service 却指向 80 且未做正确映射。排障时应用 kubectl get endpoints 看后端端口,再与容器 containerPort 和对端配置交叉验证。网络「能连通」不等于「协议与端口合同一致」。

命名空间跨度也容易被忽略:客户端若写错 DNS 后缀,可能解析到同名但错误命名空间的服务,表现为间歇性数据错乱而非连接失败。平台若允许多环境共存于同一集群,必须把命名空间当作合同的一部分写进调用约定,而不是假设「短名总会落到我想要的地方」。

生产经验

滚动发布期间偶发 502,常见根因是 Readiness 只检查「进程存活」而未覆盖缓存预热或依赖连通。Endpoints 只反映探针结果,不保证业务完全就绪。探针组合细则见 T17。

复合场景:VIP 还在,后端为空

再展开第一章提到的发现层事故。某内部 gRPC 服务升级后,ClusterIP 可解析但调用超时。表面看 Service 对象健康,细查发现新 Pod 标签键变更,Service selector 仍匹配旧键,EndpointSlice 为空。修复路径是统一标签规范或同步改 selector,而不是把类型改成 NodePort「碰运气」。这说明「Service 存在」远不等于「有可用后端」——判断必须落到就绪集合。

另一个复合场景发生在滚动窗口:个别用户报偶发 502。链路显示新 Pod 的 Readiness 仅检查健康端口返回成功,但应用尚未完成本地缓存预热;Endpoints 已纳入新 Pod,数据面开始转发,真实业务请求仍失败。解决方向是让探针表达「可接流量」,而不仅是「进程还活着」。把这类问题归咎于 kube-proxy,往往会浪费一整轮网络排障。

关于数据面模式:iptables、IPVS 与部分 eBPF 实现都在消费同一类就绪后端信息,差异主要在性能与规则规模,不改变「先有 Ready,才有后端」的语义。排障时优先验证 EndpointSlice 内容,再怀疑节点上的代理组件。绝大多数业务故障停在标签、探针与端口,而不是代理实现细节。

核心机制 · 配置与调度

6ConfigMap 注入语义,与 Pod 为何 Pending

ConfigMap 存放非敏感键值或文件片段。两条主注入路径语义不同:环境变量在容器启动时固化,变更后通常需重启 Pod; Volume 挂载在未使用危险 subPath 模式时可被 kubelet 异步更新文件,但应用必须支持热加载。Secret 语义类似,面向凭证与证书。 单对象体量受 etcd 约束,常见上限约 1MiB;超大配置应拆分或外置。1.28+ 可用 immutable: true 强制「改配置 = 新对象 + 滚动」。

图 5 · 调度过滤与污点/亲和约束
自制示意图
待调度 Pod Pending Filter 资源 / 污点 / 亲和 PVC / 端口冲突 Score 打分选最优 Node 再 Bind Node 可调度 被 Taint 过滤 topologySpreadConstraints(1.28+ 常用)在 zone/hostname 上控倾斜,比手写反亲和更易读 Pending 是调度域问题,不是「服务崩溃」的同义词
关键点:看到 Pending 先 describe Events,再查污点、亲和、PVC 与配额;不要第一反应滚动重启 Deployment。
注入方式优点限制
环境变量简单,应用直接读 env大体积不适合;变更需重启
Volume 挂载适合配置文件,可热更新subPath 与权限行为需小心

亲和性可把副本打散或聚合;污点排斥一般负载,只有声明对应 toleration 的 Pod 才能进入专用池。 topologySpreadConstraintsmaxSkew 描述可接受的倾斜,适合多可用区无状态服务。集群若缺少拓扑标签,约束会失效——这属于平台基线,而不是应用 YAML 单独能解决的问题。

已核验事实

调度器先 Filter 再 Score;Taint 的 NoSchedule 阻止新调度,不等价于立刻驱逐已有 Pod(驱逐由 NoExecute 与驱逐控制器等路径处理)。

配置变更的发布纪律

把配置当代码的团队,仍可能在注入方式上翻车:以为改了 ConfigMap 就等同于所有副本立即看见新值。环境变量路径下,旧 Pod 会继续带着旧环境运行,直到被滚动替换;若只有部分副本被重启,集群内会出现配置双版本并存。对开关类配置,双版本可能是兼容策略;对格式不兼容的配置,双版本就是事故。发布说明里应写清「配置生效依赖重启还是热加载」,而不是只写「已更新 ConfigMap」。

不可变 ConfigMap 适合强制流程:创建后不允许改 data,变更必须新建对象并修改工作负载引用。代价是对象名或哈希后缀变多;收益是减少「有人改了线上配置但没滚动」的漂移。是否启用取决于变更频率与审核流程。对高频开关,更常见的路径是配置中心;对低频基础配置,不可变对象足够清晰。

调度排障的框架建议固定为:看到 Pending 先读 Events,再 describe node 看 allocatable 与 taint,再核对亲和与拓扑标签是否在集群中真实存在。不要第一反应删除 Pod「重试调度」——若约束本身不可满足,删除只会制造新的 Pending。把「约束不可满足」与「瞬时资源不足」区分开,才能决定是改 YAML、改节点池,还是等容量回收。

适合 · 不适合

7资源选型边界:什么时候该换模型

核心资源「都会用」之后,真正拉开差距的是知道什么时候不该继续硬套 Deployment + ClusterIP。 下面用适合 / 不适合把常见误配钉死,避免把有状态、节点级、批处理问题塞进无状态模板。

适合继续用 Deployment + ClusterIP

  • 无状态 HTTP / gRPC API,实例可随意替换
  • 需要滚动发布与快速回滚 revision
  • 集群内稳定服务发现,入口另由 Ingress / 网关承接
  • 配置可外置,镜像与环境解耦

不适合硬套这套组合

  • 强依赖稳定网络标识与专用盘:改用 StatefulSet
  • 必须每节点一份 agent:改用 DaemonSet
  • 需要客户端点名具体实例:评估 Headless
  • 单副本却要求高可用:先加副本与 PDB,而不是调探针参数自我安慰

决策矩阵:继续 / 迁移 / 选型

信号继续当前模型迁移 / 改选型关键判据
Pod 重启告警但 SLI 平稳 继续,降噪实例级告警 若单副本则补副本 看 Endpoints 数量与错误率
Service 在、调用全超时 修 selector / Ready 不要先换 LoadBalancer 类型 endpoints 是否为空
需要磁盘身份与有序名 继续用 Deployment 会疼 迁到 StatefulSet + Headless 是否绑定 PVC 与固定 DNS
发布必须停机切换 可用 Recreate 若要在线则改 RollingUpdate 业务是否允许零副本窗口
配置频繁变更 env + 滚动可接受 改 volume 热加载或配置中心 应用是否支持 reload
作者解释

「选型」不是追新 API,而是减少错误层操作:标签漂移用规范与策略工程解决,比换成另一种 Service 类型更有效;Pending 用调度约束治理,比加大 replicas 更能对症。

再补一张「继续还是迁移」的直觉尺。若痛点是发布过程容量抖动,优先调 maxUnavailable / maxSurge 与就绪探针,而不是换控制器;若痛点是实例需要稳定身份,再迁 StatefulSet;若痛点是每个节点都要跑采集剂,才是 DaemonSet 的主场。很多人在错误层解决问题,是因为工具最顺手的操作是「再 apply 一份 Deployment」。顺手不等于对症。

对外暴露时也要克制:不是所有服务都该拥有独立 LoadBalancer。多数内部 API 保持 ClusterIP,由统一 Ingress 或网关做 TLS、鉴权与限流,能减少云资源碎片与安全暴露面。NodePort 适合过渡与调试,长期裸露高端口而不配防火墙,是把平台便利当成了安全策略。选型表里的「注意」列,本质上都是成本与风险列。

配置侧同样存在「过度解耦」:把所有参数都外置到 ConfigMap,却没有版本化与回滚策略,会让配置成为第二条不受控发布通道。镜像版本与配置版本应能关联到同一次变更单。若配置可以绕过流水线直改生产,Deployment 的 revision 历史就失去了审计意义。核心资源理解也包括:谁有权改哪一类对象。

生产实践 · 排障序

8从误解到正确心智:一次发布走读与值班框架

假设三副本 order-api 执行镜像升级,策略为 maxSurge=1maxUnavailable=0。 集群先多起一个新版本 Pod,待 Ready 后终止一个旧 Pod,循环至全部替换。ClusterIP 与 DNS 不变; EndpointSlice 在纳入与摘除之间滑动,存在受 grace period 与探针影响的秒级窗口。监控上同时出现重启/更替事件、rollout 进度、后端列表变化——三者描述同一发布,不是三次独立故障。

若业务方只订阅「restartCount 增加」,几乎每次正常发布都会误报。更合理的服务级信号是:就绪后端数持续低于期望、HTTP 5xx 上升、或 Deployment unavailable 副本长期非零。 把告警从实例级提升到服务级,是从虚拟机思维迁到 Kubernetes 思维的关键一步。

最小 YAML 对照四问

  1. Deployment selector.matchLabels 与 Pod template labels 是否一致?
  2. Service selector 是否能匹配到 Pod labels?
  3. targetPort 是否等于容器实际监听端口?
  4. ConfigMap 名称是否存在且与工作负载同命名空间?
# 排障序(Kubernetes 1.28+)
kubectl get deploy,rs,pods -l app=order-api -o wide
kubectl get svc,endpoints,endpointslice -l app=order-api
kubectl describe pod <pod>   # Events / Conditions
kubectl logs <pod> -c app --previous
kubectl rollout status deployment/order-api
kubectl get cm order-api-config -o yaml
步骤命令意图关注字段
1. 期望态get deploy,rs,podsREADY、DESIRED/CURRENT
2. 有无后端get svc,endpointsENDPOINTS 是否为空
3. 单实例describe podEvents、Conditions
4. 应用证据logs --previous崩溃前栈与配置
5. 配置对齐get cm -o yaml与 env/挂载交叉验证

新手误区清单(架构约束版)

误区正确理解落层
Pod 重启 = 服务不可用看就绪副本与错误率,而非单次重启流量发现 + 副本
删 Pod 当作发布或缩容发布改模板;缩容改 replicas工作负载
Service 创建即可访问必须有 Ready 且 selector 匹配网络入口
Pod IP / 名当稳定标识用 Service DNS 或 StatefulSet 有序名实例
改 ConfigMap 立即全局生效env 需重启;卷视热加载能力配置
Running = 可接流量以 Readiness 进入 Endpoints 为准探针边界
Pending = 应用崩溃先查调度、镜像、污点、PVC调度
生产经验

标签键应在项目初期约定(如推荐标签 app.kubernetes.io/name),并用策略引擎防止 Service selector 与模板漂移。规范成本远低于每次事故后的口头对齐。

验证层:亲手证明「删 Pod 不等于断服」

在练习集群应用最小栈后,执行滚动状态观察,再从集群内访问 Service DNS;随后手动删除一个 Pod,重复访问应仍成功。把这个实验做成新人上手必做项,比讲解十页概念更能打掉虚拟机心智。反过来,也可以故意把 Service selector 改错,观察 EndpointSlice 变空、访问失败——用失败对照强化「发现层」概念。

值班手册里建议把命令固定成「从上到下」:先工作负载期望态,再网络后端,再单 Pod,再日志,最后配置。很多人习惯先 logs,结果在错误副本上读到过期线索。顺序本身就是架构:先确认系统合同,再阅读实例细节。若 READY 已满且 Endpoints 正常,却仍有业务错误,才应把重点转向依赖、数据与代码路径,而不是继续在 Kubernetes 对象上打转。

与监控的衔接上,实例级指标仍有价值,但必须降级为诊断信号,而不是唯一寻呼信号。寻呼应绑定服务级:可用性、错误率、就绪后端缺口持续时间。发布窗口内允许实例更替噪音,不允许后端空洞被静默吞掉。这条策略需要研发、平台与 SRE 共同签字,否则告警系统会被迫在「太吵」与「太哑」之间振荡。

决策框架 · 收尾

9决策框架、相邻知识地图与延伸

回到第一章的复合场景:先看 Service 后端是否为空,再看 Deployment 就绪副本是否低于合同,最后才下钻单个 Pod 的 Events 与日志。 三条问题分别对应流量发现层、副本层、实例层。答完再决定是修标签、补副本、调探针,还是回滚 revision——而不是被 restartCount 推着走。

决策问题优先证据常见动作
要不要半夜回滚?错误率、就绪后端数、rollout 状态SLI 平稳则观察;后端空洞则回滚或修 Ready
要不要改 Service 类型?客户端是否在集群外、是否已有 Ingress多数内部调用保持 ClusterIP
要不要换控制器?是否需要稳定身份 / 每节点一份 / 批处理对症迁 StatefulSet / DaemonSet / Job
配置怎么发?是否必须热更新、体积是否超限选 env 滚动或 volume / 外置中心

相邻知识地图

  • T17 生产稳定性配置:Requests/Limits、Liveness/Readiness/Startup、优雅停机与 PDB——把本篇的「可接流量」落到探针与配额。
  • T18 简单运维实战:日志采集、监控指标、节点维护与巡检——把本篇排障序扩展为日常 SOP。
  • T15 镜像工程化:镜像层与构建缓存决定拉取时长,直接影响滚动窗口能否收敛。
  • T10 容器化 JVM:出现 OOMKilled 时,联动容器内存与 JVM 堆外账本,而不是只加 replicas。

练习建议:在 1.28+ 的 kind / 练习集群应用最小 Deployment + Service + ConfigMap,观察 kubectl get pods -wkubectl get endpoints -w 在滚动与手动删 Pod 时的联动;刻意制造一次 selector 漂移,亲手看到「VIP 还在、后端为空」。 比背字段更能固化架构直觉。

若你负责平台规范,可以把本文误区清单转成合并请求检查项:标签是否使用约定键、Service selector 是否与模板一致、是否声明多副本、滚动参数是否显式写出、ConfigMap 注入方式是否与变更流程匹配。自动化策略(如 Kyverno / OPA)适合拦硬错误;架构判断仍需人在评审里回答「这份合同被违背时用户如何感知」。工具代替不了分层思考,但可以减少明显漂移。

最后提醒边界:本文讨论的是核心工作负载与服务发现语义,不展开网络策略细节、存储类选型、多集群流量与服务网格。那些主题会改变数据面实现,但很少改变「期望态、实例、就绪集合」这三层合同。先把三层合同用熟,再引入网格与多集群,否则会把更多名词叠在错误心智上。

已核验事实

官方文档将 Pod 定义为可调度的最小部署单元,并将 Service 定义为暴露一组 Pod 的抽象。可用性由「副本合同 + 就绪后端集合」共同表达,而不是由单个 Pod 对象的存活单独表达。

收束成一句话:Kubernetes 核心资源教给你的不是更多 YAML 字段,而是一套分层合同——Deployment 合同管期望容量与变更方式,Pod 合同管单次运行与共享资源,Service 合同管稳定身份与就绪集合,ConfigMap 合同管配置如何进入进程,调度合同管实例能否落地。值班、评审与选型,都是在问「现在违背了哪一份合同」。

当你能在五分钟内把一个红灯归类到正确合同,并指出该改标签、副本、探针、控制器还是节点池时,这篇的目标就达到了。更细的探针组合、资源配额与优雅停机,交给 T17;更长的运维 SOP 与巡检,交给 T18。核心资源篇负责把地图画对,避免你在错误的层上用力。

也可以把决策框架压缩成一句值班口令:「先合同,后实例」。合同指 Deployment 期望与 Service 就绪集合;实例指某一个 Pod 的 Events 与日志。口令不是贬低实例证据,而是防止一上来就被单容器栈追踪带走整个决策。大多数可恢复故障,在合同层就能定位到正确动作;需要深入实例时,你已经知道自己在验证哪一种假设。