前言
Helm 是 Kubernetes 的包管理器,就像 apt/yum 之于 Linux、npm 之于 Node.js。它把一组 K8s 资源打包成一个 Chart,让部署、升级、回滚变得简单可控。
本文从零开始,通过一系列前后关联的实操任务,带你循序渐进掌握 Helm 的核心用法。建议打开终端跟着敲。
前置条件:一个可用的 Kubernetes 集群(minikube / kind / k3s 均可),已安装 kubectl。
一、基础篇:认识 Helm
1.1 安装 Helm
1 2 3 4 5 6 7 8
| brew install helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
choco install kubernetes-helm
|
验证安装:
1.2 搭建本地 Chart 仓库
由于国内访问 Bitnami 等国外仓库很慢,我们从零搭建一个本地 Chart 仓库,同时也理解仓库的底层原理。
一个 Helm 仓库本质上就是一个 HTTP 服务器 + index.yaml 索引文件 + 打包好的 .tgz 文件。
第一步:创建目录结构
1 2
| mkdir -p ~/helm-repo/charts cd ~/helm-repo
|
第二步:快速准备一个 Chart
先用 helm create 生成一个脚手架 Chart 作为仓库的第一个”货物”:
这会生成一个标准的 Chart 目录。我们稍作修改,把镜像换成国内可用的:
1 2 3 4 5 6 7 8 9 10
| sed -i 's|repository: ddn-k8s/docker.io/nginx|repository: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx |' my-nginx/values.yaml sed -i 's/tag: ""/tag: "stable"/' my-nginx/values.yaml
sed '/^[[:space:]]*#/d' my-nginx/values.yaml | grep -A3 'image:'
|
镜像地址是 swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:stable。Chart 的 values.yaml 里,registry + repository + tag 三段组合成完整镜像路径。
第三步:打包并生成仓库索引
1 2 3 4 5 6 7 8 9 10
| helm package my-nginx -d charts/
helm repo index charts/ --url http://127.0.0.1:8879/charts
cat charts/index.yaml
|
第四步:启动本地仓库服务
1 2 3
| cd ~/helm-repo python3 -m http.server 8879
|
第五步:添加本地仓库并用它安装
1 2 3 4 5 6 7 8 9
| helm repo add local http://127.0.0.1:8879/charts
helm repo list
helm search repo local/
|
仓库原理小结:helm repo add 只是记下了一个 URL,helm search 去那个 URL 下载 index.yaml 进行检索,helm install 则下载对应的 .tgz 包并部署到集群。
1.3 第一个 Helm 部署:安装本地 Chart
1 2 3 4 5 6 7 8
| helm install my-nginx local/my-nginx
helm list
kubectl get all -l app.kubernetes.io/instance=my-nginx
|
访问验证:
1 2 3 4 5
| kubectl port-forward svc/my-nginx 8080:80
curl http://localhost:8080
|
1.4 升级、回滚与卸载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| helm history my-nginx
helm upgrade my-nginx local/my-nginx --set replicaCount=3
helm history my-nginx
helm rollback my-nginx 1
helm uninstall my-nginx
helm uninstall my-nginx -n dev
helm uninstall my-nginx --keep-history
helm uninstall my-nginx --dry-run
helm list --uninstalled
|
helm uninstall 会删除 Release 关联的所有 K8s 资源(Deployment、Service、ConfigMap 等)。--keep-history 保留历史 Secret 以便恢复,--dry-run 预览要删的资源。如果只是想暂时停掉服务,建议 scale replicas 为 0 而不是直接 uninstall。
理解关键概念:
- Chart:应用包(模板 + 默认值)
- Release:Chart 在集群中的一个部署实例
- Repository:Chart 的存储和分发仓库
- Revision:每次 upgrade/rollback 产生的新版本号
二、Chart 结构篇:解剖一个 Chart
2.1 创建一个空白 Chart
1 2
| helm create demo-chart tree demo-chart
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| tree demo-chart/ ├── charts # 依赖的子Chart ├── Chart.yaml # Chart元信息 ├── templates # 模板文件 │ ├── deployment.yaml │ ├── _helpers.tpl # 模板辅助函数 │ ├── hpa.yaml │ ├── httproute.yaml │ ├── ingress.yaml │ ├── NOTES.txt # 安装后提示信息 │ ├── serviceaccount.yaml │ ├── service.yaml │ └── tests │ └── test-connection.yaml └── values.yaml # 默认配置值
|
2.2 Chart.yaml — Chart 的身份证
1 2 3 4 5 6 7 8
| cd demo-chart awk '!/^[[:space:]]*#/ && NF' Chart.yaml apiVersion: v2 name: demo-chart description: A Helm chart for Kubernetes type: application version: 0.1.0 appVersion: "1.16.0"
|
关键字段都在这,version 是 Chart 版本号,每次打包发布时递增。
2.3 values.yaml — 配置的入口
这是 Helm 的核心设计:配置与模板分离。所有可变参数都定义在 values.yaml 中,模板文件引用这些值。
1 2 3 4 5 6 7 8 9
| replicaCount: 1
image: repository: nginx tag: ""
service: type: ClusterIP port: 80
|
2.4 _helpers.tpl — 模板复用
_helpers.tpl 是用来存放可复用的模板片段的文件,类似于编程中的公共函数。Helm 不会把它当作 K8s 资源去渲染,只在 include 调用时才执行。
来看一段最典型的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| {% raw %}{{/* Create a default fully qualified app name. */}} {{- define "demo-chart.fullname" -}} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- end }}
{{/* Common labels */}} {{- define "demo-chart.labels" -}} app.kubernetes.io/name: {{ include "demo-chart.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }}{% endraw %}
|
逐行拆解:
| 语法 |
含义 |
define "xxx" … end |
定义一个名为 xxx 的可复用片段 |
include "xxx" . |
调用定义好的片段,. 表示把当前上下文传进去 |
{{- 和 -}} |
吃掉模板输出前后的空白,避免渲染出多余空行 |
{{/* ... */}} |
模板注释,渲染后不产出任何内容 |
trunc 63 和 trimSuffix "-" 解释:
Kubernetes 资源名称最长 63 个字符。trunc 63 就是把 Release 名截断到 63 字符以内。但截断后末尾可能是 -,而 K8s 不允许名称以 - 结尾 — trimSuffix "-" 负责把末尾的 - 去掉。
实战示例:假设 helm install my-release-with-a-very-long-name-that-exceeds-63-characters-total,模板会把名字截断为合法的 K8s 资源名。
第二个 define 的效果: 假设你的 Release 叫 my-blog,渲染后输出:
1 2
| app.kubernetes.io/name: demo-chart app.kubernetes.io/instance: my-blog
|
这些是 Kubernetes 推荐的标准标签,用于标识资源归属,kubectl get all -l app.kubernetes.io/instance=my-blog 就能精确筛选出这个 Release 创建的所有资源。
2.5 内置对象速查
模板中可用的关键对象:
| 对象 |
含义 |
示例 |
{{ .Release.Name }} |
Release 名称 |
my-release |
{{ .Release.Namespace }} |
命名空间 |
default |
{{ .Chart.Name }} |
Chart 名称 |
demo-chart |
{{ .Chart.Version }} |
Chart 版本 |
0.1.0 |
{{ .Values.replicaCount }} |
values 中的值 |
1 |
{{ .Files.Get "config.json" }} |
读取 Chart 内文件 |
文件内容 |
{{ .Capabilities.KubeVersion }} |
K8s 版本信息 |
v1.30 |
2.6 流程控制:if / range / with
if 条件判断:
1 2 3 4 5 6
| {% raw %}{{- if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "demo-chart.serviceAccountName" . }} {{- end }}{% endraw %}
|
{{- if .Values.serviceAccount.create }} 的含义是:读取 values.yaml 中 serviceAccount.create 的值,如果为 true,就渲染这个 ServiceAccount 资源;如果为 false,整段跳过不生成。
对应的 values.yaml 中应该有:
1 2 3
| serviceAccount: create: true name: ""
|
为什么需要这个? 不是所有场景都需要独立的 ServiceAccount。Helm 用 if 开关让同一个 Chart 适配不同环境 — 开发环境可以关掉,生产环境打开。这就是”配置驱动”的核心思想。
range 循环遍历:
1 2 3 4 5
| {% raw %}env: {{- range .Values.env }} - name: {{ .name }} value: {{ .value }} {{- end }}{% endraw %}
|
对应的 values:
1 2 3 4 5
| env: - name: DB_HOST value: mysql.default.svc - name: DB_PORT value: "3306"
|
with 切换作用域:
1 2 3 4
| {% raw %}{{- with .Values.resources }} resources: {{- toYaml . | nindent 2 }} {{- end }}{% endraw %}
|
with 将当前作用域切换到指定对象,内部直接 . 即可访问。
2.7 管道与函数
管道 | 将前一个输出作为最后一个参数传给下一个函数:
1
| {% raw %}image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"{% endraw %}
|
常用函数:
| 函数 |
用途 |
示例 |
quote |
加引号 |
{{ .Values.name | quote }} |
upper / lower |
大小写转换 |
{{ .Values.env | upper }} |
default |
默认值 |
{{ .Values.port | default 8080 }} |
indent / nindent |
缩进 |
{{ toYaml . | nindent 2 }} |
toYaml / toJson |
格式转换 |
{{ .Values.config | toYaml }} |
required |
必填校验 |
{{ required "image is required" .Values.image }} |
三、实操篇:手写 nginx Chart
学完基础,现在从零写一个 nginx Chart,不依赖 helm create。
3.1 创建 Chart 目录结构
1 2
| mkdir -p nginx-chart/templates cd nginx-chart
|
3.2 Chart.yaml
1 2 3 4 5 6
| apiVersion: v2 name: nginx-chart description: A simple nginx chart for learning Helm type: application version: 0.1.0 appVersion: "1.25"
|
3.3 values.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| replicaCount: 1
image: registry: swr.cn-north-4.myhuaweicloud.com repository: ddn-k8s/docker.io/nginx tag: "stable" pullPolicy: IfNotPresent
service: type: ClusterIP port: 80
ingress: enabled: false host: nginx.local
resources: limits: cpu: 200m memory: 256Mi requests: cpu: 100m memory: 128Mi
nginxConf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } location /health { return 200 'OK'; } }
|
设计思路:这里故意加了一个 nginxConf 参数,后面会用它演示 ConfigMap 的用法。
3.4 _helpers.tpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| {% raw %}{{- define "nginx-chart.name" -}} {{- .Chart.Name | trunc 63 | trimSuffix "-" }} {{- end }}
{{- define "nginx-chart.labels" -}} app.kubernetes.io/name: {{ include "nginx-chart.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion }} helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} {{- end }}
{{- define "nginx-chart.selectorLabels" -}} app.kubernetes.io/name: {{ include "nginx-chart.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }}{% endraw %}
|
3.5 templates/configmap.yaml
先创建 ConfigMap 来存储 nginx 配置:
1 2 3 4 5 6 7 8 9
| {% raw %}apiVersion: v1 kind: ConfigMap metadata: name: {{ include "nginx-chart.name" . }}-config labels: {{- include "nginx-chart.labels" . | nindent 4 }} data: default.conf: | {{- .Values.nginxConf | nindent 4 }}{% endraw %}
|
nindent 4 让整个 nginxConf 内容相对于 default.conf: | 缩进 4 个空格。
3.6 templates/deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| {% raw %}apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "nginx-chart.name" . }} labels: {{- include "nginx-chart.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "nginx-chart.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "nginx-chart.selectorLabels" . | nindent 8 }} spec: containers: - name: nginx image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 80 {{- with .Values.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d volumes: - name: nginx-config configMap: name: {{ include "nginx-chart.name" . }}-config{% endraw %}
|
关键点:注意到 Deployment 通过 ConfigMap Volume 挂载了 nginx 配置。values.yaml 中的 nginxConf 最终注入到了 Pod 的 /etc/nginx/conf.d/default.conf。
3.7 templates/service.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13
| {% raw %}apiVersion: v1 kind: Service metadata: name: {{ include "nginx-chart.name" . }} labels: {{- include "nginx-chart.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: http selector: {{- include "nginx-chart.selectorLabels" . | nindent 4 }}{% endraw %}
|
3.8 templates/ingress.yaml(可选)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| {% raw %}{{- if .Values.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "nginx-chart.name" . }} labels: {{- include "nginx-chart.labels" . | nindent 4 }} spec: rules: - host: {{ .Values.ingress.host }} http: paths: - path: / pathType: Prefix backend: service: name: {{ include "nginx-chart.name" . }} port: number: {{ .Values.service.port }} {{- end }}{% endraw %}
|
3.9 安装并验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| helm install nginx-demo ./nginx-chart
helm get manifest nginx-demo | head -40
helm get values nginx-demo
kubectl port-forward svc/nginx-chart 8080:80 curl http://localhost:8080/health
helm upgrade nginx-demo ./nginx-chart --set replicaCount=3 kubectl get pods -l app.kubernetes.io/instance=nginx-demo
|
四、多环境篇:dev 与 prod 环境区分
同一套 Chart,不同环境用不同的 values。这是 Helm 在生产中最常用的模式。
4.1 创建环境 values 文件
1 2 3 4 5
| nginx-chart/ ├── values.yaml # 公共默认值 ├── values-dev.yaml # 开发环境覆盖 ├── values-prod.yaml # 生产环境覆盖 └── ...
|
4.2 values-dev.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| replicaCount: 1
image: tag: "stable"
service: type: NodePort
ingress: enabled: true host: nginx-dev.local
resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi
nginxConf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; add_header X-Environment "DEV"; } }
|
4.3 values-prod.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| replicaCount: 3
image: tag: "stable"
service: type: ClusterIP
ingress: enabled: true host: nginx.liuxu.site
resources: limits: cpu: 500m memory: 512Mi requests: cpu: 200m memory: 256Mi
nginxConf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; add_header X-Environment "PRODUCTION"; } }
|
4.4 安装到不同命名空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| kubectl create ns dev kubectl create ns prod
helm install nginx-demo ./nginx-chart \ -f values-dev.yaml \ -n dev
helm install nginx-prod ./nginx-chart \ -f values-prod.yaml \ -n prod
kubectl -n dev get pods kubectl -n dev port-forward svc/nginx-chart 8081:80 & curl -I http://localhost:8081 2>&1 | grep X-Environment
kubectl -n prod get pods kubectl -n prod port-forward svc/nginx-chart 8082:80 & curl -I http://localhost:8082 2>&1 | grep X-Environment
|
values 合并规则:Helm 按顺序合并 — 后面的覆盖前面的。默认 values.yaml → -f 指定的文件 → --set 命令行。更深层的合并是 merge 而非 replace,所以 resources 中只改了一部分值也是安全的。
五、进阶篇:helm-diff 发布前预览
升级前想知道到底变了什么?helm-diff 插件解决这个问题。
5.1 安装插件
1
| helm plugin install https://github.com/databus23/helm-diff
|
5.2 预览变更
1 2 3 4 5 6
|
helm diff upgrade nginx-demo ./nginx-chart \ -f values-dev.yaml \ -n dev
|
输出类似:
1 2 3 4 5 6
| default, nginx-chart, Deployment (apps) has changed: # Source: nginx-chart/templates/deployment.yaml ... - replicas: 1 + replicas: 2 ...
|
用颜色高亮增删改的行,一目了然。
5.3 常用命令
1 2 3 4 5 6 7 8
| helm diff upgrade <release> <chart> -f values.yaml
helm diff release <release-a> <release-b>
helm diff revision <release> <rev1> <rev2>
|
最佳实践:CI/CD 流水线中在 helm upgrade 前先跑 helm diff,输出 diff 到 PR comment,审核通过后再执行真正的升级。
六、安全篇:helm-secrets + sops 加密敏感配置
生产环境必然涉及密码、Token、证书等敏感信息。绝不能把明文密钥提交到 Git!
6.1 安装工具
1 2 3 4 5 6 7 8 9 10 11
|
brew install sops
curl -LO https://github.com/getsops/sops/releases/download/v3.10.2/sops-v3.10.2.linux.amd64 sudo mv sops-v3.10.2.linux.amd64 /usr/local/bin/sops sudo chmod +x /usr/local/bin/sops
helm plugin install https://github.com/jkroepke/helm-secrets
|
6.2 生成 PGP 密钥(演示用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| gpg --batch --gen-key <<EOF Key-Type: RSA Key-Length: 4096 Name-Real: Helm Secrets Demo Name-Email: helm@liuxu.site Expire-Date: 1y %no-protection %commit EOF
gpg --list-secret-keys --keyid-format LONG | grep sec
export SOPS_PGP_FP="XXXXXXXX"
|
生产环境建议使用云 KMS(如 AWS KMS / GCP KMS),而不是本地 PGP 密钥。
6.3 创建加密的 secrets 文件
先准备一个包含敏感值的文件,不要直接写入 values.yaml:
1 2 3 4 5 6 7
| db: password: "MyS3cret!Dev" apiKey: "sk-dev-abc123"
redis: password: "RedisP@ss"
|
用 sops 加密:
1 2 3 4 5 6 7 8
| sops --encrypt \ --pgp $SOPS_PGP_FP \ --encrypted-regex '^(password|apiKey|secret)$' \ secrets-dev.yaml > secrets-dev.yaml.enc
cat secrets-dev.yaml.enc
|
加密后的内容类似:
1 2 3 4 5 6 7 8 9 10 11 12 13
| db: password: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str] apiKey: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str] redis: password: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str] sops: pgp: - created_at: "2026-07-13T13:00:00Z" enc: |- -----BEGIN PGP MESSAGE----- ... -----END PGP MESSAGE----- ...
|
.gitignore 中保留原始明文文件,只把 .enc 加密文件提交 Git:
1 2 3
| # .gitignore secrets-*.yaml !secrets-*.yaml.enc
|
6.4 安装时使用加密文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| helm secrets install nginx-demo ./nginx-chart \ -f values-dev.yaml \ -f secrets-dev.yaml.enc \ -n dev
helm secrets upgrade nginx-demo ./nginx-chart \ -f values-dev.yaml \ -f secrets-dev.yaml.enc \ -n dev
helm secrets diff upgrade nginx-demo ./nginx-chart \ -f values-dev.yaml \ -f secrets-dev.yaml.enc \ -n dev
|
6.5 在模板中使用敏感值
修改 templates/deployment.yaml,添加环境变量:
1 2 3 4 5 6 7 8 9
| {% raw %} env: {{- if .Values.db }} - name: DB_PASSWORD value: {{ .Values.db.password | quote }} {{- end }} {{- if .Values.redis }} - name: REDIS_PASSWORD value: {{ .Values.redis.password | quote }} {{- end }}{% endraw %}
|
更推荐使用 Secret 资源 + envFrom 的方式注入敏感值,但从 helm-secrets 解密后通过 values 注入已能满足多数场景。
七、私有仓库篇:Harbor Chart 仓库
团队协作需要私有 Chart 仓库。Harbor 提供了 OCI 兼容的 Helm Chart 存储。
7.1 配置 Harbor(假设已有 Harbor 实例)
登录 Harbor(示例地址 harbor.liuxu.site):
1 2 3
| helm registry login harbor.liuxu.site \ --username admin \ --password-stdin
|
7.2 打包 Chart
1 2 3
| helm package ./nginx-chart
|
7.3 推送到 Harbor
1 2 3 4 5 6
| helm push nginx-chart-0.1.0.tgz oci://harbor.liuxu.site/helm-charts
|
7.4 拉取并使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| helm pull oci://harbor.liuxu.site/helm-charts/nginx-chart \ --version 0.1.0
helm install my-nginx oci://harbor.liuxu.site/helm-charts/nginx-chart \ --version 0.1.0 \ -n prod
helm repo add harbor https://harbor.liuxu.site/chartrepo/helm-charts helm repo update helm install my-nginx harbor/nginx-chart -n prod
|
7.5 CI/CD 流水线集成
典型 GitLab CI 片段:
1 2 3 4 5 6 7 8 9 10
| helm-push: stage: deploy image: alpine/helm:3.17 script: - helm registry login $HARBOR_URL -u $HARBOR_USER -p $HARBOR_PASS - helm package ./nginx-chart - helm push nginx-chart-*.tgz oci://$HARBOR_URL/helm-charts only: - tags
|
八、生产排障:常见问题速查
8.1 helm install 报 Error: INSTALLATION FAILED
现象:Error: INSTALLATION FAILED: Unable to continue with install: ...
1 2 3 4 5 6
| helm list -A | grep <name>
helm uninstall <name> -n <ns> helm install <new-name> <chart> -n <ns>
|
8.2 Error: UPGRADE FAILED: another operation is in progress
1 2 3 4 5 6 7
| helm history <release> -n <ns>
kubectl get secret -n <ns> | grep <release> kubectl delete secret -n <ns> sh.helm.release.v1.<release>.v<rev>
|
8.3 Error: configmaps already exists
Helm 创建的资源名与已有资源冲突:
1 2 3 4 5 6 7 8
| kubectl get configmap <name> -n <ns>
kubectl delete configmap <name> -n <ns>
helm uninstall <old-release> -n <ns>
|
8.4 模板渲染不符合预期
1 2 3 4 5
| helm template <name> <chart> -f values.yaml --debug
diff <(helm template . -f values-dev.yaml) <(helm template . -f values-prod.yaml)
|
8.5 helm repo update 报 Error: no repositories found
1 2 3 4 5 6
| helm repo list
helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts helm repo update
|
8.6 Pod 拉镜像失败(国内环境)
Chart 默认从 docker.io 拉镜像。用 --set 或修改 values 替换为国内镜像:
1 2 3 4
| helm install my-nginx aliyun/nginx \ --set image.registry=swr.cn-north-4.myhuaweicloud.com \ --set image.repository=ddn-k8s/docker.io/nginx \ --set image.tag=stable
|
8.7 helm rollback 回滚失败
1 2 3 4 5 6 7 8
| helm history <release> -n <ns>
helm rollback <release> <rev> -n <ns>
|
8.8 helm-secrets 解密失败
1 2 3 4 5 6 7 8
| gpg --list-secret-keys
echo $SOPS_PGP_FP
sops --decrypt secrets.yaml.enc
|
九、面试高频 12 题
1. Helm 是什么?解决了什么问题?
Helm 是 K8s 的包管理器,把一组 K8s YAML 打包成 Chart,解决”应用部署需要写一堆 YAML、每次改副本数要手动改多个文件”的问题。
2. Chart 和 Release 的区别?
Chart 是模板包(代码),Release 是 Chart 在集群中的一个部署实例(运行时)。一个 Chart 可以装出多个 Release。
3. helm install 和 helm upgrade --install 的区别?
helm install 资源不存在时安装,已存在则报错;helm upgrade --install 不存在就装,存在就升级。CI/CD 用后者更安全。
4. values.yaml 合并顺序是什么?
默认 values.yaml → -f 指定文件 → --set 命令行,后面的覆盖前面的。嵌套对象是 merge 而非 replace。
5. _helpers.tpl 的作用?
存放可复用的模板函数(define/invoke),类似代码里的公共函数。Helm 不会把它渲染成 K8s 资源,只供其他模板 include 调用。
6. trunc 63 | trimSuffix "-" 为什么?
K8s 资源名最长 63 字符,trunc 截断,trimSuffix 去掉末尾 -(K8s 不允许以 - 结尾)。
7. helm template 和 helm install 的区别?
helm template 只渲染 YAML 到标准输出,不部署、不记录 Release;helm install 部署到集群并记录状态。
8. Helm Hooks 是什么?
在 Release 生命周期的特定阶段执行 Job(如安装前初始化数据库、升级后清理旧资源)。常见的 hook:pre-install、post-upgrade、pre-delete。
9. Chart 依赖怎么管理?
Chart.yaml 中 dependencies 字段声明,helm dependency update 下载。或者直接把依赖 Chart 放到 charts/ 目录。
10. helm rollback 的原理?
每次 install/upgrade 都创建一个 Secret 存储当时的 manifests 和 values。rollback 就是切回某个历史 Secret,应用当时的配置。
11. ChartMuseum 和 Harbor 区别?
都是 Chart 私仓。ChartMuseum 只存 Helm Chart;Harbor 是全能镜像仓库,同时存容器镜像 + Helm Chart。
12. 生产上用 Helm 最大的坑?
helm upgrade 时 values 不完整导致部分配置被”重置”为默认值。解决:始终传完整的 values(-f values-prod.yaml),不要依赖 Chart 默认值。
十、总结:从入门到生产的学习路径
回头看,你已完成了这样一条路径:
1 2 3 4 5 6 7 8 9 10 11 12 13
| helm install/uninstall/upgrade/list ← 基本命令 ↓ Chart 结构 + 内置对象 + 流程控制 ← 理解模板 ↓ 手写 nginx Chart ← 从零构建 ↓ 多 values 文件 → dev/prod 环境 ← 环境管理 ↓ helm-diff → 发布前预览变更 ← 安全升级 ↓ helm-secrets + sops → 加密敏感配置 ← 生产安全 ↓ Harbor 私有仓库 → 团队共享分发 ← 协作交付
|
这套能力覆盖了 Helm 从开发到生产的核心场景,足以应对日常工作中 90% 的需求。更深入的用法(如 Library Chart、Helm Hooks、Helm Test)可以在实践中逐步探索。
常用命令速查
| 场景 |
命令 |
| 添加仓库 |
helm repo add <name> <url> |
| 搜索 Chart |
helm search repo <keyword> |
| 安装 |
helm install <name> <chart> -n <ns> -f values.yaml |
| 升级 |
helm upgrade <name> <chart> -f values.yaml |
| 回滚 |
helm rollback <name> <rev> |
| 查看历史 |
helm history <name> |
| 查看 values |
helm get values <name> |
| 查看渲染结果 |
helm template <name> <chart> -f values.yaml |
| 打包 |
helm package <chart-dir> |
| 推送 OCI |
helm push <file.tgz> oci://<registry>/<path> |
| Diff 预览 |
helm diff upgrade <name> <chart> -f values.yaml |
| Secrets 安装 |
helm secrets install <name> <chart> -f secrets.yaml.enc |
希望这篇教程能帮你扎实地掌握 Helm,从”会用”到”能写”,再到”敢上生产”。