拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

K8s Deployment实战:滚动更新与回滚机制详解

K8s Deployment实战:滚动更新与回滚机制详解

K8s系列走到第四篇,终于要聊正事了。前三篇我们把集群规划、核心组件协作、Pod是怎么被创建出来的串了一遍,这些都属于地基。地基砌好之后,你总得往上盖房子,在 Kubernetes 里“盖房子”这件事对应的就是工作负载,而工作负载里日常使用频率最高的,我敢说是 Deployment。这篇我把 Deployment 单独拎出来,从设计原理到滚动更新再到回滚,完整走一遍实战。目标读者是已经搭过集群、跑过简单 Pod,但对 Deployment 的滚动机制和回滚操作还停留在“听过”阶段的同学。看完你至少能做到两件事:发布新版本时心里有数,线上出了问题时能冷静地把它退回去。

1. 工作负载里为什么偏偏绕不开 Deployment

1.1 先认人:工作负载家族里谁管哪种活儿

Kubernetes 里的工作负载不是只有 Deployment 一类,它是按“应用形态”分家的。最底层的是 Pod,但生产环境几乎不会直接创建裸 Pod,因为没人想手动维护几十个副本的生命周期。于是 K8s 在 Pod 之上包了一层又一层控制器。

工作负载管什么典型场景
Deployment管理一组无状态 Pod,支持滚动更新与回滚Web 服务、API、微服务
StatefulSet管理有状态 Pod,保证稳定网络标识和持久化数据库、消息队列、ZooKeeper 这类需要身份和存储的组件
DaemonSet保证每个节点上恰好跑一个 Pod日志采集、监控 Agent、网络插件
Job / CronJob执行一次性或定时任务,任务结束 Pod 就退出批处理、数据迁移、定时清理

这么一分你会发现,Deployment 管的是“无状态、可以随时替换”的应用。所谓无状态不是没内存没磁盘,而是说任何一个副本被干掉、被重建,都不影响整体服务。Web 服务、API 网关、业务后端,基本都是这个形态。

1.2 三层继承关系:你写的 Deployment 最终控制的其实是 Pod

很多人第一次听说 ReplicaSet 会有点懵:我明明只写了 Deployment,K8s 为啥又给我搞出来一个 ReplicaSet?

这里要先捋清 Kubernetes 对象之间的“血缘”。在 Deployment 出现之前,老版本用的是 ReplicationController,简称 RC,功能很直接:保持 Pod 副本数稳定。但 RC 对“更新应用”这件事支持得不好,于是后来社区用 ReplicaSet 替代了 RC,再后来又用 Deployment 包装了 ReplicaSet。

完整的关系是:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。Deployment 每次触发一次更新,就会创建一个新的 ReplicaSet,让新 RS 里的 Pod 按规则慢慢长大,让旧 RS 里的 Pod 慢慢缩掉。为什么要保留多个 RS?就是为了回滚。旧版本不是被删掉了,而是缩容到 0、留着历史记录,等你想退回去的时候,旧 RS 随时待命。

所以实际下发的链路是:

  • 你 write:Deployment
  • 控制器创建:ReplicaSet
  • ReplicaSet 创建:Pod

一句话概括:Deployment 是“老板”,ReplicaSet 是“工头”,Pod 是“干活的工人”。

1.3 选型时的一道判断题:不是所有场景都适合上 Deployment

Deployment 虽然好用,但别形成条件反射,看什么都先写个 Deployment。这里的判断标准就一条:你的应用能不能接受 Pod 被随机调度、随机替换、随机重启?

如果应用需要稳定的网络标识和稳定的存储,比如 Redis、MySQL、Kafka,你用 Deployment 就相当于把数据库副本当“临时工”管理,Pod 重建之后名字变了、IP 变了、数据没了,这时候应该用 StatefulSet,或者直接用对应的 Operator。如果要在每个节点上跑一个采集组件,比如 node-exporter、fluentd,那用 DaemonSet 更合适。如果是跑一次性数据清洗任务,那就交给 Job。

我见过不少生产事故,源头就是把 Redis 集群用 Deployment 部署,Pod 一重启节点挂了、数据丢了。工作负载先选型,再写 YAML,这个顺序不能省。

