Skip to content

Helm Chart、模板渲染与生产发布全过程

Helm是Kubernetes应用包和Release管理工具。Chart把Deployment、Service、Ingress、ConfigMap等资源组织成可版本化模板,Values提供环境输入,Helm客户端在发布前渲染出普通Kubernetes对象,再调用API Server创建或更新。

Helm不是另一套容器编排器:资源提交后仍由Deployment、Job、Service、CSI等Kubernetes Controller持续Reconcile。Helm upgrade成功也不一定代表Pod Ready和业务成功;rollback主要恢复历史清单,不能撤销数据库、消息和外部副作用。

学习目标

学完后应能:

  1. 解释Helm 3客户端、Chart、Release和Kubernetes API的关系。
  2. 说明Chart.yaml、values.yaml、templates、crds、charts和schema职责。
  3. 准确解释多Values文件、--set和Upgrade复用值的优先级。
  4. 解释Map合并、List替换、YAML类型和字符串陷阱。
  5. 使用.Values/.Release/.Chart/.Capabilities/.Files和作用域变量。
  6. 正确使用includenindenttoYamlrequiredfailtpl
  7. 解释Install/Upgrade的渲染、Hook、API提交、Wait和Release记录全过程。
  8. 解释Helm 3 Release为何保存为Namespace内Secret及其安全风险。
  9. 区分--wait--wait-for-jobs--atomic--cleanup-on-fail--force
  10. 解释三方Merge思想、手工漂移和资源所有权冲突。
  11. 解释Hook执行、删除策略、幂等和数据库迁移边界。
  12. 解释CRD安装、升级、删除为什么不能当普通模板处理。
  13. 设计OCI、Digest、依赖锁、签名、Schema和供应链准入。
  14. 排查模板错误、Pending Release、Hook失败、Ownership和Rollback失败。

一、Helm 3架构

Helm 2曾依赖集群内Tiller,Helm 3取消Tiller。常见执行链:

mermaid
flowchart TD
    A["Helm CLI读取Chart与Values"] --> B["本地执行Go Template渲染"]
    B --> C["可选Lint、Schema和能力校验"]
    C --> D["使用kubeconfig身份调用API Server"]
    D --> E["Kubernetes创建/更新资源"]
    E --> F["Helm在Release Namespace保存Revision记录"]
    E --> G["各Kubernetes Controller继续Reconcile"]

因此执行Helm的人拥有什么Kubernetes权限,Helm就大致能做什么。Helm不是权限边界,生产发布需要专用ServiceAccount、最小RBAC和审计。

二、Chart、Release和Revision

概念含义示例
Chart可复用应用包和模板order-api-2.3.1.tgz
Chart VersionChart包版本version: 2.3.1
appVersion应用展示版本,不自动控制镜像appVersion: 1.18.4
Release某Namespace中一次命名安装order-api-prod
RevisionRelease每次成功/失败操作产生的历史版本1、2、3

同一个Chart可以安装为多个Release:

text
Chart order-api 2.3.1
├── Release order-api-dev / namespace dev
├── Release order-api-staging / namespace staging
└── Release order-api-prod / namespace prod

Chart Version、appVersion、镜像Tag/Digest和Release Revision是四个不同概念,发布记录应全部保存。

三、标准Chart目录

text
order-api/
├── Chart.yaml
├── Chart.lock
├── values.yaml
├── values.schema.json
├── README.md
├── templates/
│   ├── _helpers.tpl
│   ├── serviceaccount.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── networkpolicy.yaml
│   ├── NOTES.txt
│   └── tests/
│       └── test-connection.yaml
├── charts/
└── crds/
路径用途
Chart.yaml名称、版本、依赖和Chart类型等元数据
values.yaml安全、可运行的默认输入
values.schema.jsonValues类型、枚举、范围和必填校验
templates/Go Template渲染的Kubernetes清单
_helpers.tpl命名、Label等可复用命名模板,不直接输出对象
charts/下载/打包后的依赖Chart
Chart.lock锁定解析后的依赖版本和Digest
crds/安装CRD定义的特殊目录,不走普通模板生命周期
NOTES.txt安装后给操作者的说明,不能泄露Secret

四、Chart.yaml

yaml
apiVersion: v2
name: order-api
description: Commercial order API workload
type: application
version: 2.3.1
appVersion: "1.18.4"
kubeVersion: ">=1.27.0-0"
dependencies:
  - name: common
    version: 1.6.0
    repository: oci://registry.example.com/helm

4.1 version

Chart包自身的SemVer。模板、默认值或发布行为发生破坏性变化时应升级Major,不要覆盖已发布 2.3.1 包。

4.2 appVersion

只是应用版本元数据,Helm不会自动把它写入镜像。模板必须显式引用:

yaml
image: "{{ .Values.image.repository }}@{{ .Values.image.digest }}"

4.3 kubeVersion

可声明支持的Kubernetes版本范围,在渲染/安装时提前拒绝明显不兼容集群。但它不能证明目标CNI、CSI、Ingress Controller和Admission Policy兼容。

