Helm Kubernetes 包管理
大约 11 分钟约 3189 字
Helm Kubernetes 包管理
简介
Helm 是 Kubernetes 的官方包管理工具,被誉为 "Kubernetes 的 apt/yum"。它通过 Chart 的概念将一组相关的 Kubernetes 资源定义打包在一起,配合模板引擎和值文件机制,实现了 Kubernetes 应用的版本化、参数化和可复用部署。Helm 极大地简化了复杂应用的部署流程,使运维团队能够像管理软件包一样管理 Kubernetes 应用的生命周期,包括安装、升级、回滚和卸载。
特点
Chart 目录结构
Chart 是 Helm 的核心概念,一个标准的 Chart 包含以下目录结构。
# 创建一个新的 Chart
helm create my-application
# Chart 目录结构
my-application/
├── Chart.yaml # Chart 元数据(名称、版本、依赖等)
├── values.yaml # 默认配置值
├── charts/ # 依赖的子 Chart
├── templates/ # Kubernetes 资源模板
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── _helpers.tpl # 可复用的模板片段
│ ├── NOTES.txt # 安装后的说明信息
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ └── tests/
│ └── test-connection.yaml
├── templates_tests/ # 模板测试文件
├── .helmignore # 打包时忽略的文件
└── README.md # Chart 说明文档# Chart.yaml - Chart 元数据定义
apiVersion: v2
name: my-application
description: A production-ready web application Helm chart
type: application
version: 1.2.0
appVersion: "3.4.1"
kubeVersion: ">=1.24.0-0"
home: https://github.com/myorg/my-application
sources:
- https://github.com/myorg/my-application
maintainers:
- name: devops-team
email: devops@example.com
url: https://example.com
keywords:
- web
- application
- nginx
annotations:
category: Application
dependencies:
- name: postgresql
version: "13.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "18.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
- name: common
version: "2.x.x"
repository: "https://charts.bitnami.com/bitnami"values.yaml 配置管理
values.yaml 是 Helm Chart 的配置中心,通过分层覆盖实现多环境部署。
# values.yaml - 默认配置
replicaCount: 2
image:
repository: myregistry.com/my-application
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets:
- name: registry-secret
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
podSecurityContext:
fsGroup: 1000
runAsNonRoot: true
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1000
capabilities:
drop:
- ALL
service:
type: ClusterIP
port: 8080
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5# values-production.yaml - 生产环境覆盖
replicaCount: 5
image:
tag: "v3.4.1"
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
autoscaling:
enabled: true
minReplicas: 5
maxReplicas: 20
targetCPUUtilizationPercentage: 70
# 启用 Pod 反亲和性,确保高可用
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- my-application
topologyKey: kubernetes.io/hostname
tolerations:
- key: "dedicated"
operator: "Equal"
value: "production"
effect: "NoSchedule"
nodeSelector:
node-role: application
# 启用 PDB
podDisruptionBudget:
enabled: true
minAvailable: "80%"模板语法详解
Helm 使用 Go 模板语言,结合 Sprig 函数库和 Helm 内置函数,提供了强大的模板渲染能力。
# templates/_helpers.tpl - 公共模板片段
{{/*
应用名称
*/}}
{{- define "my-application.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
完整应用名称
*/}}
{{- define "my-application.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
通用标签
*/}}
{{- define "my-application.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{ include "my-application.selectorLabels" . }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
选择器标签
*/}}
{{- define "my-application.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-application.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-application.fullname" . }}
labels:
{{- include "my-application.labels" . | nindent 4 }}
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-application.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
template:
metadata:
labels:
{{- include "my-application.selectorLabels" . | nindent 8 }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "my-application.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
- name: NODE_ENV
value: {{ .Values.environment | default "production" | quote }}
{{- range $key, $value := .Values.extraEnv }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
volumeMounts:
- name: config
mountPath: /app/config
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: {{ include "my-application.fullname" . }}
- name: tmp
emptyDir: {}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}Chart 测试与验证
helm lint 与模板调试
# 语法检查 — 验证 Chart 格式
helm lint my-application/
helm lint my-application/ --strict # 严格模式,警告也视为错误
# 模板渲染调试 — 查看最终 YAML(不安装)
helm template my-app my-application/ -f values-production.yaml > rendered.yaml
# 本地渲染并检查
helm template my-app my-application/ \
--set image.tag=v1.0.0 \
--debug
# 使用 helm unittest 插件
helm plugin install https://github.com/helm-unittest/helm-unittest
helm unittest my-application/
# 模拟安装(Dry Run)
helm install my-app my-application/ \
--dry-run=client \
--debug \
-f values-production.yaml \
-n production
# 服务端 Dry Run(验证 API 对象)
helm install my-app my-application/ \
--dry-run \
--debug \
-n production
# 查看渲染后的差异
helm template my-app my-application/ --set image.tag=v1.0.0 > /tmp/v1.yaml
helm template my-app my-application/ --set image.tag=v2.0.0 > /tmp/v2.yaml
diff /tmp/v1.yaml /tmp/v2.yamlChart 测试用例
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "my-application.fullname" . }}-test-connection"
labels:
{{- include "my-application.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['--spider', '-q', '{{ include "my-application.fullname" . }}:{{ .Values.service.port }}/healthz']
restartPolicy: Never# 运行 Chart 测试
helm test my-app -n production
helm test my-app -n production --logs # 查看测试日志Hooks 生命周期管理
Helm Hooks 实战
# templates/hooks/pre-install-hook.yaml — 安装前执行数据库迁移
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ include "my-application.fullname" . }}-db-migrate"
labels:
{{- include "my-application.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command: ["dotnet", "Ef.dll", "database", "update"]
env:
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: {{ include "my-application.fullname" . }}-secret
key: connection-string
---
# templates/hooks/post-install-hook.yaml — 安装后初始化数据
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ include "my-application.fullname" . }}-seed-data"
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: seed
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["dotnet", "Seed.dll"]# Hook 类型与执行时机:
# Hook 类型 执行时机
# pre-install 安装前(渲染模板后,资源创建前)
# post-install 安装后(所有资源就绪后)
# pre-delete 删除前
# post-delete 删除后
# pre-upgrade 升级前
# post-upgrade 升级后
# pre-rollback 回滚前
# post-rollback 回滚后
# test helm test 执行时
# Hook 权重(weight):
# 数字越小越先执行,默认为 0
# 例如:-5 先于 0 执行,0 先于 5 执行多环境部署策略
环境差异管理
# 推荐的 values 文件组织方式
my-application/
├── values.yaml # 默认值(开发环境基准)
├── values-staging.yaml # 预发布环境覆盖
├── values-production.yaml # 生产环境覆盖
└── values-production-cn-east.yaml # 特定区域覆盖# values-staging.yaml
replicaCount: 2
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
ingress:
enabled: true
hosts:
- host: staging.app.example.com
paths:
- path: /
pathType: Prefix
monitoring:
enabled: true
debug: true# 部署到不同环境
# 开发环境
helm install my-app ./my-application -n development
# 预发布环境
helm upgrade --install my-app ./my-application \
-f values-staging.yaml -n staging
# 生产环境(带审批和验证)
helm upgrade --install my-app ./my-application \
-f values-production.yaml \
--dry-run \
-n production
# 确认无误后实际部署
helm upgrade --install my-app ./my-application \
-f values-production.yaml \
--timeout 10m \
--wait \
--atomic \
-n production
# --atomic:部署失败自动回滚
# --wait:等待所有 Pod 就绪
# --timeout:超时时间使用 helmfile 管理多环境
# helmfile.yaml — 声明式多服务管理
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
- name: myrepo
url: https://charts.example.com
environments:
staging:
values:
- environments/staging.yaml
production:
values:
- environments/production.yaml
helmDefaults:
wait: true
timeout: 600
createNamespace: true
releases:
- name: my-app
namespace: {{ .Environment.Name }}
chart: myrepo/my-application
version: 1.2.0
values:
- values.yaml
- values-{{ .Environment.Name }}.yaml
secrets:
- secrets-{{ .Environment.Name }}.yaml
- name: redis
namespace: {{ .Environment.Name }}
chart: bitnami/redis
version: 18.x.x
values:
- redis/values-{{ .Environment.Name }}.yaml# helmfile 常用命令
helmfile -e staging sync # 同步 staging 环境
helmfile -e production apply # 应用 production 环境
helmfile -e production diff # 查看变更差异
helmfile destroy # 销毁所有环境Chart 安全最佳实践
Secret 管理方案
# 方案1:helm-secrets 插件(使用 SOPS/Mozilla 加密)
helm plugin install https://github.com/jkroepke/helm-secrets
# 加密 values 文件中的敏感信息
helm secrets encrypt values-production.yaml > secrets-production.yaml
# 使用加密的 values 部署
helm secrets upgrade --install my-app ./my-application \
-f values.yaml \
-f secrets://secrets-production.yaml \
-n production
# 方案2:外部 Secret 管理(Vault + External Secrets Operator)
# 不在 Chart 中存储敏感信息,由外部系统注入Pod 安全标准
# 在 values.yaml 中强制安全上下文
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# 启用 NetworkPolicy
networkPolicy:
enabled: true
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080Chart 仓库与分发
# 使用 OCI 注册表管理 Chart
# 登录 OCI 注册表
helm registry login registry.example.com -u username -p password
# 打包并推送 Chart
helm package my-application --version 1.2.0
helm push my-application-1.2.0.tgz oci://registry.example.com/charts
# 从 OCI 注册表安装
helm install my-app oci://registry.example.com/charts/my-application --version 1.2.0
# 使用传统 Chart 仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add myrepo https://charts.example.com
helm repo update
# 搜索 Chart
helm search repo nginx
helm search hub wordpress
# 安装与升级
helm install my-app myrepo/my-application -f values-production.yaml -n production
helm upgrade my-app myrepo/my-application -f values-production.yaml -n production
helm rollback my-app 1 -n production
# 查看发布状态
helm list -n production
helm status my-app -n production
helm history my-app -n production优点
缺点
总结
Helm 作为 Kubernetes 生态中最成熟的包管理工具,通过 Chart、模板引擎和 values 覆盖机制构建了一套完整的应用打包和分发体系。其标准化的目录结构和丰富的模板语法使运维团队能够以参数化的方式管理复杂应用的部署,结合 OCI 注册表支持和完善的版本管理机制,Helm 已成为 Kubernetes 应用交付的事实标准。虽然模板语法的复杂性带来了一定的学习成本,但凭借其强大的表达能力和活跃的社区生态,Helm 仍然是 Kubernetes 应用管理的首选工具。
关键知识点
- DevOps 主题的核心是让交付更快、更稳、更可审计。
- 自动化不是把命令脚本化,而是把失败、回滚、权限和观测一起设计进去。
- 生产链路必须明确制品、环境、凭据、配置和责任边界。
- Kubernetes 主题必须同时看资源对象、调度行为、网络暴露和配置分发。
项目落地视角
- 把流水线拆成构建、测试、制品、部署、验证和回滚几个阶段。
- 为关键步骤补齐日志、指标、通知和人工兜底点。
- 定期演练扩容、回滚、故障注入和灾备切换。
- 上线前检查镜像、命名空间、探针、资源限制、Service/Ingress 和配置来源。
常见误区
- 只关注部署成功,不关注失败恢复和审计追踪。
- 把环境差异藏在临时脚本或人工操作里。
- 上线频率高了以后,没有标准化制品和配置管理。
- 只会 apply YAML,不理解对象之间的依赖关系。
进阶路线
- 继续补齐 GitOps、可观测性、平台工程和成本治理。
- 把主题和应用架构、安全、权限、备份恢复联动起来理解。
- 形成团队级平台能力,而不是每个项目重复造轮子。
- 继续补齐调度、网络策略、存储、GitOps 和平台工程能力。
适用场景
- 当你准备把《Helm Kubernetes 包管理》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合构建自动化交付、基础设施治理、监控告警和生产发布体系。
- 当团队规模扩大、发布频率提升或环境变多时,这类主题会显著影响交付效率。
落地建议
- 所有自动化流程尽量做到幂等、可审计、可回滚。
- 把制品、变量、凭据和执行权限分层管理。
- 定期演练扩容、回滚、密钥轮换和灾备恢复。
排错清单
- 先定位失败发生在代码、构建、制品、环境还是权限层。
- 检查流水线变量、凭据、镜像标签和目标环境配置是否一致。
- 如果问题偶发,重点看并发发布、资源争抢和外部依赖抖动。
复盘问题
- 如果把《Helm Kubernetes 包管理》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Helm Kubernetes 包管理》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Helm Kubernetes 包管理》最大的收益和代价分别是什么?
