欢迎光临
我们一直在努力

Deployment 滚动更新机制详解(企业级面试与运维实战)

文章目录

  • Deployment 滚动更新机制详解(企业级面试与运维实战)
    • 一、Deployment 如何管理 Pod?(三层级联关系)
    • 二、滚动更新完整流程(以 3 副本为例)
      • 第一阶段:创建新 RS
      • 第二阶段:逐步滚动(以默认策略 maxSurge=25%, maxUnavailable=25% 为例)
      • 第三阶段:完成
    • 三、滚动更新核心参数(面试必考)
      • 1. maxUnavailable(最大不可用数)
      • 2. maxSurge(最大峰值数)
      • 3. 两个参数如何配合?
    • 四、滚动更新的状态与控制(运维核心技能)
      • 1. 查看更新进度
      • 2. 查看历史版本(每个版本对应一个 RS)
      • 3. 暂停与恢复(金丝雀发布的核心手法)
      • 4. 手动控制更新比例(精细灰度)
      • 5. 回滚
    • 五、滚动更新卡住的排查(生产环境最高频问题)
      • 现象
      • 排查思路(四步定位)
    • 六、滚动更新故障与应对(运维实战)
      • 场景 1:新版本有严重 Bug,需要立即止损
      • 场景 2:读不到新版本日志,想先全部切过去再排查
      • 场景 3:更新过程中想终止并保持现状
      • 场景 4:Deployment 更新超时自动标记失败
    • 七、面试高频追问(加分要点)
      • 1. 滚动更新期间,服务真的不中断吗?
      • 2. 为什么回滚能"一键完成"?
      • 3. 如何控制回滚历史的保留数量?
      • 4. 滚动更新与 Recreate 策略的区别?
      • 5. 滚动更新对 etcd 和 apiserver 的压力?
    • 八、一个完整的生产级滚动更新示例

Deployment 滚动更新机制详解(企业级面试与运维实战)

Deployment 的滚动更新是 Kubernetes 生产环境中最核心的发布机制。它解决的问题是:更新应用时,如何做到服务不中断、流量不丢失、故障可回滚。下面我从原理到实操、从面试到排障,完整拆解。


一、Deployment 如何管理 Pod?(三层级联关系)

在深入滚动更新之前,先理解 Deployment 的层级结构——这是整个滚动更新的地基:

Deployment(用户操作入口)
│ 管理

ReplicaSet(版本快照,每个版本对应一个 RS)
│ 管理

Pod(实际运行容器)

核心结论:

  • Deployment 不直接管理 Pod,它只管理 ReplicaSet
  • 每次修改 Deployment 的 Pod 模板(镜像、环境变量等),都会创建一个新的 ReplicaSet
  • 旧版本对应旧 RS,新版本对应新 RS,滚动更新就是在新旧两个 RS 之间"搬"副本数

关键标志:kubectl get deploy 输出的 DESIRED 是 Pod 总数,而每个 RS 的 DESIRED 是各自的副本数,两者之和恒等于 Deployment 的期望副本数(更新过程中)。


二、滚动更新完整流程(以 3 副本为例)

假设当前 Deployment 有 3 个副本,镜像版本为 nginx:1.19,现在要更新到 nginx:1.21:

初始状态:
Deployment (replicas=3)
├── RS-v1 (nginx:1.19, replicas=3) ← 当前版本
└── RS-v2 (nginx:1.21, replicas=0) ← 尚未创建

执行 kubectl set image deploy/nginx nginx=nginx:1.21

第一阶段:创建新 RS

Deployment (replicas=3)
├── RS-v1 (nginx:1.19, replicas=0 → 3, maxUnavailable=1)
└── RS-v2 (nginx:1.21, replicas=0 → 1) ← 新 RS 创建,先扩容 1 个

  • 创建 RS-v2,replicas=0(初始)
  • 根据 maxUnavailable 和 maxSurge 计算可扩缩容的幅度(见下文参数详解)
  • RS-v2 先扩容到 1(因为 maxSurge=1,允许超出期望 1 个)

第二阶段:逐步滚动(以默认策略 maxSurge=25%, maxUnavailable=25% 为例)