五、Values合并优先级

常见优先级从低到高:

text
Chart values.yaml
→ 父Chart为依赖提供的值
→ 第一个 -f values文件
→ 后续 -f values文件(后者覆盖前者)
→ --set/--set-string/--set-file/--set-json

例如:

bash
helm upgrade --install order-api ./order-api \
  -n commerce \
  -f values.yaml \
  -f values-prod.yaml \
  -f values-prod-region-a.yaml \
  --set-string image.digest='sha256:0123...'

最终值不是任意一个文件,而是完整合并结果。发布前应保存:

bash
helm template order-api ./order-api \
  -n commerce \
  -f values-prod.yaml \
  -f values-prod-region-a.yaml

的最终渲染产物和非敏感有效Values。

六、Map合并与List替换

Maps通常按Key递归合并:

yaml
# values.yaml
resources:
  requests:
    cpu: 100m
    memory: 256Mi
  limits:
    memory: 512Mi
yaml
# values-prod.yaml
resources:
  requests:
    cpu: 500m

最终通常保留memory和limits,只覆盖cpu。

Lists通常整体替换,不按元素智能合并:

yaml
# defaults
tolerations:
  - key: dedicated
    value: backend

# prod
tolerations:
  - key: zone
    value: a

最终可能只有prod这一项,默认项消失。复杂可扩展配置优先设计为Map或提供清晰完整List覆盖契约。

七、YAML类型陷阱

以下值容易被解析为不同类型:

yaml
featureEnabled: false
replicas: 3
port: 8080
version: "00123"
memory: 512Mi

命令行:

bash
--set featureEnabled=false
--set replicas=3
--set-string version=00123

如果用普通 --set version=00123,值可能被推断为数字并丢失前导零。字符串标识、Digest、账号ID和大整数优先 --set-string 或Values文件加引号。

Kubernetes字段也有类型要求:环境变量value必须是字符串,不能把布尔直接渲染成未引号YAML而导致API校验失败。

八、Upgrade时旧Values的复用陷阱

Helm Upgrade可选择复用旧Release Values或重置到新Chart默认值。--reuse-values会把旧值带入新Chart,风险:

  • 新Chart安全默认值无法生效。
  • 已删除字段继续残留。
  • 旧类型与新Schema冲突。
  • 生产行为依赖多年历史操作,无法从Git重现。

生产建议每次从版本化Values文件和明确参数重建最终值,谨慎使用reuse;升级前比较:

bash
helm get values order-api -n commerce --all
helm show values <new-chart>

具体reset/reuse默认行为和组合Flag随Helm版本确认。

九、values.schema.json

示例:

json
{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["image", "resources"],
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 2,
      "maximum": 100
    },
    "image": {
      "type": "object",
      "required": ["repository", "digest"],
      "properties": {
        "repository": {"type": "string"},
        "digest": {
          "type": "string",
          "pattern": "^sha256:[a-f0-9]{64}$"
        }
      }
    }
  }
}

Schema可在lint/template/install/upgrade阶段发现类型、范围和必填错误。它不能校验镜像Digest真实存在、Service可访问和业务兼容,仍需服务端Dry Run与集成测试。

十、模板渲染上下文

模板可访问:

对象含义
.Values合并后的Values
.ReleaseRelease名称、Namespace、Revision、是否升级等
.ChartChart.yaml元数据
.Capabilities目标Kubernetes版本和可用API能力
.Template当前模板信息
.FilesChart内非template文件

示例:

