尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
K8s控制器实战:ReplicaSet副本控制与Deployment滚动更新
Kubernetes 里 Pod 是调度的最小单位但真正让业务稳稳跑着的从来不是 Pod 本身而是站在它背后的那批 Pod 控制器。手动 kubectl run 出来的裸 Pod节点一挂它就没了副本数少了没人补镜像要升级得一个个删——这套玩法在生产环境撑不过一个晚上。ReplicaSet、Deployment、StatefulSet、DaemonSet、Job、CronJob 这一组控制器干的事说白了就一句话把“我要几个副本、长什么样、什么时候该有”写成声明式配置剩下的对账工作交给控制循环自动完成。这篇是 Kubernetes 系列第八篇只啃其中使用频率最高的两个——ReplicaSet 和 Deployment。它们覆盖了日常八成以上的无状态业务发布场景把这两块吃透再说 StatefulSet 和 Job 会轻松很多。无论你是刚接触 K8s 两三天的新手还是已经在线上维护过几十个 Deployment 的老手下面这些字段拆解、参数计算和踩坑记录应该都能直接拿去用。1. 先把“控制器”这三个字掰开看1.1 裸 Pod 在生产环境为什么活不过一个晚上很多人刚学 K8s 时会被 kubectl run 或一个最简 Pod YAML 骗到觉得“容器跑起来了K8s 也就这样”。问题是裸 Pod没被任何控制器管理的 Pod在集群里是个孤立对象它没有“备份”也没有“替身”。节点宕机、kubelet 重启、有人误删、宿主机内存回收任何一个环节出事这个 Pod 就永久消失没有任何组件会觉得“这里少了一个东西我得补上”。再往深一层看裸 Pod 有三个致命短板。第一是自愈能力缺失Pod 进程崩溃后kubelet 能按 restartPolicy 重启容器但 Pod 本身被删除、被驱逐时没有上层对象负责重建。第二是扩缩容全靠手动业务流量翻三倍你得一条条执行命令改副本数改完还得自己盯着调度结果。第三是发布过程不可控想换镜像版本只能先删旧的再建新的中间必然有一段完全不可用的时间窗口回滚更是无从谈起——旧版本的配置早就丢了。控制器的价值就在这儿它把“应该有几个、长什么样”这条期望状态持久化到 API Server 的 etcd 里然后不停地拿它跟集群里的实际状态做比对。只要两者有差距就采取动作去拉平。这中间哪怕控制器自己挂掉重启期望状态还在对账还能继续。这个设计的好处是运维人员不需要关心“怎么让 Pod 变成 3 个”只需要声明“我要 3 个”剩下的路径由控制器自己走。1.2 控制循环期望状态与实际状态的对账机制所有控制器共享同一套骨架也就是常说的 reconcile loop。用伪代码表达大概长这样for { desired : readSpec() // 从 etcd 读期望状态比如 replicas: 3 actual : readCluster() // 从集群读实际状态比如当前只有 2 个健康 Pod if desired ! actual { takeAction(desired, actual) // 创建或删除 Pod把差距补平 } waitForNextEvent() // 阻塞等待下一次事件或定时重试 }这段逻辑看着平淡但有几个点值得单独拎出来说。第一控制器是 level-based 而非 edge-based 的。它不关心“刚才发生了什么事件”只关心“现在该是什么样”。这意味着中间漏掉几条事件通知没关系下一轮对账会自动纠正。第二控制器通过 informer 机制在本地维护一份缓存绝大多数判断走缓存而不是直接打 API Server所以几千个 Pod 的集群里控制器依然能保持很低的延迟。第三每轮对账都必须收敛写代码的人如果在中途 return 得太早下一次对账间隔会被拉长故障恢复就会变慢。提示排查控制器的行为时kubectl describe里的 Events 只能看到结果看不到对账过程。真正想看控制器在干什么得去看 kube-controller-manager 的日志或者用kubectl get events --field-selector involvedObject.name对象名过滤出具体对象的流水。1.3 六个控制器各管一摊上篇先啃两个为了不把篇幅铺得太散先把这六个控制器的分工在脑子里立个坐标。ReplicaSet 只认“副本数”它不管你版本也不管顺序Deployment 建在 ReplicaSet 之上管的是“版本如何平滑切换”StatefulSet 管的是“有序、有身份、有持久存储”DaemonSet 管的是“每个节点都得有一份”Job 管的是“跑完就结束的一次性任务”CronJob 则是给 Job 加了个时间表。这六个里ReplicaSet 和 Deployment 是使用频率最高的组合也是理解其余四个的基础——StatefulSet 内部同样是通过类似 ReplicaSet 的方式管理副本只是多了身份和顺序的约束。所以上篇的路线很明确先把 ReplicaSet 这个最朴素的副本管理者拆透理解标签选择器、副本数对账、Pod 归属关系这几个底层概念再把 Deployment 这个“包装层”拆开看它怎么通过 ReplicaSet 做版本管理。下篇再讲另外四个有状态、有节点约束、有任务生命周期的控制器。这个顺序不是为了凑篇幅而是因为 Deployment 的很多行为比如滚动更新时旧 ReplicaSet 为什么还留着如果不先理解 ReplicaSet会一直是个黑盒。2. ReplicaSet副本数的守门人2.1 三个核心字段撑起整个 ReplicaSet一个 ReplicaSet 对象看起来字段不少但真正决定它行为的只有三块spec.replicas、spec.selector、spec.template。理解这三个字段的关系比背下整份 YAML 有用得多。spec.replicas是期望副本数语义非常直接但有一个容易忽略的点它统计的是被这个 ReplicaSet 拥有的 Pod 数量不是“带某标签的 Pod 数量”。这两个概念在绝大多数情况下一致但一旦有人手动创建了一个标签相同的 Pod且在 ReplicaSet 的 selector 范围内就会产生归属争议——ReplicaSet 会尝试“收养”这个孤儿 Pod从而让实际创建的副本数少于 replicas。这个行为经常把新手搞懵明明写的是 3结果只看到 2 个新 Pod。spec.selector是标签选择器用来圈定“哪些 Pod 归我管”。在apps/v1版本里selector 是必填且不可变的创建之后想改只能删了重建。这个限制是刻意的因为 selector 一旦可变就可能导致 ReplicaSet 对自己已有的 Pod “失忆”进而在集群里制造出大量无人认领的孤儿 Pod。spec.template是 Pod 模板描述“要创建什么样的 Pod”。这里有一个硬性校验模板里metadata.labels必须能被 selector 匹配到否则 API Server 会直接拒绝创建请求报错信息是selector does not match template labels。这个校验看着啰嗦其实是保护机制防止你写出一个永远找不到自己 Pod 的 ReplicaSet。2.2 手写一份能跑的 ReplicaSet 清单理论说再多不如直接上手。下面这份清单是我平时用来验证集群状态的最小可用版本字段精简但五脏俱全apiVersion: apps/v1 kind: ReplicaSet metadata: name: web-rs labels: app: web tier: frontend spec: replicas: 3 selector: matchLabels: app: web tier: frontend template: metadata: labels: app: web tier: frontend spec: containers: - name: web image: nginx:1.25 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi这份清单里有几处值得说明。selector.matchLabels用的是精确匹配两个标签同时满足才算命中如果想用更灵活的表达比如In、Exists可以换成matchExpressions。resources部分一定要写哪怕只是象征性地给一点——没有 requests 的 Pod 在调度时被视为“零占用”节点资源被撑爆的风险会明显上升。镜像标签写死到1.25而不是latest这个习惯后面讲 Deployment 时还会反复强调。应用之后用三条命令验证kubectl get rs看 DESIRED / CURRENT / READY 三列是否对齐kubectl get pods --show-labels看 Pod 的标签是否和 selector 一致kubectl describe rs web-rs看 Events 里有没有异常。如果 CURRENT 一直是 0八成是镜像拉不下来或者资源请求过高导致调度失败用kubectl describe pod继续往下查。2.3 标签选择器写错会怎样一次典型的现场复现selector 和模板标签的问题值得单独拿一小节出来讲因为它是最容易犯、也最难第一眼看出来的错误。我见过两种典型翻车方式。第一种是模板标签少写了一个。比如 selector 里写了app: web和tier: frontend模板里只写了app: web。这种情况 API Server 会直接拒绝报错很明确改完重新 apply 就行属于“好错误”。第二种更阴险selector 只写了部分标签比如 selector 只有app: web但模板里写了三个标签。这样能创建成功运行时也没问题但隐患是——任何带app: web标签的 Pod 都会被这个 ReplicaSet 认领。隔壁 Deployment 创建的 Pod 如果也带这个标签两个控制器就会开始互相“抢人”Pod 被反复创建和删除集群里出现莫名其妙的抖动。还有一种场景是手动kubectl run起的调试 Pod 忘了删标签刚好撞上了业务 ReplicaSet 的 selector。ReplicaSet 会认为这是自己人副本数直接少一个业务实际能力下降但监控上看起来一切正常。这类问题在监控大盘上是看不出来的只能靠定期审计“带业务标签但不带 ownerReferences 的 Pod”来发现。注意给业务打标签时建议至少保留一个全局唯一的业务标识如app.kubernetes.io/name再加上环境、组件等维度。标签撞车在多人协作的集群里发生概率比想象中高得多。2.4 单独用 ReplicaSet 的三个硬伤ReplicaSet 能保证副本数但它只管“数量”不管“质量”和“版本”。这就带来三个绕不过去的硬伤。第一镜像升级是暴力替换。你把模板里的镜像从1.25改成1.26ReplicaSet 会直接按新模板创建 Pod但旧 Pod 怎么处理它不会优雅地一批批换而是可能出现新旧混杂的状态。而且模板更新之后旧 Pod 和新 Pod 混在同一个 ReplicaSet 里你甚至没法从 RS 层面区分哪些是新版本。想按版本回滚旧模板早就被覆盖了。第二没有版本历史。ReplicaSet 不保存任何历史配置改完就是改完了。生产环境出了问题想退回上一个稳定版本只能靠人肉记住“上次的镜像 tag 是多少”或者从 Git 里翻 commit。这个流程在紧急故障时基本不可用。第三不支持暂停和分批。有时候新版本想先放一台机器观察半小时没问题再全量推。ReplicaSet 层面做不到这件事你只能手动改 replicas 或者临时打标签隔离操作起来非常别扭。正是这三个硬伤催生了 Deployment。Deployment 本身不直接管 Pod它在 ReplicaSet 之上加了一层“版本编排”的能力每次模板变更生成一个新的 ReplicaSet旧的不删只缩容到 0于是版本历史和回滚能力就有了滚动更新策略则在缩容扩容的节奏上做文章从而支持分批发布。3. Deployment把滚动更新做成标准动作3.1 Deployment、ReplicaSet、Pod 的三层委托关系先把这三层关系讲清楚后面所有行为都能推导出来。你创建一个 DeploymentDeployment 控制器会立刻创建一个 ReplicaSet这个 ReplicaSet 再去创建 Pod。Pod 的ownerReferences指向 ReplicaSetReplicaSet 的ownerReferences指向 Deployment。删除 Deployment 时级联删除会顺着这条链把 RS 和 Pod 一起清掉。关键点在“每次模板变更生成一个新 ReplicaSet”这条规则上。Deployment 控制器会给 Pod 模板算一个哈希值作为pod-template-hash标签加到 ReplicaSet 的名字后缀和 Pod 标签上。模板一变哈希就变新 RS 被创建旧 RS 保留但副本数逐步缩到 0。所以你kubectl get rs时经常能看到一堆名字带随机后缀的 ReplicaSet其中只有一个是 CURRENT 大于 0 的其余都是历史版本。这里有个细节很多人不知道Deployment 的 selector 里其实会自动带上pod-template-hash只不过你在 YAML 里看不到是控制器补的。这个设计是为了防止不同版本的 ReplicaSet 互相抢 Pod——毕竟它们的业务标签是一样的只有哈希能把它们区分开。理解这一点你就能明白为什么“手动给 Pod 打上和 Deployment 一样的标签”通常不会引发抢人大战因为哈希对不上。3.2 一份生产可用的 Deployment 清单逐字段拆解下面这份清单是我在多个环境里用下来的基础版本字段不算多但该有的都有apiVersion: apps/v1 kind: Deployment metadata: name: web-deploy labels: app: web annotations: kubernetes.io/change-cause: init: nginx 1.25 baseline spec: replicas: 4 revisionHistoryLimit: 5 progressDeadlineSeconds: 300 minReadySeconds: 5 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginx:1.25 ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 3 periodSeconds: 5 failureThreshold: 2 livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 10 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi逐个说几个容易踩的点。revisionHistoryLimit控制保留多少个旧 ReplicaSet默认是 10。设太小会导致回滚深度不够设太大则会在集群里堆积大量 replicas 为 0 的 RS 对象虽然不占计算资源但会让kubectl get rs的输出非常长。我个人习惯设 5够回滚几个版本输出也不至于刷屏。千万不要设成 0那样旧 RS 会被立即清理回滚能力直接归零。progressDeadlineSeconds是滚动更新的“超时闹钟”默认 600 秒。超过这个时间还没推进成功Deployment 的 Conditions 里会出现ProgressDeadlineExceededkubectl rollout status也会返回非零退出码。这个字段对 CI 流水线特别有用能让发布失败在流水线层面暴露出来而不是靠人盯着看。minReadySeconds的含义是“Pod 进入 Ready 之后还要稳定多少秒才算可用”。默认是 0也就是探针一通过立刻算数。设成 5 到 10 秒能挡掉一类问题应用启动极快但实际依赖还没连上的情况探针刚过就接流量结果第一批请求全 500。这个值设太大会拖慢整体发布速度10 秒以内是比较平衡的选择。3.3 maxSurge 与 maxUnavailable 到底怎么算这两个参数是滚动更新的核心但百分比形式的取整规则经常把人绕晕。先把定义说清楚maxSurge是“在期望副本数之上最多允许多出多少个 Pod”maxUnavailable是“在期望副本数之下最多允许多少个 Pod 不可用”。两个值不能同时为 0否则滚动更新会陷入死锁——既不能多起也不能少停永远推进不了。百分比取整规则是不一样的maxSurge 向上取整maxUnavailable 向下取整。这个差异是刻意的目的是保证更新过程中始终有足够的可用容量。拿具体数字算一遍就清楚了。假设replicas: 3两个参数都写25%。maxSurge 3 × 25% 0.75向上取整得 1所以最多可以有 4 个 PodmaxUnavailable 3 × 25% 0.75向下取整得 0所以一个都不能少。这意味着更新节奏是“先起一个新的等它就绪再停一个旧的”全程可用副本数不低于 3。这个配置安全性很高但要求集群有额外 1 个 Pod 的资源余量。再假设replicas: 4同样25%。maxSurge 1maxUnavailable 1可用副本数在 3 到 5 之间浮动。这个配置的更新速度更快因为可以“先停一个再起一个”但代价是更新期间业务承载能力会短暂降到 75%。如果业务对容量敏感这个配置就要慎重。还有一类场景是资源紧张节点上腾不出额外 Pod 的位置。这时候可以设maxSurge: 0、maxUnavailable: 1语义是“不许多起必须先停后起”。这种配置在资源打满的集群里能跑通但更新期间一定会有容量缺口且如果新版本起不来可用副本会一直少一个需要人工介入。配置组合replicas3 时可用范围更新速度适用场景maxSurge25%, maxUnavailable25%3 ~ 4较慢容量敏感、资源充足maxSurge1, maxUnavailable12 ~ 4中等一般无状态业务默认值maxSurge0, maxUnavailable12 ~ 3较慢节点资源紧张maxSurge1, maxUnavailable03 ~ 4较快要求零容量缺口的核心业务maxSurge0, maxUnavailable0非法不可用API Server 直接拒绝表格里最后一行值得记住两个都设 0 时apply 阶段就会报错错误信息大意是may not be 0 when maxSurge is 0。这个校验在写 Helm chart 做参数化时特别容易撞上如果模板里两个值都来自变量记得加个兜底。提示滚动更新的实际推进顺序不是“一次性起满 maxSurge 再停”而是控制器按批次逐步调整新旧 ReplicaSet 的副本数。观察kubectl get rs -w能看到新旧 RS 的数字在来回变化那个过程比看文档直观得多。3.4 探针与 minReadySeconds 对更新节奏的实质影响很多人以为滚动更新的节奏只由 maxSurge 和 maxUnavailable 决定其实探针配置的影响更大。控制器判断“这个新 Pod 能不能算作可用”依据就是 readinessProbe 的结果。没有配 readinessProbe 的 Pod只要容器 Running 就被视为 Ready这个判定发生得非常早应用可能连端口都没监听上。结果就是新 Pod 一 Running 就被算作可用旧 Pod 立刻被缩掉流量打过来一片 502。我见过最典型的一次故障是这样某个服务启动需要加载一份较大的本地缓存耗时大约 20 秒。Deployment 里只配了 livenessProbe 没配 readinessProbe滚动更新时新 Pod 秒变 Ready旧 Pod 秒被删整整 20 秒内所有请求都打到还没准备好的新 Pod 上。日志里全是连接被拒但 Pod 状态是 Running监控上看起来“服务正常”。后来加上 readinessProbe把initialDelaySeconds设成 10、periodSeconds设成 5问题立刻消失。livenessProbe 的配置则要反过来小心initialDelaySeconds 不能太短。如果应用启动需要 30 秒而 livenessProbe 在 10 秒时就开始探测连续失败达到failureThreshold后 kubelet 会直接杀掉容器重启。重启之后又是同样的循环最终表现为 CrashLoopBackOff但根因其实是探针配得太急。这种情况在启动慢的 JVM 应用上尤其常见稳妥的做法是把initialDelaySeconds设得比实测启动时间宽裕一些或者直接用startupProbe把启动阶段单独隔离出来。minReadySeconds是在 readinessProbe 之上再加一道缓冲。探针通过只说明“这一刻是好的”minReadySeconds 要求“连续这么多秒都是好的”。它挡的是那种启动后短暂可用又立刻崩掉的情况比如依赖的连接池初始化失败后重连。代价是发布变慢每个 Pod 多等这么几秒10 个 Pod 就是几十秒的差距。生产环境我一般设 5 到 10 秒测试环境设 0 加快迭代。4. 完整实操从首次发布到版本回滚4.1 首次发布与状态确认把前面那份清单存成web-deploy.yaml执行kubectl apply -f web-deploy.yaml。返回deployment.apps/web-deploy created之后立刻跟一条kubectl rollout status deployment/web-deploy --timeout180s。这条命令会阻塞直到发布完成或者超时用脚本跑的时候特别顺手超时会返回非零退出码可以直接在 CI 里做门禁。发布完成后验证三层对象是否都符合预期kubectl get deploy web-deploy -o wide kubectl get rs -l appweb kubectl get pods -l appweb -o wide --show-labelskubectl get deploy的输出里 READY 那一列格式是“就绪数/期望数”比如4/4UP-TO-DATE 表示已经更新到最新模板的副本数AVAILABLE 表示持续可用的副本数。这三个数字在稳定状态下应该完全一致任何一个对不上都说明有问题。kubectl get rs应该只看到一个 CURRENT 为 4 的 RS其余历史 RS 的 DESIRED 都是 0。4.2 观察滚动更新的每一步现在把镜像从nginx:1.25改成nginx:1.26用命令行的方式最快kubectl set image deployment/web-deploy webnginx:1.26 kubectl annotate deployment/web-deploy kubernetes.io/change-causebump nginx to 1.26 --overwrite第二条命令是给这次变更打一个“变更原因”的注释后面kubectl rollout history能看到回溯时非常有用。很多人只记得set image忘了打注释结果版本历史里一排none回滚时根本分不清哪个是哪个版本。改完之后立刻开一个窗口跑kubectl get pods -l appweb -w另一个窗口跑kubectl get rs -l appweb -w能清楚看到新 RS 从 0 逐步涨到 4、旧 RS 从 4 逐步降到 0 的过程。因为配置的是 maxSurge1、maxUnavailable1Pod 总数会在 3 到 5 之间波动不会出现瞬间掉到 0 的情况。如果观察到总数长时间停在某个数字不动说明新 Pod 起不来去kubectl describe pod看具体原因。整个过程可以用kubectl rollout status卡住等它返回之后再继续下一步。这个习惯在自动化流程里很重要否则后面的验证步骤可能在发布还没完成时就开始跑得出的结论全是错的。4.3 回滚、暂停与分批发布回滚有两级。快速回滚用kubectl rollout undo deployment/web-deploy默认回到上一个 revision。如果要多退几步先kubectl rollout history deployment/web-deploy看历史列表再用--to-revision编号指定。历史列表里 CHANGE-CAUSE 那一列就是前面annotate打进去的内容这也是为什么建议每次变更都补一条。kubectl rollout history deployment/web-deploy kubectl rollout undo deployment/web-deploy --to-revision2 kubectl rollout status deployment/web-deploy回滚的本质是把旧 ReplicaSet 的模板“提”成当前版本重新走一遍滚动更新。所以回滚本身也是渐进的不是瞬间切换。回滚之后原来的 revision 编号会被重新分配历史列表看起来可能和预期不太一样这属于正常行为。暂停功能适合做手动分批发布。执行kubectl rollout pause deployment/web-deploy之后再改镜像或改配置控制器不会立刻推进等你观察一阵子确认没问题再kubectl rollout resume deployment/web-deploy让它继续。这个能力在灰度场景下很好用先改一台的副本数或者先在一个小集群里验证再放开全量。注意暂停期间如果新的 RS 已经创建并扩了副本这些 Pod 是真实在跑的只是控制器不再继续往下推进。还有一个不太起眼但很实用的命令kubectl rollout restart deployment/web-deploy。它不改镜像也不改配置只是在 Pod 模板上加一个kubectl.kubernetes.io/restartedAt注释从而触发一次滚动更新。适用于配置通过 ConfigMap 挂载、改完 ConfigMap 需要重启 Pod 才生效的场景。比起手动删 Pod这种方式是滚动进行的不会造成服务中断。4.4 扩缩容与历史版本清理扩缩容直接用kubectl scale deployment/web-deploy --replicas6。这条命令改的是 Deployment 的 replicas控制器会同步调整当前 RS 的副本数。如果集群装了 HPA水平自动扩缩要小心手动 scale 和 HPA 打架——HPA 会在下一个评估周期把副本数改回它算出来的值。这种场景下应该改 HPA 的 min/max而不是直接 scale Deployment。历史版本的清理靠revisionHistoryLimit。假设设的是 5当你滚动更新到第 7 个版本时最老的两个 RS 会被自动删掉。这个清理是异步的不会影响正在进行的发布。如果想主动清理可以手动删除特定 RS但必须确认它的 DESIRED 是 0删错会把正在服务的 Pod 一起带走。还有一个观察技巧kubectl get rs -l appweb -o wide的输出里每个 RS 的 IMAGE 列显示的是它对应的镜像版本。历史 RS 虽然副本数是 0但模板信息完整保留这就是回滚能工作的基础。如果发现某个历史 RS 的 IMAGE 列显示nnone说明当时用的是摘要引用或者模板被改过回滚时要特别小心。5. 故障现场Deployment 更新卡住的排查实录5.1 一张速查表覆盖八成问题发布出问题时最忌讳的是漫无目的地翻日志。按“Deployment 状态 → ReplicaSet 状态 → Pod 状态”这个顺序往下捋绝大多数问题能在三分钟内定位。下面这张表是我平时贴在显示器旁边的版本现象最可能的原因首查命令处理方向Pod 一直 Pending资源请求过高、节点选择器、污点kubectl describe pod看 Events降低 requests 或扩容节点ImagePullBackOff镜像名错、私有仓库凭证缺失kubectl describe pod看拉取错误修正镜像地址或补 imagePullSecretCrashLoopBackOff应用启动失败、探针配太急kubectl logs --previous调大 initialDelaySeconds 或修应用rollout 长时间无进展新 Pod 起不来、进度超时kubectl rollout statusdescribe deploy看 Conditions 定位卡点新旧 RS 副本数互相拉扯selector 标签冲突kubectl get rs -o yaml对比 selector修正标签避免跨控制器撞车Pod 数量对但流量 5xx没配 readinessProbekubectl get pods看 READY补探针加 minReadySeconds回滚失败提示找不到 revisionrevisionHistoryLimit 太小kubectl rollout history调大保留数量表格里最后一行是很多人踩过的坑为了“让kubectl get rs输出干净”把 revisionHistoryLimit 设成 2 甚至 1结果出了故障想回退三个版本发现历史已经被清掉了。这个字段不能当清理工具用它的下限取决于你能接受的最坏回滚深度。5.2 三个真实场景的排查思路场景一更新卡在Waiting for deployment rollout to finish: 1 old replicas are pending termination这句话的意思是“还有一个旧 Pod 没被杀掉”。控制器的逻辑是只有新 Pod 数量达到期望、且可用数量满足约束才会继续缩旧 RS。所以卡在这里通常不是旧 Pod 的问题而是新 Pod 的可用数量还不够。往下查kubectl get rs看新 RS 的 READY 是不是小于 DESIRED再查新 Pod 的状态看是不是卡在 ContainerCreating 或者探针失败。我遇到过一例是镜像层太大节点上磁盘空间不足导致镜像拉取一直重试节点层面看不出来describe pod的 Events 里才有磁盘相关的报错。场景二Deployment 的 Conditions 显示ProgressDeadlineExceeded这个状态的官方语义是“超过 progressDeadlineSeconds 没有取得进展”。注意是“没有进展”不是“没有完成”。如果控制器一直在推进副本数在变哪怕总时长超过 deadline 也不会报这个。一旦报出来说明进程真的停滞了。此时kubectl describe deployment的 Conditions 段会附带原因通常是新 RS 无法达到可用状态。处理方法要么是修问题让它继续要么是kubectl rollout undo退回去。注意这个状态不会自动回滚K8s 不做自动回滚得人工决策。场景三apply 成功但没有任何变化改完 YAML 执行 apply返回unchangedPod 也没重启。原因通常是改的字段不触发模板哈希变化。比如只改了metadata.labels或者metadata.annotationsDeployment 对象本身的不是 Pod 模板里的这些不会进 Pod 模板自然不触发更新。还有一种情况是镜像 tag 用了latest且没变“看起来是新的”但字符串完全相同哈希也不变。想知道到底哪些字段会触发更新看新 RS 有没有被创建就行——没有新 RS就一定没有发生滚动更新。5.3 上线前的预检清单与个人习惯最后分享几个把问题挡在发布之前的习惯。第一条apply 之前先 diff。装好 diff 插件后kubectl diff -f web-deploy.yaml能列出服务端当前配置和本地文件的差异比kubectl apply --dry-runserver更直观。这个命令在改复杂清单时能救命我曾经靠它发现一份 YAML 里 replicas 被误改成了 1。第二条变更必须带 change-cause。这一条看起来是流程要求实际是给自己留后路。半年后回滚某个服务rollout history里如果能看清“这次是把超时从 30s 调到 60s”“这次是换基础镜像”决策成本会低很多。团队里可以把这个写进发布脚本自动从 commit message 里取。第三条readinessProbe 是必填项不是可选项。哪怕只是简单返回 200 的 HTTP 探针也比没有强。没有探针的 Deployment滚动更新的可靠性完全靠运气。第四条资源 requests 一定要按实测填。见过太多清单里 requests 随手写 100m/128Mi实际应用启动峰值要 1 核结果新 Pod 调度不上去滚动更新卡在 Pending。填资源前先在测试环境压一遍用kubectl top pod观察真实占用再留 30% 余量。我个人在实际操作中的体会是Deployment 这套东西真正难的不是写 YAML而是理解“控制器在什么时刻做了什么决定”。同样一个卡住的发布知道它在等什么三分钟能解决不知道可能翻一晚上日志还在原地打转。所以每次发布失败别急着undo了事先花两分钟看一眼kubectl describe deployment和kubectl get rs把当时的状态记下来。这些记录攒多了下次再遇到类似现象基本能做到看一眼就知道往哪儿查。
RELATED

相关推荐

STM32F407+LWIP+DP83848以太网移植与排障指南

STM32F407+LWIP+DP83848以太网移植与排障指南

简介:面向嵌入式开发者的STM32F407LWIPDP83848移植例程,聚焦无操作系统环境下TCP/IP Server的搭建与数据收发,适合已具备STM32基础、希望打通以太网通信链路的工程师与学生参考。压缩包共3458个文件,以C源码与头文件为主体&#x…

📅 2026/10/26 12:47:01
Transformer核心逻辑与魔改实践:注意力、残差与位置编码的细节

Transformer核心逻辑与魔改实践:注意力、残差与位置编码的细节

一个经常魔改神经网络架构的人,自然免不了反复拆解和重组各种模型组件。Transformer这个架构,可以说是绕不开的“工作对象”。说实话,拆过的网络多了,你会发现很多结构都是纸老虎,换几个数据集就现原形。但Transformer…

📅 2026/10/26 12:47:01
信息学奥赛排名题解析:Dense Rank与Ordinal Rank双解法

信息学奥赛排名题解析:Dense Rank与Ordinal Rank双解法

1. 题目本质拆解:这根本不是一道“排名题”,而是一道隐性排序规则识别题 看到标题“东方博宜 1542 - 小X算排名 共2种答案”,很多刚接触信息学奥赛模拟题的同学第一反应是:“哦,排序题,套个sort函数完事”。…

📅 2026/10/26 12:42:00
MORE NEWS

更多资讯

📰

大模型与AIGC技术发展现状及厂商选型指南

1. 大模型与AIGC技术发展现状 2023年被称为"生成式AI元年",大模型和AIGC技术已经从实验室走向产业化应用。根据最新行业报告显示,全球AI市场规模预计在2025年将达到1900亿美元,年复合增长率超过30%。在这个快速发展的赛道上&#x…

📰

MEA与双层BP神经网络混合优化方案解析

1. 项目概述:当群智能遇上神经网络在机器学习领域,BP神经网络作为经典算法已经服役三十余年,但其固有的局部最优和收敛速度问题始终困扰着从业者。去年我在一个工业预测项目中,就曾因为传统BP网络反复陷入局部最优而连续加班两周。…

📰

西门子1200PLC随动程序实战:从架构设计到避坑验收

简介:这份资源面向已具备一定S7-1200基础的自动化工程师与运动控制学习者,围绕编码器反馈下的随动控制展开,解决电机如何实时跟随参考信号、实现位置与速度精确校正的问题。内容涉及高速计数器采集编码器信号、脉冲输出驱动伺服或步进电机、P…

📰

AI工程师必备:从理论到实践的算法实现四阶训练法

1. 项目概述:当AI遇上动手实践三年前我在一次技术沙龙上听到一个观点:"AI工程师的代码能力正在退化"。当时觉得这个说法有些危言耸听,直到去年review团队项目时发现,许多同事的解决方案清一色都是"调API拼prompt&q…

📰

Token工厂生死局:大模型时代数据标注行业如何突围

开头 前一阵子我带着"Token工厂到底是门什么生意"这个问题,陆陆续续和十几个数据服务团队的创始人、项目经理聊了一圈。说实话,聊完之后我心里很不舒服——这个行业在过去两年被大模型带上了风口,无数人冲进去想分一杯羹&#xff0…

📰

AI编码时代程序员价值重构:从写代码到定义问题、编排Agent工作流与质量兜底

1. AI 编码时代,程序员的价值锚点到底在哪1.1 先搞清楚一个事实:AI 写代码这件事,到底走到了哪一步这两年我身边不少同行都在焦虑同一件事:AI 都能写代码了,我还要不要继续干这行?尤其是各种编码助手、Agen…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