4. Helm 升级机制:helm upgrade 工作原理、三路合并策略、回滚机制与升级钩子

在生产集群中,应用发布不是一次性动作。Helm 的升级机制决定了你能否在故障域隔离的节点迁移场景下,安全、可重复地更新工作负载。本章深入 helm upgrade 的执行链路、三路合并策略、回滚保障以及升级钩子的编排时机,为后续的故障域迁移与容错设计奠定基础。

4.1 helm upgrade 工作原理

helm upgrade [RELEASE] [CHART] 并非简单替换,而是一个包含差异计算、资源协调、状态持久化的三阶段过程。其核心执行流程如下:

  1. 加载 Chart 与 Values:读取本地 Chart 模板、用户提供的 values 文件以及 --set 参数,合并为最终的渲染上下文。
  2. 模板渲染与校验:将合并后的 values 注入模板,生成 Kubernetes 资源清单。Helm 内置的 --dry-run 和 --validate 在此阶段拦截语法错误。
  3. 三路差异计算:对比 当前集群实际状态、上一个 Release 的渲染清单、新 Chart 的渲染清单,生成补丁集(patch set)。
  4. 分批 Apply:按资源类型优先级(Namespace → CRD → 核心资源 → 自定义资源)依次 apply 补丁。默认启用 --atomic 时,任意资源失败会触发自动回滚。
  5. 更新 Release 记录:将新版本号、渲染后的清单、values、状态写入 Secret(默认命名空间 kube-system 或 Release 所在命名空间)。
关键指标:helm upgrade 默认超时 5 分钟(--timeout 可调)。在故障域迁移场景中,建议将超时设置为 10m 以上,避免因节点排水(drain)或 PVC 迁移导致的超时中断。

4.2 三路合并策略(Three-Way Strategic Merge Patch)

Helm 2 使用两路合并(仅对比旧 Chart 与新 Chart),导致手动修改集群资源后升级会丢失变更。Helm 3 引入三路合并,彻底解决了这个问题。

对比维度 两路合并(Helm 2) 三路合并(Helm 3)
输入来源 旧 Chart 清单 + 新 Chart 清单 旧 Chart 清单 + 新 Chart 清单 + 集群当前实际清单
手动变更处理 被覆盖(视为旧 Chart 的一部分) 保留(作为集群实际状态参与合并)
字段删除行为 无法感知手动删除的字段 能识别手动删除,并自动补回(若新 Chart 仍定义该字段)
适用场景 仅 Helm 独占管理资源 Helm + 运维手动调优(如 HPA、PDB 临时调整)

三路合并的核心算法基于 Kubernetes 的 Strategic Merge Patch:

  • 如果字段在新 Chart 和集群实际状态中都存在,以新 Chart 为准。
  • 如果字段在旧 Chart 中存在,但在新 Chart 中被删除,且集群实际状态中该字段已被手动移除,则保持删除。
  • 如果字段在旧 Chart 中存在,在新 Chart 中被删除,但集群实际状态中仍保留,则 Helm 会删除该字段(因为新 Chart 是权威来源)。
避坑提示:三路合并对 list 类型字段(如 containers、volumes)采用 merge by key 策略,而非完全替换。如果你在 values 中删除了一个容器,Helm 不会自动移除集群中对应的容器,除非你在模板中显式使用 tpl 或 merge 函数控制。在故障域迁移中,建议对 DaemonSet 或 StatefulSet 的 nodeSelector 变更使用 --recreate-pods 标志强制重建。
三路合并策略执行流程 旧 Chart 清单 (previous release) 新 Chart 清单 (current upgrade) 集群实际状态 (live cluster) 三路差异计算引擎 Strategic Merge Patch + 字段级 diff 补丁集(Patch Set) 分批 Apply 到集群

4.3 回滚机制

Helm 的回滚基于 Release 版本历史,每个版本对应一个 Secret 记录。回滚命令 helm rollback [RELEASE] [REVISION] 的执行逻辑如下:

  1. 读取目标版本:从 Secret 中取出指定 revision 的渲染清单和 values。
  2. 再次执行三路合并:以目标版本为新 Chart,当前集群状态为实际状态,当前版本为旧 Chart,重新计算补丁。
  3. Apply 补丁:与 upgrade 相同的分批策略。
  4. 创建新 Revision:回滚成功后,Helm 会创建一个新的 revision(不是覆盖),版本号递增。例如从 v5 回滚到 v3,会生成 v6,其内容与 v3 一致。