2. 声明式设计是怎么让“滚动更新”这件事成立的

2.1 期望状态与控制器循环:你只负责提需求

Deployment 最核心的设计思想叫“声明式”:你告诉集群“我要 3 个 nginx 副本,镜像版本 1.27”,剩下的事 K8s 自己想办法。这个“想办法”的过程,靠的是控制器循环(reconcile loop)。

ReplicaSet 控制器会不停地做一件事:比对“期望副本数”和“当前实际 Pod 数”,多了就删,少了就建。这个循环没有终点,它一直挂着,时刻等着现实偏离期望的状态。拿空调打比方最直观:你设 26 度是期望状态,温度传感器是当前状态,空调压缩机就是控制器,温度高了就制冷、低了就停机,直到误差归零。

Deployment 的控制器循环比 ReplicaSet 多了一层:它还要比对“期望的 Pod 模板”和“当前 RS 里的 Pod 模板”。一旦发现模板变了,就认为这是一次新版本发布,于是创建新的 ReplicaSet,再让新 RS 和旧 RS 按滚动策略调整副本数量。

理解了这一层,你就能明白:K8s 里几乎所有“自动修复”的能力,都靠这种循环。它不是定时任务,是常驻的反馈回路。

2.2 拆 YAML:那些真正决定行为的关键字段

一个最小可用的 Deployment YAML 长这样:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27 ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 10

我不打算把每个字段都念一遍,只说你必须吃透的五个。

第一是replicas,期望副本数。它就是个数字,但你要清楚它会被 HPA(水平自动扩缩容)接管,如果配了 HPA 就别手动改副本数,改了会被 HPA 调回来。

第二是selector,决定了这个 Deployment 管哪些 Pod。Deployment 通过matchLabels去挑选 Pod。这里有个关键限制:selector 一旦创建就不可修改,因为控制器靠它识别归属,你中途改了 selector,相当于跟集群说“我不认识原来那些 Pod 了”,旧的 ReplicaSet 和 Pod 就变成了无主对象,全部失控。

第三是template,也就是 Pod 模板。Deployment 判断要不要发布新版本,就是看这个模板的哈希值有没有变化。镜像改个 tag,模板哈希就变,触发滚动更新。

第四是strategy,发布策略。可选值只有两个:Recreate和RollingUpdate。Recreate 简单粗暴:先把旧 Pod 全删了,再建新的,数据不丢但服务必断,适合不能同时存在两个版本的场景。RollingUpdate 是默认值,也是这篇文章的主角。

第五是revisionHistoryLimit,保留多少份历史 ReplicaSet。默认值是 10,这个值直接决定了你能回滚几步。

2.3 maxSurge 和 maxUnavailable:滚动更新的两个旋钮

滚动更新的行为由两个参数决定:maxSurge和maxUnavailable,默认都是 25%。这俩参数很多人配置过,但真正理解算法的不多。

  • maxSurge:滚动期间允许超出期望副本数的最大 Pod 数。比如期望 3 个副本,maxSurge=25%,那么最多可以同时存在 4 个 Pod,多出来的 1 个会用来创建新版本 Pod。
  • maxUnavailable:滚动期间允许最多有多少个 Pod 处于不可用状态。maxUnavailable=25%,3 个副本向下取整是 0,也就是说最理想情况下,新 Pod 没就绪前,旧 Pod 一个都不能删,保证服务不降级。

算一下 3 副本、两个参数都 25% 的场景:

  • maxSurge = 3 * 25% = 0.75,向上取整为1
  • maxUnavailable = 3 * 25% = 0.75,向下取整为0

所以滚动期间,K8s 会先把新 RS 的 Pod 从 0 加到 1,新 Pod 通过就绪探针之后,再缩旧 RS 的 Pod,整个过程始终保证可用 Pod 不少于 3 个。

如果你希望“既快又不抖动”,可以把maxSurge设大、maxUnavailable设小;如果你愿意接受短暂容量下降,可以反过来。这两个值必须有一个不为 0,否则滚动永远无法推进。

2.4 一次滚动到底发生了什么(时间线)

