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主要恢复历史清单,不能撤销数据库、消息和外部副作用。
学习目标
学完后应能:
- 解释Helm 3客户端、Chart、Release和Kubernetes API的关系。
- 说明Chart.yaml、values.yaml、templates、crds、charts和schema职责。
- 准确解释多Values文件、
--set和Upgrade复用值的优先级。 - 解释Map合并、List替换、YAML类型和字符串陷阱。
- 使用
.Values/.Release/.Chart/.Capabilities/.Files和作用域变量。 - 正确使用
include、nindent、toYaml、required、fail和tpl。 - 解释Install/Upgrade的渲染、Hook、API提交、Wait和Release记录全过程。
- 解释Helm 3 Release为何保存为Namespace内Secret及其安全风险。
- 区分
--wait、--wait-for-jobs、--atomic、--cleanup-on-fail和--force。 - 解释三方Merge思想、手工漂移和资源所有权冲突。
- 解释Hook执行、删除策略、幂等和数据库迁移边界。
- 解释CRD安装、升级、删除为什么不能当普通模板处理。
- 设计OCI、Digest、依赖锁、签名、Schema和供应链准入。
- 排查模板错误、Pending Release、Hook失败、Ownership和Rollback失败。
一、Helm 3架构
Helm 2曾依赖集群内Tiller,Helm 3取消Tiller。常见执行链:
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 Version | Chart包版本 | version: 2.3.1 |
| appVersion | 应用展示版本,不自动控制镜像 | appVersion: 1.18.4 |
| Release | 某Namespace中一次命名安装 | order-api-prod |
| Revision | Release每次成功/失败操作产生的历史版本 | 1、2、3 |
同一个Chart可以安装为多个Release:
Chart order-api 2.3.1
├── Release order-api-dev / namespace dev
├── Release order-api-staging / namespace staging
└── Release order-api-prod / namespace prodChart Version、appVersion、镜像Tag/Digest和Release Revision是四个不同概念,发布记录应全部保存。
三、标准Chart目录
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.json | Values类型、枚举、范围和必填校验 |
templates/ | Go Template渲染的Kubernetes清单 |
_helpers.tpl | 命名、Label等可复用命名模板,不直接输出对象 |
charts/ | 下载/打包后的依赖Chart |
Chart.lock | 锁定解析后的依赖版本和Digest |
crds/ | 安装CRD定义的特殊目录,不走普通模板生命周期 |
NOTES.txt | 安装后给操作者的说明,不能泄露Secret |
四、Chart.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/helm4.1 version
Chart包自身的SemVer。模板、默认值或发布行为发生破坏性变化时应升级Major,不要覆盖已发布 2.3.1 包。
4.2 appVersion
只是应用版本元数据,Helm不会自动把它写入镜像。模板必须显式引用:
image: "{{ .Values.image.repository }}@{{ .Values.image.digest }}"4.3 kubeVersion
可声明支持的Kubernetes版本范围,在渲染/安装时提前拒绝明显不兼容集群。但它不能证明目标CNI、CSI、Ingress Controller和Admission Policy兼容。
五、Values合并优先级
常见优先级从低到高:
Chart values.yaml
→ 父Chart为依赖提供的值
→ 第一个 -f values文件
→ 后续 -f values文件(后者覆盖前者)
→ --set/--set-string/--set-file/--set-json例如:
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...'最终值不是任意一个文件,而是完整合并结果。发布前应保存:
helm template order-api ./order-api \
-n commerce \
-f values-prod.yaml \
-f values-prod-region-a.yaml的最终渲染产物和非敏感有效Values。
六、Map合并与List替换
Maps通常按Key递归合并:
# values.yaml
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi# values-prod.yaml
resources:
requests:
cpu: 500m最终通常保留memory和limits,只覆盖cpu。
Lists通常整体替换,不按元素智能合并:
# defaults
tolerations:
- key: dedicated
value: backend
# prod
tolerations:
- key: zone
value: a最终可能只有prod这一项,默认项消失。复杂可扩展配置优先设计为Map或提供清晰完整List覆盖契约。
七、YAML类型陷阱
以下值容易被解析为不同类型:
featureEnabled: false
replicas: 3
port: 8080
version: "00123"
memory: 512Mi命令行:
--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;升级前比较:
helm get values order-api -n commerce --all
helm show values <new-chart>具体reset/reuse默认行为和组合Flag随Helm版本确认。
九、values.schema.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 |
.Release | Release名称、Namespace、Revision、是否升级等 |
.Chart | Chart.yaml元数据 |
.Capabilities | 目标Kubernetes版本和可用API能力 |
.Template | 当前模板信息 |
.Files | Chart内非template文件 |
示例:
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提交。缩进和空白错误会产生无效清单:
resources:
{{ toYaml .Values.resources | nindent 10 }}nindent 10先换行再缩进10空格。缩进数字必须与模板位置匹配,不能盲抄。
使用:
helm template order-api ./chart --debug查看最终文本,而不是只读模板猜测。
十二、if、with、range的作用域
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}with内部的 . 变成 podAnnotations,访问根上下文要保存 $:
{{- 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:
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对空值的定义可能把 false、0、空字符串视为空:
{{ 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拼在一行:
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设计
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模板关键片段
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加载与渲染全过程
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全过程
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,名称通常类似:
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”。先:
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全过程
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和集群不一致。
使用:
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识别资源归属,例如:
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
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全过程
helm history order-api -n commerce
helm rollback order-api 11 -n commerce --wait --timeout 10mHelm读取历史Revision中的Chart/Values/Manifest,生成新的Rollback Revision并把Kubernetes资源调整到历史期望。它不会把Revision数字倒退,历史仍继续增长。
Rollback不能撤销:
- 数据库迁移。
- 独立修改的Secret/ConfigMap(若不在历史Manifest或外部管理)。
- Hook副作用。
- 被手工修改/删除的外部资源。
- CRD升级。
应用和数据必须支持新旧版本兼容。
三十七、Hook是什么
Hook通过Annotation声明:
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迁移可能:
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:
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命令:
helm dependency update ./order-api
helm dependency build ./order-api- update按约束重新解析并更新Chart.lock。
- build按Chart.lock恢复已锁定依赖。
生产CI优先使用锁文件和不可变制品,避免每次发布临时解析到新依赖。
四十四、Subchart Values和global
父Chart通过依赖名配置子Chart:
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能证明什么
helm lint ./order-api -f values-prod.yaml --strict可发现部分Chart元数据、模板和Schema问题。不能证明:
- 目标集群API存在。
- Admission允许。
- 镜像可拉取。
- Pod可调度。
- Service/Ingress可访问。
- 业务正常。
四十七、helm template
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能力,或把渲染结果交给:
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:
helm test order-api -n commerce --logs适合验证Service DNS、最小接口和权限。它不自动成为Install/Upgrade阻断,流水线要显式执行并判断;测试必须幂等、不泄密、不产生不可控业务副作用。
五十一、Secret不能明文放Values
错误:
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
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
错误示例:
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。
检查:
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
- 确认是否仍有Helm进程/流水线执行。
- 查看Hook Job和资源状态。
- 查看history/status最新Revision。
- 保存Release和资源证据。
- 根据实际状态rollback或修复,不并发再发Upgrade。
删除Release Secret是最后的受控修复,需要备份并理解历史结构;常规操作不要手工改Helm内部记录。
六十、Hook Job失败
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边界
helm uninstall order-api -n commerce --keep-history通常删除Release管理的普通资源,但可能保留:
- CRD及其Custom Resource。
- 带
helm.sh/resource-policy: keep的资源。 - Hook资源。
- PVC/底层卷,取决于对象和策略。
- Controller创建的外部LB/云资源在异步删除中。
- 数据库和外部副作用。
生产卸载前先列清数据和外部资源,不能把uninstall当无害清理命令。
六十四、生产发布Pipeline
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"]六十五、生产命令示例
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写入审计日志。
六十七、常见误区
- Helm是另一套K8s Controller:它主要渲染和提交,资源由K8s Controller维护。
- appVersion自动决定镜像:模板必须显式引用。
- List会智能合并:Values中的List通常整体替换。
helm lint通过就能上线:不覆盖目标API和运行时。--wait证明业务成功:仍需Smoke和指标。--atomic是事务:无法撤销数据库/Hook/外部副作用。rollback把Revision倒退:它创建新的回滚Revision。- CRD会随upgrade/rollback自动处理:Helm刻意保守。
- Secret放Values只存在内存:可能进入Release历史和Artifact。
- 手工改资源不会影响Helm:会形成Drift和未来Patch差异。
--force只是强制覆盖:可能删除重建资源。- 有历史就一定可回滚:旧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,记录哪些字段保留/覆盖及原因。
验收清单
- 能画出Helm 3渲染、API提交和Release存储。
- 能解释Chart/app/Release/Revision四类版本。
- 能手算Values优先级、Map/List和类型结果。
- 能正确使用作用域、include、nindent、required和fail。
- 能解释tpl/lookup的安全与可复现风险。
- 能解释Install、Upgrade和三方Merge思想。
- 能区分wait、atomic、cleanup和force。
- 能设计幂等Hook和兼容数据库迁移。
- 能解释CRD特殊生命周期。
- 能锁定依赖、OCI Digest和签名供应链。
- 能排查pending、ownership、Hook和Rollback失败。
- 能构建lint→render→scan→dry-run→diff→smoke→metrics发布链。