回滚场景 Helm 行为 对故障域迁移的影响
手动 rollback 保留回滚后的新 revision,旧 revision 仍可访问 迁移失败时可快速切回稳定版本,但需注意 PVC 和 PV 的绑定状态
--atomic 自动回滚 升级失败时自动回滚到上一个 revision,并删除失败 revision 的记录 建议在节点迁移前关闭 --atomic,避免因排水超时导致意外回滚
--history-max 限制 默认保留 10 个 revision,超出后删除最旧的 迁移过程中可能产生多个 revision,建议增大到 20 以上
最佳实践:在故障域迁移前,执行 helm history [RELEASE] 确认当前 revision 和最近 3 个版本的 values 差异。使用 helm get values [RELEASE] --revision N 导出特定版本的 values 作为备份。

4.4 升级钩子(Pre/Post Upgrade Hooks)

Helm 钩子允许你在升级生命周期的特定节点执行自定义 Job。钩子资源通过 metadata.annotations 中的 helm.sh/hook 声明,Helm 会根据钩子权重和策略控制执行顺序。

4.4.1 钩子类型与执行时机

钩子类型 执行时机 典型用途
pre-upgrade 模板渲染后、资源 Apply 前 数据库备份、配置校验、旧资源快照
post-upgrade 所有资源 Apply 成功后 数据迁移、缓存预热、监控告警恢复
pre-rollback 回滚操作执行前 锁定关键资源、记录回滚原因
post-rollback 回滚操作完成后 清理临时资源、发送通知

4.4.2 钩子资源定义示例

apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ .Release.Name }}-pre-upgrade-backup"
  annotations:
    "helm.sh/hook": pre-upgrade
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: backup
        image: bitnami/kubectl:latest
        command:
        - /bin/sh
        - -c
        - |
          kubectl get deployment {{ .Release.Name }} -o yaml > /tmp/backup/{{ .Release.Name }}-pre-upgrade.yaml
          echo "Backup completed for release {{ .Release.Name }}"

4.4.3 钩子在故障域迁移中的设计模式

  • Pre-upgrade 钩子:在节点迁移前,执行 kubectl cordon 标记节点为不可调度,并记录当前 Pod 分布拓扑。
  • Post-upgrade 钩子:迁移完成后,执行 kubectl uncordon 恢复节点调度,并验证所有 Pod 的 topology.kubernetes.io/zone 标签是否符合预期。
  • 钩子权重控制:使用 helm.sh/hook-weight 注解(默认 0,可正可负)控制多个同类型钩子的执行顺序。权重越小越先执行。
  • 钩子删除策略:建议设置 helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation,避免历史钩子 Job 残留占用集群资源。
Helm Upgrade 钩子执行时序 升级时间轴 pre-upgrade 备份 / 封锁节点 模板渲染 三路合并 Apply 资源 分批更新 post-upgrade 解封 / 验证 失败时自动回滚(--atomic)

4.5 升级过程中的故障域隔离考量

在故障域隔离的节点迁移场景中,Helm 升级机制需要额外关注以下三点:

  • Pod 分布拓扑:升级前通过 pre-upgrade 钩子记录当前 Pod 的 topology.kubernetes.io/zone 分布,升级后通过 post-upgrade 钩子验证是否仍满足 podAntiAffinity 或 topologySpreadConstraints。
  • 节点排水与升级顺序:如果升级涉及 nodeSelector 或 affinity 变更,建议先执行 kubectl drain 排空节点,再执行 helm upgrade,最后 kubectl uncordon。钩子可以自动化这一流程。
  • StatefulSet 的序号保留:三路合并不会改变 StatefulSet 的 Pod 序号。在跨故障域迁移时,如果目标节点不可用,Helm 升级会卡在 Pending Pod 上,直到超时。此时应使用 --force 标志(仅用于 StatefulSet 的 spec.selector 变更)或手动删除卡住的 Pod。
总结:Helm 的三路合并策略和钩子系统为故障域迁移提供了可编程的升级前校验和升级后验证能力。理解 helm upgrade 的差异计算逻辑,能帮助你在节点迁移场景中精确控制资源变更范围,避免因手动干预导致的配置漂移。