yaml
metadata:
  name: {{ include "order-api.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | quote }}

十一、Go Template不是YAML感知引擎

Helm先执行文本模板,再把结果作为YAML提交。缩进和空白错误会产生无效清单:

yaml
resources:
{{ toYaml .Values.resources | nindent 10 }}

nindent 10先换行再缩进10空格。缩进数字必须与模板位置匹配,不能盲抄。

使用:

bash
helm template order-api ./chart --debug

查看最终文本,而不是只读模板猜测。

十二、if、with、range的作用域

yaml
{{- with .Values.podAnnotations }}
annotations:
  {{- toYaml . | nindent 8 }}
{{- end }}

with内部的 . 变成 podAnnotations,访问根上下文要保存 $

yaml
{{- range .Values.extraContainers }}
- name: {{ .name }}
  image: {{ .image }}
  env:
    - name: RELEASE_NAME
      value: {{ $.Release.Name | quote }}
{{- end }}

大量“nil pointer evaluating”来自作用域变化后仍把 . 当根对象。

十三、template与include

定义:

{{- define "order-api.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

使用 include

yaml
labels:
  {{- include "order-api.labels" . | nindent 4 }}

include返回字符串,可继续管道处理;template更像直接输出,难以接 nindent。Chart helper名称全局可见,依赖Chart可能冲突,使用Chart前缀和版本化命名。

十四、required、fail和default

{{- $digest := required "image.digest is required" .Values.image.digest -}}

组合约束:

{{- if and .Values.ingress.enabled (not .Values.ingress.host) -}}
{{- fail "ingress.host is required when ingress.enabled=true" -}}
{{- end -}}

default对空值的定义可能把 false0、空字符串视为空:

{{ default true .Values.feature.enabled }}

用户显式设置false时可能仍得到true。布尔配置应通过Schema和 hasKey/明确逻辑处理,不要滥用default。

十五、tpl的能力和风险

tpl把Values中的字符串再次当模板执行:

{{ tpl .Values.extraConfig . }}

这提高灵活性,也扩大模板执行面。不可信租户可利用模板函数、lookup等读取集群对象或生成危险清单,能力取决于Helm身份和函数集合。

生产Chart不应默认对任意用户Values执行tpl;只允许受控字段并进行代码评审。

十六、lookup让渲染依赖集群

{{- $existing := lookup "v1" "Secret" .Release.Namespace "order-api" -}}

lookup查询当前集群,导致:

  • 离线 helm template 与在线渲染不同。
  • 不同集群产生不同清单。
  • Helm身份需要读取权限。
  • GitOps难以复现。
  • 集群暂时不可用导致渲染失败。

仅在必要的受控兼容场景使用,优先把期望状态显式放Values/Git。

十七、.Capabilities做API兼容

{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }}
apiVersion: policy/v1
{{- else }}
{{- fail "policy/v1 PodDisruptionBudget is required" }}
{{- end }}

离线template需要显式传目标Kubernetes版本/API能力,否则可能走错分支。发布验证最终仍使用目标集群服务端Dry Run。

十八、.Files与配置文件

data:
  application.yml: |
{{ .Files.Get "config/application.yml" | nindent 4 }}

.Files不能读取templates目录外Chart包未包含的任意本机文件。大文件和Secret不应打进Chart,因为Chart包、缓存、Registry和Release历史都可能保留内容。

十九、空白控制陷阱

{{--}} 会删除相邻空白。两边都过度裁剪可能把YAML拼在一行:

text
food: "PIZZA"mug: "true"

每个条件分支都应对最终渲染做YAML解析和快照测试,不凭模板视觉判断。

二十、命名和Label Helper

商业Chart统一:

  • app.kubernetes.io/name
  • app.kubernetes.io/instance
  • app.kubernetes.io/version
  • app.kubernetes.io/managed-by
  • helm.sh/chart

名称需满足DNS长度,避免直接拼长Release和Chart名。Selector Label创建后常不可变,应把稳定selector和可变version Label分开,升级Chart不能随意改变selector helper。

二十一、完整values.yaml设计

yaml
replicaCount: 2

image:
  repository: registry.example.com/order-api
  digest: ""
  pullPolicy: IfNotPresent

serviceAccount:
  create: true
  automount: false

service:
  port: 80
  targetPort: http

ingress:
  enabled: false
  className: public-nginx
  host: ""
  tlsSecretName: ""

resources:
  requests:
    cpu: 250m
    memory: 512Mi
  limits:
    cpu: "1"
    memory: 1Gi

probes:
  startup:
    failureThreshold: 24
    periodSeconds: 5
  readiness:
    failureThreshold: 3
    periodSeconds: 5
  liveness:
    failureThreshold: 3
    periodSeconds: 10

config:
  version: v42
  checksum: ""

credential:
  existingSecret: order-db-credential-v7

默认值应安全:Ingress默认关闭、镜像Digest必填、Secret只引用existingSecret、不生成示例弱密码。

二十二、Deployment模板关键片段

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "order-api.fullname" . }}
  labels:
    {{- include "order-api.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      {{- include "order-api.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "order-api.selectorLabels" . | nindent 8 }}
      annotations:
        config.example.com/version: {{ .Values.config.version | quote }}
        config.example.com/checksum: {{ required "config.checksum is required" .Values.config.checksum | quote }}
    spec:
      serviceAccountName: {{ include "order-api.serviceAccountName" . }}
      automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
      containers:
        - name: order-api
          image: "{{ .Values.image.repository }}@{{ required "image.digest is required" .Values.image.digest }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 8080
          envFrom:
            - secretRef:
                name: {{ required "credential.existingSecret is required" .Values.credential.existingSecret }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

模板仅是片段,完整Chart还要Probe、SecurityContext、ConfigMap、Service和NetworkPolicy。不要在模板中自动创建明文Secret。

二十三、Chart加载与渲染全过程

mermaid
flowchart TD
    A["定位本地目录、tgz或OCI Chart"] --> B["读取Chart.yaml并校验依赖"]
    B --> C["加载values.yaml与用户覆盖"]
    C --> D["按规则Coalesce最终Values"]
    D --> E["执行values.schema.json校验"]
    E --> F["构建Release/Chart/Capabilities上下文"]
    F --> G["执行templates Go Template"]
    G --> H["按Hook/普通资源/NOTES分类"]
    H --> I["生成最终Kubernetes清单"]

渲染是客户端计算,模板有相同输入、相同Helm版本和能力时应尽量确定性。不要使用随机函数每次生成不可预测Secret/名称,升级会持续漂移。

二十四、Install全过程

mermaid
flowchart TD
    A["helm install"] --> B["加载、合并、Schema和模板渲染"]
    B --> C["保存pending-install Revision"]
    C --> D["执行pre-install Hook"]
    D --> E{"Hook是否成功"}
    E -- "否" --> F["记录failed并按Flag处理"]
    E -- "是" --> G["向API Server创建普通资源"]
    G --> H["可选wait观察Ready/Complete"]
    H --> I["执行post-install Hook"]
    I --> J["把Revision状态更新为deployed/failed"]

实际顺序还受CRD、资源排序、Hook权重和Controller行为影响。Helm通常先持久化pending状态再执行操作,因此客户端中断可能留下pending Revision;API创建成功也不等于资源业务就绪。

二十五、Release记录保存在哪里

Helm 3默认把Release Revision保存在Release Namespace内的Secret,名称通常类似:

text
sh.helm.release.v1.order-api.v12

记录包含Chart、Values、渲染Manifest、Hook和状态等编码/压缩数据。安全影响:

  • 能读取这些Secret的人可能获得历史渲染内容。
  • 如果Chart把生产Secret值直接渲染进Kubernetes Secret,Release历史也可能长期保留Base64值。
  • Namespace中每次Revision都会增加存储。

不要把Secret明文放Values;优先引用External Secret/已有Secret。限制Release Secret的RBAC和保留历史数量。

二十六、Install失败的Release状态

失败安装可能留下:

  • Release状态failed。
  • 已创建的部分资源。
  • Hook资源。
  • CRD。
  • 外部系统副作用。

再次同名install可能报“name is still in use”。先:

bash
helm status order-api -n commerce
helm history order-api -n commerce
helm get manifest order-api -n commerce

再决定rollback、uninstall或修复upgrade。不要直接删除Release Secret伪造状态,可能让资源变成孤儿且历史不可恢复。

二十七、Upgrade全过程

mermaid
flowchart TD
    A["读取当前Release和新Chart/Values"] --> B["渲染新Manifest"]
    B --> C["保存pending-upgrade新Revision"]
    C --> D["执行pre-upgrade Hook"]
    D --> E["比较旧Manifest、Live对象和新Manifest"]
    E --> F["向API Server创建/Patch/替换资源"]
    F --> G["可选wait观察Rollout/Job"]
    G --> H["执行post-upgrade Hook"]
    H --> I["更新Revision为deployed/failed"]

Helm 3升级采用三方合并思想,考虑旧Release清单、集群Live状态和新清单,降低两方Patch丢失注入字段等问题。但它默认不等同于Server-Side Apply字段所有权,具体资源Patch策略受类型和Helm版本影响。

二十八、手工修改与Drift

如果运维手工修改Helm管理的Deployment:

  • 下一次Upgrade可能保留、覆盖或冲突,取决于新旧/Live差异和字段。
  • Helm历史记录不包含手工变更意图。
  • Rollback可能覆盖Live状态。
  • Git和集群不一致。

使用:

bash
helm get manifest order-api -n commerce
helm get values order-api -n commerce --all
kubectl get deployment order-api -n commerce -o yaml

比较期望与Live。紧急手工修复后必须回写Chart/Values并重新发布,不能让漂移永久存在。

二十九、资源所有权

Helm通常通过Label/Annotation识别资源归属,例如:

yaml
metadata:
  labels:
    app.kubernetes.io/managed-by: Helm
  annotations:
    meta.helm.sh/release-name: order-api
    meta.helm.sh/release-namespace: commerce

如果同名资源已由人工或另一个Release创建,Install/Upgrade可能拒绝接管,提示invalid ownership metadata。

不要为了通过而盲目补Annotation:先确认资源真实所有者、字段兼容、删除/回滚影响。两个Release不能安全共同管理同一资源。

三十、helm upgrade --install

bash
helm upgrade --install order-api oci://registry.example.com/helm/order-api \
  --version 2.3.1 \
  -n commerce \
  --create-namespace \
  -f values-prod.yaml \
  --wait \
  --timeout 10m

它实现“存在则升级,不存在则安装”的幂等入口,但不保证业务幂等。并发两条流水线仍可能冲突,应对同一Release加发布锁。

三十一、--wait到底等什么

--wait让Helm在超时内等待部分资源达到Ready/可用条件,例如Pod、PVC、Service、Deployment/StatefulSet副本等,具体集合和判定随Helm版本。

它通常不证明:

  • Ingress公网DNS/TLS可用。
  • 所有业务接口成功。
  • 外部数据库迁移正确。
  • MQ Lag正常。
  • Job完成,除非使用适当 --wait-for-jobs/Hook语义。

所以Wait后仍需业务Smoke和版本指标闸门。

三十二、--wait-for-jobs

要求普通Job等到Complete或失败,受timeout控制。长批任务不应无界阻塞发布;数据库迁移Job要有activeDeadline、幂等和失败恢复。

Hook Job本身有Hook等待语义,普通Job与Hook Job不能混为一谈。

三十三、--atomic不是分布式事务

Install使用atomic时,失败可自动卸载;Upgrade使用atomic时,失败可自动尝试回滚,通常隐含wait。

它不能撤销:

  • 数据库DDL/DML。
  • Hook已调用的外部API。
  • 已发送消息/邮件。
  • 已签发证书。
  • CRD Schema更新。
  • 应用已写入的新数据格式。
  • 云资源Controller已经创建的外部资源边界。

自动回滚本身也可能失败。生产流水线必须保留失败和回滚失败Runbook。

三十四、--cleanup-on-fail

Upgrade失败时清理本次新创建的某些资源,避免残留;它不等于把所有已修改资源恢复旧值,也不清理所有Hook/CRD/外部资源。

使用前理解与atomic的组合,不能把它当数据库回滚。

三十五、--force为什么危险

--force可能通过替换资源解决不可变字段Patch失败,代价可能是删除重建:

  • Service ClusterIP变化风险。
  • Pod中断。
  • Job重建。
  • 资源UID变化。
  • Stateful资源和Volume引用风险。

先设计兼容迁移或创建新资源,不把force作为日常升级选项。

三十六、Rollback全过程

bash
helm history order-api -n commerce
helm rollback order-api 11 -n commerce --wait --timeout 10m

Helm读取历史Revision中的Chart/Values/Manifest,生成新的Rollback Revision并把Kubernetes资源调整到历史期望。它不会把Revision数字倒退,历史仍继续增长。

Rollback不能撤销:

  • 数据库迁移。
  • 独立修改的Secret/ConfigMap(若不在历史Manifest或外部管理)。
  • Hook副作用。
  • 被手工修改/删除的外部资源。
  • CRD升级。

应用和数据必须支持新旧版本兼容。

三十七、Hook是什么

Hook通过Annotation声明:

yaml
metadata:
  annotations:
    helm.sh/hook: pre-upgrade
    helm.sh/hook-weight: "-10"
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded

常见阶段:pre/post-install、pre/post-upgrade、pre/post-rollback、pre/post-delete、test。

Hook资源不完全按普通Release资源生命周期管理。没有删除策略时可能残留,未来Helm垃圾回收行为也不能作为唯一保证。

三十八、Hook权重和顺序

同一阶段可按字符串表示的整数weight排序,再按Kind/名称等规则处理。不要让多个Hook隐式依赖默认顺序;显式权重,并让每个Hook检查前置条件。

Hook顺序仍不是跨系统事务顺序,Controller异步动作可能尚未完成。

三十九、数据库迁移Hook风险

pre-upgrade迁移可能:

text
DDL成功
→ 新Deployment发布失败
→ Helm自动Rollback旧应用
→ 旧应用无法读取新Schema

正确策略:

  • Expand:先增加向后兼容Schema。
  • Migrate:后台迁移数据并可重试。
  • Contract:所有旧版本退出后再删除旧字段。
  • 迁移版本表和数据库锁。
  • Hook Job幂等、Deadline和审计。
  • 失败时前滚/补偿,不假设Helm撤销DDL。

高风险数据库迁移可以从Chart Hook拆出为独立受控发布阶段。

四十、Hook删除策略

策略含义
before-hook-creation新Hook创建前删除上次同名资源
hook-succeeded成功后删除
hook-failed失败后删除,可能丢失现场

生产失败Hook通常需要保留日志和对象一段时间。若立即 hook-failed 删除,排障证据可能消失;日志必须集中采集。

四十一、CRD为什么放crds目录

crds/中的CRD在普通模板前安装,通常不进行Go Template渲染。Helm出于数据安全不会在Upgrade自动升级或在Uninstall删除CRD。

原因:删除CRD可能级联删除所有Custom Resource和业务数据;Schema升级也可能破坏现有对象。

CRD生命周期需要:

  • 独立版本管理。
  • Conversion/Webhook兼容。
  • Storage Version迁移。
  • 备份和回滚计划。
  • 与Controller版本矩阵。

不要把CRD当普通Deployment模板期待rollback自动恢复。

四十二、模板中的CR与CRD顺序

若Chart模板包含自定义资源,而CRD由 crds/安装,Install时API Discovery更新可能有传播边界。Upgrade不会自动升级CRD;新CR字段可能被旧CRD拒绝。

生产常把平台Operator/CRD Chart与业务CR Chart分离,按兼容矩阵升级。

四十三、依赖Chart

Chart.yaml:

yaml
dependencies:
  - name: common
    version: 1.6.0
    repository: oci://registry.example.com/helm
  - name: redis
    version: 19.6.0
    repository: https://charts.example.com
    condition: redis.enabled

命令:

bash
helm dependency update ./order-api
helm dependency build ./order-api
  • update按约束重新解析并更新Chart.lock。
  • build按Chart.lock恢复已锁定依赖。

生产CI优先使用锁文件和不可变制品,避免每次发布临时解析到新依赖。

四十四、Subchart Values和global

父Chart通过依赖名配置子Chart:

yaml
redis:
  enabled: true
  architecture: replication
global:
  imageRegistry: registry.example.com

子Chart不能直接读取父Chart任意Values,只能获得其命名范围和global等约定。global会扩大耦合,不应成为所有配置的垃圾桶。

依赖的condition、tags、alias可控制启用和多实例,必须在schema和文档中说明。

四十五、Library Chart

type: library的Chart提供可复用模板Helper,不直接安装资源。适合统一Label、SecurityContext和容器片段。

过度抽象会让最终YAML难以理解。平台Library升级要语义版本、兼容测试和渲染快照,不能一次修改让所有业务模板行为漂移。

四十六、helm lint能证明什么

bash
helm lint ./order-api -f values-prod.yaml --strict

可发现部分Chart元数据、模板和Schema问题。不能证明:

  • 目标集群API存在。
  • Admission允许。
  • 镜像可拉取。
  • Pod可调度。
  • Service/Ingress可访问。
  • 业务正常。

四十七、helm template

bash
helm template order-api ./order-api \
  -n commerce \
  -f values-prod.yaml \
  --kube-version 1.30.0 \
  --debug > rendered.yaml

输出可进入YAML解析、策略扫描、Secret扫描和快照测试。离线Capabilities可能与真实集群不同,仍需服务器校验。

四十八、Server Dry Run

根据Helm版本可使用服务端Dry Run能力,或把渲染结果交给:

bash
kubectl apply --dry-run=server -f rendered.yaml

它会经过目标API Schema、默认值和Admission,更接近真实提交。但不会创建资源,也不能验证调度、镜像、探针和业务。

包含Secret的Dry Run输出和Debug日志必须受保护,不要上传公开CI Artifact。

四十九、Diff和发布评审

Helm核心命令不一定内置完整Diff体验,社区常用helm-diff插件或GitOps平台差异视图。插件是额外供应链组件,需要固定版本和校验来源。

评审应关注:

  • 删除/替换资源。
  • Selector和不可变字段。
  • Service/Ingress/NetworkPolicy变化。
  • Secret引用。
  • Resource/Probe。
  • PVC/Reclaim。
  • RBAC权限扩大。
  • Hook/CRD。

五十、helm test

Chart test通常是带 helm.sh/hook: test 的Pod/Job:

bash
helm test order-api -n commerce --logs

适合验证Service DNS、最小接口和权限。它不自动成为Install/Upgrade阻断,流水线要显式执行并判断;测试必须幂等、不泄密、不产生不可控业务副作用。

五十一、Secret不能明文放Values

错误:

yaml
database:
  password: real-production-password

可能进入:

  • Git。
  • CI日志/Artifact。
  • Chart包。
  • Helm Release Secret历史。
  • helm get values输出。
  • Debug渲染文件。

正确方向:Chart只接受 existingSecret/ExternalSecret引用,凭据由专用系统管理。若必须在渲染阶段注入,确保CI输出、Release历史和RBAC风险经过评审。

五十二、OCI Chart和不可变Digest

bash
helm pull oci://registry.example.com/helm/order-api \
  --version 2.3.1

生产准入:

  • 固定Chart Version和OCI Digest。
  • Registry启用不可变Tag。
  • 依赖使用Chart.lock。
  • 扫描模板、镜像引用和恶意Hook。
  • 验证签名/Provenance或组织供应链证明。
  • 保存Chart Digest与发布Revision。

版本号相同但包被覆盖会破坏可复现发布。

五十三、Chart签名和Provenance

传统Repository可使用Provenance文件和 helm verify,OCI生态可结合Registry签名/供应链工具。签名证明某主体对特定包负责,不证明模板安全、镜像无漏洞或配置适合生产。

需要Key轮换、信任根、撤销和CI强制验证。

五十四、GitOps与Helm

Argo CD/Flux等可用Helm渲染Chart,再由GitOps Controller持续同步。此时:

  • Helm CLI Release Secret可能不是实际状态来源,取决于工具模式。
  • 回滚通常通过Git Commit/应用版本,不一定执行helm rollback。
  • Live Drift可能被自动纠正。
  • Hook支持语义与原生Helm不同。

必须明确“谁是Reconciler”和“谁保存Release历史”,不能把Helm CLI和GitOps行为混用。

五十五、Release状态

常见状态包括deployed、failed、pending-install、pending-upgrade、pending-rollback、uninstalled等。状态长期pending可能因为:

  • 上一次进程中断。
  • Hook/Wait仍未完成。
  • Release记录锁定在pending Revision。
  • API超时。

先查看history/status和集群资源,不直接删除最新Release Secret。

五十六、模板报nil pointer

错误示例:

text
nil pointer evaluating interface {}.enabled

原因:父Map不存在、with/range作用域变化、Values类型不符。修复:

  • values.schema规定结构。
  • 使用with/hasKey/dig等安全访问。
  • 不依赖多层隐式默认。
  • 为最小/生产Values做模板测试。

五十七、渲染成功但API拒绝

可能:

  • apiVersion在目标集群不存在。
  • 字段类型错误。
  • Admission策略拒绝。
  • Namespace/RBAC不足。
  • CRD未安装或版本不匹配。
  • 不可变字段更新。

使用Server Dry Run、Controller Event和API错误定位,不能只重复helm upgrade。

五十八、Upgrade成功但Pod失败

未使用wait时,Helm可能在API接收资源后返回成功,但Deployment后来ImagePullBackOff、Pending或CrashLoop。

检查:

bash
helm status order-api -n commerce
helm get manifest order-api -n commerce
kubectl get deployment,rs,pod -n commerce -l app.kubernetes.io/instance=order-api
kubectl describe pod <pod> -n commerce
kubectl logs <pod> -n commerce --previous --tail=300

流水线应使用有界wait、Rollout、Smoke和业务指标。

五十九、Upgrade卡在pending-upgrade

  1. 确认是否仍有Helm进程/流水线执行。
  2. 查看Hook Job和资源状态。
  3. 查看history/status最新Revision。
  4. 保存Release和资源证据。
  5. 根据实际状态rollback或修复,不并发再发Upgrade。

删除Release Secret是最后的受控修复,需要备份并理解历史结构;常规操作不要手工改Helm内部记录。

六十、Hook Job失败

bash
kubectl get job,pod -n commerce
kubectl describe job <hook-job> -n commerce
kubectl logs job/<hook-job> -n commerce --timestamps

检查Hook Annotation、Weight、DeletePolicy、镜像、RBAC、配置、Deadline和业务部分成功。重跑前确认幂等和数据库迁移位置。

六十一、Ownership冲突

错误常提示资源已存在且无法导入当前Release。先判断:

  • 由另一个Helm Release管理。
  • 人工创建。
  • Operator管理。
  • 上次失败残留。

选择:重命名、迁移所有权、从Chart排除或由平台统一管理。迁移前保存清单和回滚方案,不只补三个Label/Annotation。

六十二、Rollback失败

原因:

  • 历史API版本已被集群移除。
  • 旧镜像已从Registry删除。
  • 历史Secret/Config不存在。
  • 不可变字段无法Patch。
  • 数据库Schema不兼容。
  • Hook失败。
  • Admission策略已变化。

所以“有Helm历史”不等于“随时能回滚”。定期在预发演练旧Revision恢复,并保留镜像和依赖。

六十三、Uninstall边界

bash
helm uninstall order-api -n commerce --keep-history

通常删除Release管理的普通资源,但可能保留:

  • CRD及其Custom Resource。
  • helm.sh/resource-policy: keep 的资源。
  • Hook资源。
  • PVC/底层卷,取决于对象和策略。
  • Controller创建的外部LB/云资源在异步删除中。
  • 数据库和外部副作用。

生产卸载前先列清数据和外部资源,不能把uninstall当无害清理命令。

六十四、生产发布Pipeline

mermaid
flowchart TD
    A["固定Chart/依赖/镜像Digest"] --> B["lint、Schema、单元与渲染快照"]
    B --> C["策略/Secret/漏洞扫描"]
    C --> D["目标集群Server Dry Run"]
    D --> E["Diff和人工/自动准入"]
    E --> F["helm upgrade --install到金丝雀环境/分组"]
    F --> G["wait、test、Smoke和版本指标"]
    G --> H{"是否满足发布闸门"}
    H -- "否" --> I["停止扩散,回滚或前滚并处理副作用"]
    H -- "是" --> J["分批生产发布"]
    J --> K["保存Chart Digest、Values版本和Release Revision"]

六十五、生产命令示例

bash
helm upgrade --install order-api \
  oci://registry.example.com/helm/order-api \
  --version 2.3.1 \
  --namespace commerce \
  --create-namespace \
  --values values-prod.yaml \
  --set-string image.digest='sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' \
  --wait \
  --wait-for-jobs \
  --timeout 10m \
  --history-max 20

是否使用atomic取决于Hook/数据库/外部副作用的回滚设计,不能默认加上就认为绝对安全。

六十六、监控和审计

  • Release Revision、状态和操作耗时。
  • Chart Version/Digest、appVersion、镜像Digest。
  • 最终Values版本(脱敏)。
  • Hook/Job状态。
  • Controller Rollout和业务指标。
  • Helm API错误、RBAC和Admission拒绝。
  • 失败/回滚失败率。
  • Release Secret数量和etcd空间。
  • 操作者、流水线、变更单。

不要把完整Secret Values写入审计日志。

六十七、常见误区

  1. Helm是另一套K8s Controller:它主要渲染和提交,资源由K8s Controller维护。
  2. appVersion自动决定镜像:模板必须显式引用。
  3. List会智能合并:Values中的List通常整体替换。
  4. helm lint通过就能上线:不覆盖目标API和运行时。
  5. --wait证明业务成功:仍需Smoke和指标。
  6. --atomic是事务:无法撤销数据库/Hook/外部副作用。
  7. rollback把Revision倒退:它创建新的回滚Revision。
  8. CRD会随upgrade/rollback自动处理:Helm刻意保守。
  9. Secret放Values只存在内存:可能进入Release历史和Artifact。
  10. 手工改资源不会影响Helm:会形成Drift和未来Patch差异。
  11. --force只是强制覆盖:可能删除重建资源。
  12. 有历史就一定可回滚:旧API/镜像/数据Schema可能已不兼容。

六十八、面试标准回答

Helm Install全过程

Helm客户端加载Chart和依赖,按优先级合并Values并做Schema校验,构造Release/Capabilities上下文执行Go Template;执行pre-install Hook后把普通资源提交API Server,可选wait,再执行post-install,并把Chart、Values、Manifest、Hook和状态作为Revision保存在Release Namespace的Secret中。后续资源由Kubernetes Controller持续Reconcile。

Values优先级和合并规则

默认values最低,父Chart覆盖依赖,其后多个-f按顺序后者覆盖,--set类最高;Map通常递归合并,List通常整体替换。--set会推断类型,标识/Digest用--set-string。Upgrade复用旧Values可能让新Chart安全默认不生效,生产应从版本化输入重建最终值。

--wait和--atomic区别

wait在timeout内等待部分Kubernetes资源Ready/Complete,但不证明DNS、Ingress和业务成功;atomic在Install失败时卸载或Upgrade失败时尝试回滚,并通常启用wait。atomic不能撤销数据库迁移、Hook外部调用、消息、CRD和新数据格式,也可能回滚失败。

Helm Rollback能回滚什么

它读取历史Revision的Chart/Values/Manifest,把Kubernetes资源调整回历史期望,并生成新的Revision;不能撤销数据库DDL/DML、Hook副作用、外部Secret、CRD Schema和应用已写数据。旧API、镜像或Schema失效时Rollback也会失败。

Helm为什么不自动升级/删除CRD

CRD定义所有Custom Resource的Schema和生命周期,错误升级会拒绝/破坏对象,删除可能级联删除业务数据。Helm对crds目录采用保守策略,Install先创建,但Upgrade/Uninstall不自动管理完整生命周期,需独立版本、转换和迁移Runbook。

Release为什么有Secret安全风险

Helm 3默认把每个Revision的Chart、Values和渲染Manifest编码保存在Namespace Secret;如果密码明文放Values或渲染进Secret,历史Revision可能长期保留它。必须限制RBAC、脱敏CI输出、引用外部/已有Secret并控制history-max。

pending-upgrade怎样处理

先确认是否仍有发布进程,查看status/history、Hook Job、资源和API事件,保留Release证据;根据实际状态修复或rollback并加Release级发布锁。不要先删除最新Release Secret,手工改内部记录会造成资源和历史孤儿。

六十九、学习实验与验收

实验一:Values合并

设计嵌套Map和List,依次使用两个-f和--set-string,输出最终Values/Manifest,证明Map合并、List替换和类型推断。

实验二:作用域与缩进

在with/range中故意错误使用.复现nil pointer;修复为$根作用域,并用toYaml+nindent生成可解析YAML。

实验三:Install/Release Secret

在隔离Namespace安装测试Chart,查看history/status/get manifest和Release Secret元数据,不解码生产内容;升级两次观察Revision。

实验四:wait与atomic

制造Readiness失败,比较无wait、wait和atomic的命令结果、资源和Revision;说明自动回滚不能撤销测试Hook写入的外部记录。

实验五:Hook幂等

创建测试迁移Job,在“写入成功但Job失败”后重试,先复现重复,再用迁移版本表/唯一键修复。

实验六:CRD边界

在隔离集群安装简单CRD/CR,观察upgrade/uninstall行为并设计独立CRD升级步骤,不在生产执行删除实验。

实验七:Drift和Rollback

手工修改测试Deployment字段,执行Upgrade/Rollback,比较旧Manifest、Live、新Manifest,记录哪些字段保留/覆盖及原因。

验收清单

  1. 能画出Helm 3渲染、API提交和Release存储。
  2. 能解释Chart/app/Release/Revision四类版本。
  3. 能手算Values优先级、Map/List和类型结果。
  4. 能正确使用作用域、include、nindent、required和fail。
  5. 能解释tpl/lookup的安全与可复现风险。
  6. 能解释Install、Upgrade和三方Merge思想。
  7. 能区分wait、atomic、cleanup和force。
  8. 能设计幂等Hook和兼容数据库迁移。
  9. 能解释CRD特殊生命周期。
  10. 能锁定依赖、OCI Digest和签名供应链。
  11. 能排查pending、ownership、Hook和Rollback失败。
  12. 能构建lint→render→scan→dry-run→diff→smoke→metrics发布链。

关联知识点