Helm实战指南:从零到一掌握Kubernetes包管理工具
背景介绍
在没有 Helm 之前,我们在 Kubernetes 上部署应用通常是这样的:
- 编写 Deployment.yaml, Service.yaml, Ingress.yaml 等。
kubectl apply -f .- 如果需要修改镜像版本或副本数,手动编辑 yaml 文件,再次 apply。
痛点:
- 硬编码问题:参数(如副本数、镜像Tag)写死在 yaml 里,不同环境(Dev/Test/Prod)需要维护多套 yaml。
- 版本管理难:如何一键回滚到上一个版本?很难追踪 yaml 的变更历史。
- 共享困难:我写好的 Redis 高可用部署方案,很难打包给同事直接用。
Helm 的出现解决了这些问题。 它就像 Linux 下的 yum 或 apt,将 Kubernetes 资源打包成 Chart,通过简单的命令即可实现安装、升级、回滚。

环境准备
在开始之前,请确保你的操作环境满足以下要求。本次实战演示基于 Linux 环境。
- 操作系统:CentOS 7.9 或 Ubuntu 20.04+
- Kubernetes 集群:v1.20+ (本文演示基于 v1.26)
- 网络权限:能够访问外网(需要下载 Chart 包)
- 权限:拥有集群的
admin权限或足够的 RBAC 权限
Linux/macOS安装
# 下载最新版本
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
# 验证安装
helm version
补全插件
echo "source <(helm completion bash)" >> ~/.bashrc
source ~/.bashrc
核心概念速览
简单通过 3 个概念理清 Helm 逻辑:
- Chart(图表):即“软件包”,包含了一组定义 K8s 资源的文件(模板)。
- Repository(仓库):存放 Chart 的地方,类似 Docker Hub 或 Yum 源。
- Release(发行版):Chart 运行在集群中的一个实例。同一个 Chart 可以安装多次,每次安装都是一个不同的 Release(如
mysql-dev和mysql-prod)。
mychart/
├── Chart.yaml # Chart元数据
├── values.yaml # 默认配置值
├── charts/ # 子Chart目录
├── templates/ # 模板文件目录
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl # 辅助模板
└── README.md
实战演练:部署 Nginx 应用
接下来,我们通过部署一个 Nginx 来体验 Helm 的完整生命周期:添加仓库 -> 搜索 -> 安装 -> 升级 -> 回滚 -> 卸载。
添加 Chart 仓库
默认 Helm 没有添加仓库。Bitnami 是目前最活跃、质量最高的第三方仓库之一。
# 添加 bitnami 仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
# 更新仓库缓存(类似 yum makecache)
helm repo update
搜索应用
helm search repo nginx
经验之谈:你会看到很多结果,通常选择
bitnami/nginx,关注CHART VERSION(Chart包的版本) 和APP VERSION(软件本身的版本)。
安装应用 (Install)
我们将 Nginx 安装到 web-test 命名空间中。
# 创建命名空间
kubectl create ns web-test
# 安装 Release,名称取名为 my-web
helm install my-web bitnami/nginx -n web-test
执行后,Helm 会输出安装说明(NOTES),告诉你如何获取 Service IP 等信息。
查看安装状态:
helm list -n web-test
kubectl get pods -n web-test
###自定义配置 (Values)
这步是运维的关键。默认安装的配置往往不符合生产需求(比如默认副本数是 1,我们需要 3)。
方法一:使用 --set 临时修改(适合少量修改)
helm upgrade my-web bitnami/nginx \
--set replicaCount=2 \
-n web-test
方法二:使用 values.yaml 文件(生产环境推荐)
首先,导出默认配置:
helm show values bitnami/nginx > values.yaml
然后,使用编辑器(vim)修改 values.yaml:
# 修改如下内容
replicaCount: 3
image:
tag: 1.25.0 # 假设我们要指定特定版本
service:
type: NodePort # 方便外部访问测试
nodePorts:
http: 30080
执行升级:
helm upgrade my-web bitnami/nginx -f values.yaml -n web-test
版本回滚 (Rollback)
假设刚刚升级的 Nginx 1.25.0 有 Bug,老板要求立刻回滚。在没有 Helm 之前,你可能要改 yaml 重新 apply,现在只需:
# 1. 查看历史版本
helm history my-web -n web-test
# 输出:
# REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
# 1 ... superseded nginx-15.0.0 1.23.0 Install complete
# 2 ... superseded nginx-15.0.0 1.23.0 Upgrade complete
# 3 ... deployed nginx-15.0.0 1.25.0 Upgrade complete
# 2. 回滚到版本 1(最初安装的版本)
helm rollback my-web 1 -n web-test
卸载 (Uninstall)
测试完毕,清理环境。
helm uninstall my-web -n web-test
创建自定义Chart
初始化Chart
# 创建新Chart
helm create myapp
# 查看生成的文件结构
tree myapp/
编写Chart.yaml
apiVersion: v2
name: myapp
description: A Helm chart for my application
type: application
version: 0.1.0
appVersion: "1.0.0"
# 依赖声明(可选)
dependencies:
- name: mysql
version: "8.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: mysql.enabled
模板开发示例
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "myapp.labels" . | nindent 8 }}
app.kubernetes.io/component: web
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.port }}
name: http
env:
- name: DATABASE_HOST
value: {{ .Values.mysql.host | quote }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
values.yaml配置
replicaCount: 2
image:
repository: nginx
tag: "1.21-alpine"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
mysql:
enabled: true
host: "myapp-mysql"
安装自定义Chart
# 依赖更新(如果声明了依赖)
helm dependency update myapp/
# 模板渲染测试
helm template myapp/ --debug
# 安装Chart
helm install my-release ./myapp \
--namespace myapp \
--create-namespace \
--set replicaCount=3 \
--set image.tag=1.22-alpine
# 查看渲染的YAML
helm get manifest my-release -n myapp
Chart维护与分发
打包和分发
# 打包Chart
helm package myapp/ --destination ./dist
# 创建本地仓库
mkdir repo
mv dist/*.tgz repo/
helm repo index repo/ --url https://mycompany.com/charts/
# 使用本地仓库
helm repo add myrepo ./repo
helm install myapp myrepo/myapp
Chart测试与验证
# 语法检查
helm lint myapp/
# 模板渲染测试
helm template myapp/ --values myapp/values.yaml
# 安装测试(dry-run)
helm install myapp ./myapp --dry-run --debug
# 升级测试
helm upgrade myapp ./myapp --dry-run --debug
模板编程技巧汇总
Helm 模板语法速查表
#=============================================================================
# 1. 变量访问
#=============================================================================
{{ .Values.replicaCount }} # 访问 values
{{ .Release.Name }} # Release 名称
{{ .Release.Namespace }} # 命名空间
{{ .Chart.Name }} # Chart 名称
{{ .Chart.Version }} # Chart 版本
{{ .Template.Name }} # 当前模板名称
#=============================================================================
# 2. 条件判断
#=============================================================================
# if-else
{{- if .Values.ingress.enabled }}
# 渲染 ingress
{{- else }}
# 不渲染
{{- end }}
# 复杂条件
{{- if and .Values.ingress.enabled (eq .Values.ingress.className "nginx") }}
# nginx ingress 特殊处理
{{- end }}
# 检查值是否存在
{{- if .Values.env }}
{{- if hasKey .Values "env" }}
# 空值检查
{{- if not (empty .Values.resources) }}
#=============================================================================
# 3. 循环
#=============================================================================
# range 循环
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
# range with index
{{- range $index, $host := .Values.ingress.hosts }}
- host: {{ $host.host }}
{{- end }}
# range over map
{{- range $key, $value := .Values.configMap.data }}
{{ $key }}: {{ $value }}
{{- end }}
#=============================================================================
# 4. 常用函数
#=============================================================================
{{ .Values.name | quote }} # 加引号
{{ .Values.name | upper }} # 大写
{{ .Values.name | lower }} # 小写
{{ .Values.name | title }} # 首字母大写
{{ .Values.name | trunc 63 }} # 截断
{{ .Values.name | trimSuffix "-" }} # 移除后缀
{{ .Values.name | indent 4 }} # 缩进
{{ .Values.name | nindent 4 }} # 换行+缩进
{{ .Values.data | toYaml }} # 转 YAML
{{ .Values.data | toJson }} # 转 JSON
{{ .Values.secret | b64enc }} # Base64 编码
{{ .Values.secret | b64dec }} # Base64 解码
{{ default "default" .Values.name }} # 默认值
{{ coalesce .Values.a .Values.b }} # 返回第一个非空值
#=============================================================================
# 5. 逻辑运算
#=============================================================================
{{ and .Values.a .Values.b }} # 且
{{ or .Values.a .Values.b }} # 或
{{ not .Values.a }} # 非
{{ eq .Values.a "value" }} # 等于
{{ ne .Values.a "value" }} # 不等于
{{ lt .Values.a 10 }} # 小于
{{ le .Values.a 10 }} # 小于等于
{{ gt .Values.a 10 }} # 大于
{{ ge .Values.a 10 }} # 大于等于
{{ empty .Values.a }} # 是否为空
#=============================================================================
# 6. 模板复用
#=============================================================================
# 定义模板
{{- define "myapp.labels" -}}
app: {{ .Chart.Name }}
{{- end }}
# 调用模板
{{ include "myapp.labels" . }}
{{ template "myapp.labels" . }}
# include vs template
# include 可以管道操作,template 不行
{{ include "myapp.labels" . | nindent 4 }}
#=============================================================================
# 7. 变量定义
#=============================================================================
{{- $name := .Values.name -}}
{{- $fullname := printf "%s-%s" .Release.Name .Chart.Name -}}
#=============================================================================
# 8. with 语句(改变作用域)
#=============================================================================
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 2 }}
{{- end }}
#=============================================================================
# 9. 控制空白
#=============================================================================
{{- xxx }} # 移除左边空白
{{ xxx -}} # 移除右边空白
{{- xxx -}} # 移除两边空白
实用技巧示例
# 技巧1: 根据条件设置不同的值
{{- $probePort := ternary .Values.service.port .Values.containerPort .Values.service.enabled -}}
# 技巧2: 合并多个 map
{{- $labels := merge .Values.podLabels (include "myapp.selectorLabels" . | fromYaml) -}}
# 技巧3: 动态生成环境变量
env:
{{- range $key, $value := .Values.envVars }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
# 技巧4: 条件性地添加数组元素
containers:
- name: main
# ...
{{- if .Values.sidecars }}
{{- toYaml .Values.sidecars | nindent 2 }}
{{- end }}
# 技巧5: 计算配置 checksum(用于触发滚动更新)
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
# 技巧6: 根据值类型处理
{{- if kindIs "string" .Values.command }}
command: {{ .Values.command }}
{{- else if kindIs "slice" .Values.command }}
command:
{{- toYaml .Values.command | nindent 2 }}
{{- end }}
# 技巧7: 必需值验证
image: {{ required "image.repository is required" .Values.image.repository }}
# 技巧8: 失败时提供有意义的错误
{{- if and .Values.autoscaling.enabled (lt (int .Values.replicaCount) 1) }}
{{- fail "When autoscaling is enabled, replicaCount should be at least 1" }}
{{- end }}
更多推荐


所有评论(0)