第 1 轮:RS-v2: 1, RS-v1: 2(总 3 个,均在服务中)
第 2 轮:RS-v2: 2, RS-v1: 1
第 3 轮:RS-v2: 3, RS-v1: 0

每轮动作:

  • 新 RS 扩容(+1):创建新版本 Pod,等待其就绪(Readiness Probe 通过)
  • 旧 RS 缩容(-1):等新 Pod 就绪后,才删除一个旧版本 Pod
  • 循环往复,直到所有副本都切到新版本
  • 第三阶段:完成

    Deployment (replicas=3)
    ├── RS-v1 (nginx:1.19, replicas=0) ← 保留但缩容为 0(用于回滚)
    └── RS-v2 (nginx:1.21, replicas=3) ← 当前版本

    关键点:

    • 旧 RS 不会删除,只是缩容到 0。这是为了支持一键回滚
    • 新 Pod 必须通过 Readiness Probe 才会被视为"就绪"
    • 如果新 Pod 一直不就绪,滚动更新会卡住(不会继续缩容旧 RS)

    三、滚动更新核心参数(面试必考)

    滚动更新的节奏由 strategy.rollingUpdate 下的两个参数控制:

    spec:
    strategy:
    type: RollingUpdate
    rollingUpdate:
    maxUnavailable: 25% # 更新过程中允许最多 25% 的副本不可用
    maxSurge: 25% # 更新过程中允许超出期望副本数最多 25%

    1. maxUnavailable(最大不可用数)

    • 含义:滚动更新过程中,允许同时"不可用"(正在被替换)的 Pod 最大数量
    • 作用:保证服务有足够的可用副本,避免全部 Pod 同时被替换导致服务中断
    • 默认 25%(向上取整):3 副本 → 允许 1 个不可用(3 × 25% = 0.75 → 向上取整 = 1)

    2. maxSurge(最大峰值数)

    • 含义:滚动更新过程中,允许超出期望副本数的最大 Pod 数量
    • 作用:决定新版本 Pod 可以"提前"创建多少个,加快更新速度
    • 默认 25%(向上取整):3 副本 → 允许超出 1 个(3 × 25% = 0.75 → 向上取整 = 1)

    3. 两个参数如何配合?

    滚动更新的节奏可以理解为:

    每轮可缩容旧 Pod 数 = maxUnavailable
    每轮可扩容新 Pod 数 = maxSurge

    极端场景举例:

    配置效果
    maxUnavailable: 1, maxSurge: 1 一次只更新 1 个 Pod,更新速度慢但最安全
    maxUnavailable: 100%, maxSurge: 0% 先缩容所有旧 Pod(全部不可用),再创建新 Pod → 等价于 Recreate 策略(先删后建)
    maxUnavailable: 0%, maxSurge: 100% 先创建全部新 Pod(峰值翻倍,消耗双倍资源),全部就绪后再删旧 Pod → 零停机但资源占用高(蓝绿发布思想)

    四、滚动更新的状态与控制(运维核心技能)

    1. 查看更新进度

    # 查看更新状态(会阻塞直到完成或超时)
    kubectl rollout status deploy/nginx

    # 输出示例:
    # Waiting for deployment "nginx" rollout to finish: 1 out of 3 new replicas have been updated…
    # deployment "nginx" successfully rolled out

    2. 查看历史版本(每个版本对应一个 RS)

    kubectl rollout history deploy/nginx

    # 输出示例:
    # REVISION CHANGE-CAUSE
    # 1 <none> # 可通过 –record 记录变更原因
    # 2 kubectl set image deploy/nginx nginx=nginx:1.21

    注意:CHANGE-CAUSE 来自 Deployment 的 metadata.annotations.kubernetes.io/change-cause。K8s v1.26+ 已移除 –record 参数,需手动添加 annotation:

    kubectl annotate deploy/nginx kubernetes.io/change-cause="升级nginx到1.21"

    3. 暂停与恢复(金丝雀发布的核心手法)

    暂停:更新过程中暂停,让新旧版本并存一段时间,验证新版本稳定性后再继续:

    # 触发更新后立即暂停
    kubectl set image deploy/nginx nginx=nginx:1.21
    kubectl rollout pause deploy/nginx

    # 此时:RS-v2 创建了 1 个新 Pod 并停止,其余 2 个还在旧版本
    # 相当于手动控制的金丝雀发布

    恢复:

    kubectl rollout resume deploy/nginx
    # 继续完成剩余的滚动更新

    使用场景:先更新 1 个 Pod 验证日志/监控无异常,再恢复继续全量更新。

    4. 手动控制更新比例(精细灰度)

    通过直接修改 maxSurge 和 maxUnavailable,可以控制每次更新的 Pod 数量:

    # 每次只更新 1 个 Pod
    kubectl patch deploy/nginx -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'

    # 或
    kubectl edit deploy/nginx # 修改后保存,会自动触发一次滚动更新

    5. 回滚

    # 回滚到上一个版本
    kubectl rollout undo deploy/nginx

    # 回滚到指定版本
    kubectl rollout undo deploy/nginx –to-revision=1

    # 查看回滚进度
    kubectl rollout status deploy/nginx

    回滚原理:让旧 RS 重新扩容、新 RS 缩容,和正向更新的过程完全对称。


    五、滚动更新卡住的排查(生产环境最高频问题)

    现象

    kubectl rollout status 长时间停留在:

    Waiting for deployment "nginx" rollout to finish: 1 out of 3 new replicas have been updated…

    排查思路(四步定位)

    Step 1:查看 Deployment 状态与事件

    kubectl describe deploy/nginx

    重点看 Conditions 字段中的 Progressing 和 Available:

    Conditions:
    Type Status Reason

    Available True MinimumReplicasAvailable
    Progressing False ProgressDeadlineExceeded # ← 更新超时失败

    Step 2:查看新旧 RS 的副本分布与状态

    kubectl get rs -l app=nginx
    # NAME DESIRED CURRENT READY AGE
    # nginx-abc 3 3 3 1h # 旧 RS
    # nginx-def 2 2 0 5m # 新 RS,READY=0 → 问题在这里

    Step 3:查看新 RS 的 Pod 状态与事件

    kubectl get pods -l app=nginx | grep nginx-def
    kubectl describe pod/nginx-def-xxxxx

    Step 4:定位根因

    现象根因
    Pod 一直 ContainerCreating 镜像拉取失败(私有仓库认证)、存储挂载失败(PVC 未绑)
    Pod Running 但 READY 0/1 Readiness Probe 失败(最常见)—— 端口探测不通、健康检查路径返回非 200
    Pod CrashLoopBackOff 新版本启动即崩溃(环境变量缺失、配置错误、依赖服务未就绪)
    新 Pod 一直 Pending 资源不足、节点亲和性不满足

    最典型场景:新版本镜像有 bug,启动后 Readiness Probe 一直失败 → 新 Pod 永远不就绪 → 滚动更新永远不会继续缩容旧 RS → 服务保持旧版本可用(这是滚动更新的设计优点:宁可卡住也不中断)。


    六、滚动更新故障与应对(运维实战)

    场景 1:新版本有严重 Bug,需要立即止损

    # 紧急回滚(最快速)
    kubectl rollout undo deploy/nginx

    # 或直接改回旧镜像
    kubectl set image deploy/nginx nginx=nginx:1.19

    场景 2:读不到新版本日志,想先全部切过去再排查

    # 将 maxUnavailable 设为 100%(等价于先删后建)
    kubectl patch deploy/nginx –type='json' \\
    -p='[{"op": "replace", "path": "/spec/strategy/rollingUpdate/maxUnavailable", "value": "100%"}]'

    # 此时会立即缩容所有旧 Pod,全部切换到新版本
    # 注意:这会短暂中断服务(等价于 Recreate 策略)

    场景 3:更新过程中想终止并保持现状

    # 暂停滚动更新
    kubectl rollout pause deploy/nginx

    # 此时新旧版本并存,保持当前状态不再变化
    # 适合"先停下来观察一下新版本表现"的场景

    场景 4:Deployment 更新超时自动标记失败

    默认 progressDeadlineSeconds=600(10分钟),超过后 Deployment 被标记为 Progressing=False,但不会自动回滚,只是停止更新。需要手动介入:

    # 查看失败原因
    kubectl describe deploy/nginx | grep -A5 Conditions

    # 手动回滚
    kubectl rollout undo deploy/nginx


    七、面试高频追问(加分要点)

    1. 滚动更新期间,服务真的不中断吗?

    不完全是。关键取决于:

    • Readiness Probe 是否配置正确——新 Pod 就绪后才加入 Service 的 Endpoints,流量才会切过去
    • Pod 终止时是否优雅——旧 Pod 收到 SIGTERM 后是否能在宽限期内完成"排空"(处理完当前请求再退出)

    面试表述:“滚动更新不中断服务的前提是配置了正确的 Readiness Probe 和 preStop Hook。如果没有 Readiness Probe,新 Pod 刚启动就会被加入 Endpoints,此时容器可能还没真正就绪,会短暂返回 5xx。”

    2. 为什么回滚能"一键完成"?

    因为每次更新都会留下一个对应的 RS(保留完整的历史 Pod 模板)。回滚只是把目标 RS 重新扩容、当前 RS 缩容——本质也是一次滚动更新,只是方向相反。

    3. 如何控制回滚历史的保留数量?

    通过 spec.revisionHistoryLimit(默认 10)。超过数量的旧 RS 会被 GC 清理,但注意:缩容为 0 的 RS 不会被删除(保留用于回滚),只有超过 revisionHistoryLimit 的 RS 才会被彻底删除。

    4. 滚动更新与 Recreate 策略的区别?

    RollingUpdate(默认)Recreate
    服务中断 通常无(前提是配置正确) 有(先删后建)
    资源消耗 可能瞬时超出期望副本数(maxSurge) 无额外消耗
    适用场景 无状态服务、微服务 有状态服务(数据库)等无法并存的场景
    更新速度 较慢(受 maxSurge/maxUnavailable 控制)

    5. 滚动更新对 etcd 和 apiserver 的压力?

    每次滚动更新会触发大量 Pod 创建/删除事件,这些事件会写入 etcd 并广播给所有控制器。大规模集群(数百节点、数千 Pod)同时滚动更新时,需要控制更新速率,否则可能压垮 apiserver。

    优化手段:

    • 分批更新(先更新部分节点或部分 Deployment)
    • 使用 maxSurge: 1, maxUnavailable: 0 保守配置
    • 结合 PDB(PodDisruptionBudget) 保护关键服务的副本可用性

    八、一个完整的生产级滚动更新示例

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx
    annotations:
    kubernetes.io/change-cause: "升级nginx到1.21,修复安全漏洞"
    spec:
    replicas: 5
    revisionHistoryLimit: 5 # 保留最近 5 个历史版本
    strategy:
    type: RollingUpdate
    rollingUpdate:
    maxUnavailable: 1 # 最多 1 个副本不可用(保守)
    maxSurge: 2 # 最多超出 2 个副本(加快速度)
    selector:
    matchLabels:
    app: nginx
    template:
    metadata:
    labels:
    app: nginx
    spec:
    terminationGracePeriodSeconds: 30 # 优雅终止等待时间
    containers:
    name: nginx
    image: nginx:1.21
    ports:
    containerPort: 80
    readinessProbe: # 就绪探针(关键!)
    httpGet:
    path: /healthz
    port: 80
    initialDelaySeconds: 5
    periodSeconds: 5
    lifecycle:
    preStop: # 优雅下线:先摘流量再停止
    exec:
    command: ["/bin/sh", "-c", "sleep 5"]

    执行更新:

    # 1. 触发更新
    kubectl apply -f deployment.yaml

    # 2. 监控进度
    kubectl rollout status deploy/nginx

    # 3. 确认完成后查看版本
    kubectl rollout history deploy/nginx

    # 4. 如果出问题,回滚
    kubectl rollout undo deploy/nginx

    preStop Hook 的作用:当 Pod 被终止时,先执行 sleep 5,给 kube-proxy 时间从 Endpoints 中移除该 Pod 的 IP,确保不再有新流量进来,然后才收到 SIGTERM 退出。这是避免"正在处理的请求被中断"的关键。


    赞(0)
    未经允许不得转载:171主机测评 » Deployment 滚动更新机制详解(企业级面试与运维实战)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址