我按时间线把一次理想的滚动更新拆成五个阶段,方便你对照观察:

  1. 你执行kubectl apply修改镜像版本,Deployment 控制器发现 Pod 模板哈希变了。
  2. 控制器创建新的 ReplicaSet,比如nginx-deploy-7d9f8cbf5f,初始副本数 0。
  3. 由于maxSurge=1,新 RS 开始创建 1 个新 Pod;新 Pod 进入 ContainerCreating 状态,然后启动,等待就绪探针通过。
  4. 新 Pod Ready 后,控制器才把旧 RS 的副本数从 3 减到 2;于是又有名额创建第二个新 Pod,如此往复。
  5. 最后新 RS 副本数到 3,旧 RS 副本数到 0,Deployment 的 Available 状态为 True,滚动完成。

整个过程不是在“替换”Pod,而是在“增”和“减”之间找平衡。新 Pod 不 Ready,旧 Pod 就不会被大规模缩容——这就是滚动更新不会导致全量中断的根本原因。

3. 完整实战:从 apply 一个 Deployment 到观察滚动全程

3.1 先把清单写明白(带就绪探针的那种)

我建议你一开始就养成写 YAML 的习惯,别总是kubectl run一把梭。文件化有两个好处:第一是可审查,发布前 diff 一下就知道改了什么;第二是可复现,换环境不需要凭记忆敲命令。

这里给一份可以直接用的清单,保存为nginx-deploy.yaml:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27 imagePullPolicy: IfNotPresent ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 10 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi

注意两个小细节。

一是readinessProbe。我在第 5 部分会重点讲它,这里先记住:没有就绪探针的滚动更新是残缺的,K8s 无法判断新 Pod 是不是真的“能用”,只能默认“进程活着就算 Ready”。

二是resources。你把 requests 写清楚,调度器才能去算哪些节点放得下这批 Pod。不写 requests 导致的问题通常不会立刻爆发,等集群资源紧张时才发现 Pod 全挤在一起,那时就难收拾了。

3.2 创建之后先做状态确认

执行:

kubectl apply -f nginx-deploy.yaml

然后不要急着刷get pods,我一般先看两层状态。

kubectl get deploy nginx-deploy -o wide kubectl get rs -l app=nginx -o wide

看 Deployment 的状态列,重点关注AVAILABLE是不是等于READY。如果 READY 是 3 而 AVAILABLE 也是 3,说明整个副本组都已被负载均衡接受。再看 RS,第一次创建时只会有一个 RS,名字后缀是一串随机字符,下面是它的 3 个 Pod。如果你想确认当前是哪个版本,可以看:

kubectl get deploy nginx-deploy -o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}{"\n"}'

版本号是 1,这就对了。Revision 从 1 开始计数,后面每次模板变更都会递增。

3.3 触发更新的三种方式,我推荐哪一种

触发滚动更新有三种常见方式。

第一种是kubectl set image:

kubectl set image deployment/nginx-deploy nginx=nginx:1.28

优点是一条命令搞定,适合临时应急;缺点是没有留下可 review 的变更记录,多人操作时你不知道谁改的。

第二种是kubectl edit:

kubectl edit deployment/nginx-deploy

会打开默认编辑器,直接改内存里的对象。方便,但同样不适合生产,任何人都能改,且改完难以追溯。

第三种是改 YAML 文件后重新kubectl apply:

vim nginx-deploy.yaml kubectl apply -f nginx-deploy.yaml

我强烈推荐第三种。原因是 YAML 文件是你的声明式资产,它应该进 Git,配合 CI 走发布流程。发布之前看一眼 diff,发布之后留一条 commit,线上到底什么状态全部有据可查。个人玩测试集群怎么折腾都行,但在生产环境,你希望每一次变更都像代码一样被记录。

3.4 滚动过程中的 Pod 变化,逐条看

改完镜像重新 apply 之后,新开一个终端观察滚动。

watch kubectl get pods -l app=nginx -o wide

你会看到类似这样的过程:

先出现第 4 个 Pod,名字后缀是新的 RS 前缀,状态从 Pending 变成 Running,然后 READY 从 0/1 变成 1/1。这期间旧的 3 个 Pod 没有任何变化。新 Pod Ready 之后,旧 Pod 才一个一个变成 Terminating。接着第 5 个新 Pod 被创建,旧 Pod 再减一个,直到新旧交替完成。

这个过程用kubectl get rs看更直观:

kubectl get rs -l app=nginx

滚动前只有一个 RS,显示DESIRED=3 CURRENT=3 READY=3。滚动中你会同时看到两个 RS:旧的 DESCIRED 在往下减,新的 DESCIRED 在往上加。滚动结束后,旧的 RS 变成DESIRED=0 CURRENT=0 READY=0,但它还留在列表里,这就是回滚的历史资产。

如果你想等滚动彻底结束再走,用这个命令:

kubectl rollout status deployment/nginx-deploy --timeout=120s

滚动完成的输出是deployment "nginx-deploy" successfully rolled out。如果滚动卡住了,这个命令会一直等,直到超时返回非零状态码。在 CI 里发布后接这条命令,能让流水线第一时间发现发布失败。

3.5 容易误解的一件事:滚动更新和流量切换不是一回事

我在这里要专门踩一个很多人都会误解的坑:Deployment 滚动更新不等于你做的灰度发布,更不等于流量立刻切到新版本。

Kubernetes 的滚动更新只管“创建新 Pod、删除旧 Pod”。那流量为什么没断?因为你的 Service 的 Endpoints 列表里只放 Ready 的 Pod IP,新 Pod Ready 之后它的 IP 会自动加入 Endpoints,旧 Pod 被删之前它的 IP 会自动摘除。这个摘除和加入是异步的,中间会有极短暂的重叠,但对大多数应用来说无感。

问题是,这个机制并不懂“业务流量按比例分配”。它只是让新旧 Pod 短暂共存,共存的时长和比例完全由maxSurge/maxUnavailable决定,没有“先把 10% 流量导到新版本”这种逻辑。想要按比例灰度,得用 Istio、Argo Rollouts 这类专门工具,或者自己在 Service 和 Deployment 之间做多层控制。

所以当你听到“Deployment 支持金丝雀发布”这句话时,心里要留个底:它只是支持“暂停和分批”,但它不做流量权重。

4. 事故现场的救命技能:回滚与发布版本管理

4.1 版本是怎么被你留下的

回滚的前提是有历史版本可以回。Deployment 的历史版本其实就是那些“缩容到 0 的旧 ReplicaSet”。每次 Pod 模板发生变化,控制器就创建一个新 RS,并把旧的 RS 保留下来,数量受revisionHistoryLimit控制。

查历史用这个命令:

kubectl rollout history deployment/nginx-deploy

输出会类似:

deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 1 <none> 2 <none>

如果你的 YAML 或命令带了kubernetes.io/change-cause注解,CHANGE-CAUSE 那一列就会显示你写的备注,方便一眼看出每个版本改了什么。给当前版本补备注可以这样做:

kubectl annotate deployment/nginx-deploy kubernetes.io/change-cause="bump nginx to 1.28"

注意这个注解要在发布发生时已经存在,才会被记录到历史里;事后补的是给当前修订版本补一笔备注,效果也够用。新版 kubectl 里--record已经被弃用了,我劝你别再依赖它,老老实实用注解。

每个新版本都会生成新的 RS,注意回滚操作本身也会生成一个新版本号,而不是回到旧的 revision 编号。所以rollout history里的 REVISION 会一直往上递增,这一点和 Git 有点像:revert 也是一次新的 commit。

4.2 事故现场:一次完整的回滚命令演练

我模拟一个最常见的故障:你把镜像 tag 改成了一个不存在的版本,比如nginx:1.28.8,Pod 全部拉不到镜像,处于ImagePullBackOff。

此时你看到:

kubectl get pods

一堆ImagePullBackOff,但服务其实还没断,因为旧 RS 的 Pod 还没被全部缩掉。可如果你等滚动持续推进,旧 Pod 会被慢慢删光,到时才是真的事故。

回滚第一条命令,回到上一个版本:

kubectl rollout undo deployment/nginx-deploy

执行后,Deployment 控制器会把新 RS 的 Pod 往下缩,把旧 RS 的 Pod 往上抬,整个过程同样是滚动式的。跑完后确认:

kubectl rollout status deployment/nginx-deploy

看到successfully rolled out就说明旧版本已经重新接管。这里有个细节:回滚也不暂停。它也是滚动更新,所以如果你的应用新旧版本完全不兼容,回滚也可能造成短暂的新旧共存。真正的“立即切换”只能用 Recreate 策略,或者手动把新 RS 直接缩到 0、旧 RS 拉满,但这属于紧急止血手段,不太优雅。

如果想回退到指定版本,比如第 1 版:

kubectl rollout undo deployment/nginx-deploy --to-revision=1

回滚前我建议先确认一下目标版本和当前版本的差异,用:

kubectl rollout history deployment/nginx-deploy --revision=1

会列出那个版本对应的完整 Pod 模板信息,比对着看一遍再决定回不回,避免回错了又来回折腾。

4.3 暂停发布:想先放一批试水

有时候你不想一次性滚完,想分阶段确认。Deployment 的暂停能力可以帮到你。

发布前先暂停:

kubectl rollout pause deployment/nginx-deploy

然后修改镜像:

kubectl set image deployment/nginx-deploy nginx=nginx:1.28

此时你会发现 Pod 没有任何变化——因为 Deployment 处于暂停状态,控制器不会推进 rollout。你可以先看看这次要发布的模板是不是写对了,甚至再用一个临时 Deployment 验证一下新镜像的启动。确认没问题之后恢复:

kubectl rollout resume deployment/nginx-deploy

从 resume 这一刻起,滚动更新才真正开始。这个模式的本质是把“发布动作”和“发布推进”拆开,给操作者留一个手动的确认阀门。

不过我还是要诚实地说:这个暂停/恢复只能控制“是否创建新 Pod”,控制不了流量比例。想真正让 1/10 的用户访问到新版本,还是要借助服务网格或专门的发布工具。Deployment 的 pause/resume 更适合解决“分阶段演练,人工判断通过后继续”的场景。

4.4 revisionHistoryLimit:保留多少历史版本才合适

默认 10 这个值,对大多数应用来说偏多了。每个 RS 及其 Pod 模板都占 etcd 存储,版本太多会拖累 apiserver。我自己的习惯是设成 3 或者 5。

spec: revisionHistoryLimit: 3

设成 3 意味着你只能回滚最近 3 次发布,超过的旧 RS 会被控制器回收。太少了怕不够回,太多了浪费存储,3 到 5 这个区间在大多数业务场景里都够用。

另外提一句:如果你对一个 Deployment 只改replicas不碰模板,这不会产生新版本,因为 RS 的 Pod 模板没有变,控制器只需要原地调整副本数。这个行为经常让人困惑,但不影响使用,记住即可。

5. 生产环境里撞过的墙和固定排查套路

5.1 滚动卡在“等待中”,多半和探针有关

我在测试环境模拟故障时,最爱做的就是让新版本镜像一启动就报错,或者故意让就绪探针检查一个不存在的路径。这时候滚动更新的表现非常典型:新 Pod 一直处于0/1 Running,旧 Pod 一个都不动,Deployment 的副本数显示比期望多 1 个,但 Available 的副本数始终到不了 3。

原因就是maxUnavailable的保护:新 Pod 不 Ready,控制器就不能缩旧 Pod,否则可用副本数就会低于期望值。于是整个滚动停在原地,直到progressDeadlineSeconds超时。默认 600 秒,超时后 Deployment 会标记为ProgressDeadlineExceeded。

看状态命令:

kubectl describe deployment/nginx-deploy

Conditions 里会出现:

Progressing: False (ProgressDeadlineExceeded)

这并不代表集群会帮你自动回滚,它只是告诉你“我没能在超时时间内完成,你自己看着办”。真正的止损动作还得你手动rollout undo。所以探针不是可有可无的装饰,它直接决定发布流程是自动推进还是卡死等你收拾。

5.2 镜像 tag 没变,导致发布了但感觉“没更新”

这是另一个高频事故。你改了代码,重新构建镜像,但构建出来的镜像是同一个 tag,比如永远叫nginx:latest。然后你执行kubectl apply,发现 Deployment 没动静,Pod 也没重建。

原因很简单:imagePullPolicy默认在 tag 不是latest时是IfNotPresent,节点上已经有这个镜像,K8s 就直接拿来用了。而且 Deployment 比对的是 Pod 模板哈希,模板没变,根本不会触发滚动。

这类问题的修复建议非常明确:镜像 tag 必须唯一。用 Git commit SHA 做 tag 是最廉价的方案,保证每次构建的 tag 都不同,模板哈希必然变化,发布必然触发。如果你非要省事用latest,记得把imagePullPolicy改成Always,这至少能保证 Pod 重建时会去重新拉取,但你不能依赖它解决“模板没变不滚动”的问题,那需要主动重启:

kubectl rollout restart deployment/nginx-deploy

这条命令会强制触发一次滚动,常用于 ConfigMap 更新后想让 Pod 重新加载配置的场景。

5.3 selector 是创建后不能改的

我见过有人为了让 Deployment 容纳新的 Pod 标签,试图去改selector.matchLabels,结果是 apiserver 直接拒绝:

The Deployment "nginx-deploy" is invalid: spec.selector: Invalid value: ... field is immutable

这不是 K8s 疯了,是设计如此。selector 变了,Deployment 就不再认识自己管理的 RS 和 Pod,那整个控制器循环就崩了。换标签的正当做法是:新建 Deployment,或者通过kubectl label给 Pod 打标并用新 selector 管理,但老 Deployment 要先删掉或缩容,避免两个控制器抢同一个 Pod。

说实话,普通业务很少需要改 selector。如果你发现想改它,先停下来想想是不是一开始的标签设计得太粗了。

5.4 看着全是 Pending?先查调度和配额

滚动更新还有一种卡法:新 Pod 起不来,一直Pending,事件里写着FailedScheduling或者FailedCreate。

前者通常是节点资源不够,kubectl describe pod里能看到:

0/5 nodes are available: 2 Insufficient cpu, 3 Insufficient memory.

这时候你该去查的是集群容量和当前 Pod 的 requests,而不是在 Deployment 上瞎折腾。后者常见于 namespace 配额限制:

failed to create pod: pods "nginx-deploy-xxx" is forbidden: exceeded quota

如果配了 ResourceQuota,新 RS 创建 Pod 时会因为配额不足而失败,RollingUpdate 也会卡住。所以发布不是只看镜像,资源和配额也是发布系统的组成部分,这是很多新手容易忽略的。

5.5 五步定位法:从 Deployment 到 Pod 日志

Deployment 出问题,很多人习惯上来就kubectl logs,其实顺序反了。我的固定套路是从上到下看,一共五步。

第一步,看 Deployment 整体状态:

kubectl get deploy nginx-deploy -o wide

如果AVAILABLE不等于READY,问题已经存在。第二步,看 Deployment 条件:

kubectl describe deploy nginx-deploy

重点看 Conditions 里的Progressing和Available,以及底部 Events,能快速判断是卡滚动、拉镜像失败还是副本缩不了。

第三步,看 ReplicaSet:

kubectl get rs -l app=nginx -o wide

新旧两个 RS 的 DESIRED 和 READY 一对比,立刻就能看出滚动进行到哪一步。第四步,看具体 Pod:

kubectl get pods -l app=nginx kubectl describe pod <pod-name>

Pod 的状态(Pending / ImagePullBackOff / CrashLoopBackOff / Running)和 Events 会告诉你问题出在调度、拉取还是启动阶段。

第五步,才轮到日志:

kubectl logs <pod-name> kubectl logs <pod-name> --previous

--previous在 Pod 崩溃重启后非常管用,能看上一轮容器退出前的日志。很多人上来就看当前日志,结果容器重启了无数次,日志早被冲掉了。

这套顺序是“自顶向下排查”的常规做法,逻辑是:先缩小范围,再定位细节。发布类的故障,百分之八十都能在这五步内找到根因。

最后说一点个人经验。Deployment 这个对象我用了几年,最大的体会是:它把运维里最复杂的一类操作——安全的版本变更——压缩成了一个声明式动作。但声明式不等于“不用懂内部”,恰恰相反,你越理解 ReplicaSet 和 Pod 之间的那层关系,越知道探针和策略参数在滚动里扮演的角色,就越能在故障发生的几分钟内做出正确决定。滚动更新不是魔法,它是一套有边界的自动化,边界之外的事,还得人来兜底。希望这篇能帮你把那部分边界摸清楚。

返回列表