一、概述
1.1 背景介绍
Helm作为Kubernetes的包管理工具,已成为云原生应用部署的事实标准。随着微服务架构的复杂度不断提升,简单的Helm Chart已无法满足企业级应用的部署需求。如何优雅地处理复杂的配置逻辑、管理多层次的Chart依赖、实现配置的复用和抽象,成为运维工程师面临的重要挑战。
高级Helm Chart开发不仅仅是编写YAML模板,更是一门关于抽象、封装和可维护性的艺术。通过掌握自定义模板函数、命名模板、复杂依赖管理、Hooks机制等高级特性,可以构建出灵活、可复用、易维护的企业级Helm Chart。本指南将深入探讨这些高级技巧,帮助您从Helm初学者成长为Chart开发专家。
1.2 技术特点
- 强大的模板引擎:基于Go template语法,支持变量、函数、流程控制、管道等高级特性,实现复杂的配置生成逻辑
- 命名模板复用:通过
define和include机制实现代码复用,避免重复定义,提高Chart的可维护性
- 依赖管理体系:支持Chart依赖声明、版本约束、条件启用、父子Chart值传递等复杂依赖场景
- 生命周期Hooks:提供
pre-install、post-install、pre-upgrade等钩子,实现数据库迁移、备份等复杂部署流程
- 内置函数库:丰富的Sprig函数库(200+函数),涵盖字符串处理、数学运算、加密、日期时间等常用操作
- 调试和测试工具:
helm lint、helm template、helm test等工具链,确保Chart质量
1.3 适用场景
- 企业级微服务平台:大型企业需要部署包含数十个微服务的复杂应用,服务间存在依赖关系,需要统一管理配置、版本和部署顺序
- 多环境部署:同一应用需要部署到开发、测试、预发布、生产等多个环境,每个环境的配置参数(如副本数、资源限制、域名)不同,需要灵活的配置管理机制
- 应用市场和软件分发:SaaS平台或企业内部应用市场需要提供标准化的应用安装包,用户可通过简单配置一键部署复杂应用
- 基础设施即代码:将Kubernetes集群的基础组件(如Ingress Controller、监控系统、日志收集)封装为Helm Chart,实现基础设施的版本化管理
- 多租户应用部署:为不同租户部署隔离的应用实例,每个实例有独立的配置和资源限制,但共享相同的Chart模板
1.4 环境要求
| 组件 |
版本要求 |
说明 |
| 操作系统 |
Linux/macOS/Windows |
Helm支持主流操作系统 |
| Kubernetes |
1.23+ |
Chart API版本需与Kubernetes版本匹配 |
| Helm |
3.10+ |
推荐使用Helm 3,已废弃Tiller |
| kubectl |
1.23+ |
与Kubernetes版本保持一致 |
| Git |
2.30+ |
用于Chart源码管理和版本控制 |
| helm-docs |
1.11+ |
自动生成Chart文档(可选) |
| chart-testing |
3.8+ |
Chart CI/CD测试工具(可选) |
| 硬件配置 |
2C4G(开发) |
Chart开发对硬件要求不高 |
二、详细步骤
2.1 准备工作
◆ 2.1.1 系统检查
# 检查Helm版本
helm version --short
# 检查Kubernetes集群连接
kubectl cluster-info
kubectl get nodes
# 验证Helm仓库配置
helm repo list
# 添加常用仓库
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# 检查当前Helm releases
helm list -A
# 验证Helm插件
helm plugin list
◆ 2.1.2 安装开发工具
# 安装helm-docs(自动生成README)
wget https://github.com/norwoodj/helm-docs/releases/download/v1.11.0/helm-docs_1.11.0_Linux_x86_64.tar.gz
tar -xzf helm-docs_1.11.0_Linux_x86_64.tar.gz
sudo mv helm-docs /usr/local/bin/
helm-docs --version
# 安装chart-testing(CT工具)
wget https://github.com/helm/chart-testing/releases/download/v3.8.0/chart-testing_3.8.0_linux_amd64.tar.gz
tar -xzf chart-testing_3.8.0_linux_amd64.tar.gz
sudo mv ct /usr/local/bin/
ct version
# 安装kubeval(Kubernetes manifest验证)
wget https://github.com/instrumenta/kubeval/releases/download/v0.16.1/kubeval-linux-amd64.tar.gz
tar -xzf kubeval-linux-amd64.tar.gz
sudo mv kubeval /usr/local/bin/
kubeval --version
# 安装helm-unittest插件(单元测试)
helm plugin install https://github.com/helm-unittest/helm-unittest
# 验证工具安装
which helm-docs ct kubeval
helm plugin list | grep unittest
◆ 2.1.3 创建Chart项目结构
# 创建新的Chart
helm create myapp
# 查看生成的目录结构
tree myapp
# 标准Chart目录结构
# myapp/
# ├── Chart.yaml # Chart元数据
# ├── values.yaml # 默认配置值
# ├── charts/ # 依赖的子Chart
# ├── templates/ # Kubernetes资源模板
# │ ├── NOTES.txt # 安装后提示信息
# │ ├── _helpers.tpl # 命名模板定义
# │ ├── deployment.yaml
# │ ├── service.yaml
# │ ├── ingress.yaml
# │ └── tests/ # Helm测试用例
# │ └── test-connection.yaml
# └── .helmignore # 打包时忽略的文件
# 创建高级Chart目录结构
mkdir -p myapp/{crds,templates/{hooks,tests},ci}
# 完整的企业级Chart结构
# myapp/
# ├── Chart.yaml
# ├── values.yaml
# ├── values.schema.json # values验证schema
# ├── README.md # Chart文档
# ├── LICENSE
# ├── .helmignore
# ├── charts/ # 子Chart依赖
# ├── crds/ # Custom Resource Definitions
# ├── templates/
# │ ├── _helpers.tpl # 命名模板
# │ ├── _custom_functions.tpl # 自定义函数
# │ ├── NOTES.txt
# │ ├── configmap.yaml
# │ ├── deployment.yaml
# │ ├── service.yaml
# │ ├── ingress.yaml
# │ ├── hpa.yaml
# │ ├── pdb.yaml
# │ ├── serviceaccount.yaml
# │ ├── rbac.yaml
# │ ├── hooks/ # 生命周期钩子
# │ │ ├── pre-install-job.yaml
# │ │ └── post-upgrade-job.yaml
# │ └── tests/ # 测试用例
# │ └── test-connection.yaml
# └── ci/ # CI/CD配置
# ├── test-values.yaml
# └── lint-values.yaml
2.2 核心配置
◆ 2.2.1 Chart.yaml元数据配置
# 文件路径:myapp/Chart.yaml
# Chart元数据定义
apiVersion: v2 # Chart API版本(v2用于Helm 3)
name: myapp
description: A production-grade Helm chart for microservice deployment
type: application # 类型:application或library
# 版本信息(语义化版本)
version: 1.2.3 # Chart版本
appVersion: "2.5.1" # 应用版本
# 维护者信息
maintainers:
- name: DevOps Team
email: devops@example.com
url: https://github.com/example/myapp
# Chart来源
sources:
- https://github.com/example/myapp
- https://hub.docker.com/r/example/myapp
# 主页和图标
home: https://myapp.example.com
icon: https://myapp.example.com/logo.png
# 关键词(用于搜索)
keywords:
- microservice
- api
- production
# 依赖管理
dependencies:
# PostgreSQL数据库
- name: postgresql
version: "12.1.9"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # 条件启用
tags:
- database
import-values: # 从子Chart导入值
- child: postgresql.auth.password
parent: database.password
# Redis缓存
- name: redis
version: "17.8.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
tags:
- cache
# 公共库Chart(可复用的模板和函数)
- name: common
version: "1.x.x"
repository: https://charts.example.com/library
# 注解(元数据)
annotations:
category: Application
licenses: Apache-2.0
# Artifact Hub注解
artifacthub.io/changes: |
- kind: added
description: Support for custom environment variables
- kind: changed
description: Updated application to version 2.5.1
- kind: fixed
description: Fixed ingress path issue
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/prerelease: "false"
说明:Chart.yaml是Chart的核心元数据文件,定义了Chart的基本信息、版本、依赖关系等。version字段遵循语义化版本规范(SemVer),每次Chart变更都应更新版本号。dependencies字段支持条件启用、版本约束、值导入等高级特性。
◆ 2.2.2 自定义模板函数开发
{{/*
生成资源名称的通用函数
参数:
- .Chart.Name: Chart名称
- .Release.Name: Release名称
- suffix: 资源后缀(如"db"、"cache")
用法:{{ include "myapp.fullname" . }}
*/}}
{{- define "myapp.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 -}}
{{/*
生成带后缀的资源名称
用法:{{ include "myapp.name.with.suffix" (dict "context" . "suffix" "worker") }}
*/}}
{{- define "myapp.name.with.suffix" -}}
{{- $name := include "myapp.fullname" .context -}}
{{- printf "%s-%s" $name .suffix | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
生成标准标签
用法:{{ include "myapp.labels" . | nindent 4 }}
*/}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ include "myapp.chart" . }}
{{ include "myapp.selectorLabels" . }}
{{- if .Chart.AppVersion -}}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Values.commonLabels -}}
{{ toYaml .Values.commonLabels }}
{{- end -}}
{{- end -}}
{{/*
选择器标签
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/*
Chart名称和版本
*/}}
{{- define "myapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
生成环境变量列表(合并默认和自定义)
用法:{{ include "myapp.env" . | nindent 12 }}
*/}}
{{- define "myapp.env" -}}
{{- $defaultEnv := list -}}
{{- $defaultEnv = append $defaultEnv (dict "name" "APP_ENV" "value" .Values.environment) -}}
{{- $defaultEnv = append $defaultEnv (dict "name" "LOG_LEVEL" "value" .Values.logLevel) -}}
{{- if .Values.database.enabled -}}
{{- $defaultEnv = append $defaultEnv (dict "name" "DB_HOST" "value" (printf "%s-postgresql" (include "myapp.fullname" .))) -}}
{{- $defaultEnv = append $defaultEnv (dict "name" "DB_PORT" "value" "5432") -}}
{{- end -}}
{{- $allEnv := concat $defaultEnv (.Values.extraEnv | default list) -}}
{{- range $allEnv -}}
- name: {{ .name }}
{{- if .value -}}
value: {{ .value | quote }}
{{- else if .valueFrom -}}
valueFrom:
{{- toYaml .valueFrom | nindent 4 -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
生成镜像拉取地址
支持全局镜像仓库覆盖
用法:{{ include "myapp.image" . }}
*/}}
{{- define "myapp.image" -}}
{{- $registry := .Values.image.registry -}}
{{- if .Values.global -}}
{{- if .Values.global.imageRegistry -}}
{{- $registry = .Values.global.imageRegistry -}}
{{- end -}}
{{- end -}}
{{- $repository := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- if $registry -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- else -}}
{{- printf "%s:%s" $repository $tag -}}
{{- end -}}
{{- end -}}
{{/*
生成资源限制(支持QoS类别)
用法:{{ include "myapp.resources" . | nindent 10 }}
*/}}
{{- define "myapp.resources" -}}
{{- if .Values.resources -}}
{{- toYaml .Values.resources -}}
{{- else if eq .Values.qosClass "Guaranteed" -}}
requests:
cpu: {{ .Values.defaultResources.cpu }}
memory: {{ .Values.defaultResources.memory }}
limits:
cpu: {{ .Values.defaultResources.cpu }}
memory: {{ .Values.defaultResources.memory }}
{{- else if eq .Values.qosClass "Burstable" -}}
requests:
cpu: {{ .Values.defaultResources.cpu }}
memory: {{ .Values.defaultResources.memory }}
limits:
cpu: {{ mul (.Values.defaultResources.cpu | trimSuffix "m" | atoi) 2 }}m
memory: {{ mul (.Values.defaultResources.memory | trimSuffix "Mi" | atoi) 2 }}Mi
{{- else -}}
requests:
cpu: 100m
memory: 128Mi
{{- end -}}
{{- end -}}
{{/*
条件渲染Ingress路径
支持不同Ingress Controller的路径类型
*/}}
{{- define "myapp.ingressPath" -}}
{{- if eq .Values.ingress.className "nginx" -}}
- path: {{ .path }}
pathType: Prefix
backend:
service:
name: {{ include "myapp.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- else if eq .Values.ingress.className "traefik" -}}
- path: {{ .path }}
pathType: Prefix
backend:
service:
name: {{ include "myapp.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end -}}
{{- end -}}
{{/*
生成安全上下文
用法:{{ include "myapp.securityContext" . | nindent 10 }}
*/}}
{{- define "myapp.securityContext" -}}
{{- if .Values.securityContext -}}
{{- toYaml .Values.securityContext -}}
{{- else -}}
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
{{- end -}}
{{- end -}}
{{/*
验证必需的配置值
用法:{{ include "myapp.validate" . }}
*/}}
{{- define "myapp.validate" -}}
{{- if not .Values.image.repository -}}
{{- fail "image.repository is required" -}}
{{- end -}}
{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) -}}
{{- fail "ingress.hosts must be set when ingress is enabled" -}}
{{- end -}}
{{- if and .Values.database.enabled (not .Values.database.password) -}}
{{- fail "database.password is required when database is enabled" -}}
{{- end -}}
{{- end -}}
◆ 2.2.3 复杂依赖管理配置
# 文件路径:myapp/values.yaml
# 主Chart配置文件(包含子Chart配置)
# 全局配置(所有子Chart共享)
global:
imageRegistry: "" # 全局镜像仓库覆盖
imagePullSecrets: []
storageClass: ""
# 应用配置
image:
registry: docker.io
repository: myapp/api
tag: "" # 默认使用Chart.appVersion
pullPolicy: IfNotPresent
replicaCount: 3
environment: production
logLevel: info
# 资源配置
qosClass: Burstable # Guaranteed, Burstable, BestEffort
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
# 服务配置
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress配置
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
# 安全配置
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
allowPrivilegeEscalation: false
# 自定义环境变量
extraEnv:
- name: CUSTOM_VAR
value: "custom-value"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: myapp-secrets
key: secret-key
# ===== 依赖子Chart配置 =====
# PostgreSQL数据库配置
postgresql:
enabled: true # 条件启用
auth:
username: myapp
password: "" # 应通过values文件或--set覆盖
database: myapp_prod
primary:
persistence:
enabled: true
size: 10Gi
resources:
requests:
cpu: 250m
memory: 256Mi
metrics:
enabled: true
# Redis缓存配置
redis:
enabled: true
architecture: standalone
auth:
enabled: true
password: ""
master:
persistence:
enabled: true
size: 5Gi
resources:
requests:
cpu: 100m
memory: 128Mi
# 条件性配置(基于tags)
tags:
database: true # 启用所有database tag的子Chart
cache: true # 启用所有cache tag的子Chart
# 文件路径:myapp/ci/test-values.yaml
# CI/CD测试环境配置(覆盖默认值)
replicaCount: 1
environment: test
logLevel: debug
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
ingress:
enabled: false
# 测试环境使用内存数据库
postgresql:
enabled: false
redis:
enabled: false
# 如果需要Redis,使用轻量配置
# master:
# persistence:
# enabled: false
2.3 启动和验证
◆ 2.3.1 更新Chart依赖
# 下载依赖的子Chart
cd myapp
helm dependency update
# 查看依赖树
helm dependency list
# 验证Chart结构
helm lint .
# 模拟渲染模板(不实际部署)
helm template my-release . > rendered.yaml
# 查看渲染后的特定资源
helm template my-release . --show-only templates/deployment.yaml
# 使用特定values文件渲染
helm template my-release . -f ci/test-values.yaml
# 设置特定值
helm template my-release . --set replicaCount=5 --set image.tag=v2.0
# 验证渲染结果的Kubernetes语法
helm template my-release . | kubeval --strict
# 调试模板(显示详细信息)
helm install my-release . --dry-run --debug
◆ 2.3.2 Chart打包和发布
# 打包Chart
helm package .
# 输出:myapp-1.2.3.tgz
# 生成索引文件(用于Chart仓库)
helm repo index . --url https://charts.example.com
# 推送到ChartMuseum仓库
curl --data-binary "@myapp-1.2.3.tgz" https://chartmuseum.example.com/api/charts
# 或使用helm-push插件
helm plugin install https://github.com/chartmuseum/helm-push
helm cm-push myapp-1.2.3.tgz chartmuseum
# 推送到Harbor仓库
helm registry login harbor.example.com
helm package .
helm push myapp-1.2.3.tgz oci://harbor.example.com/charts
# 推送到GitHub Pages(静态Chart仓库)
git clone https://github.com/example/helm-charts.git
cp myapp-1.2.3.tgz helm-charts/
cd helm-charts
helm repo index . --url https://example.github.io/helm-charts
git add .
git commit -m "Add myapp chart v1.2.3"
git push
◆ 2.3.3 安装和测试
# 安装Chart到Kubernetes
helm install my-release myapp/ \
--namespace myapp \
--create-namespace \
--set postgresql.auth.password=secretpassword \
--set redis.auth.password=redispassword
# 查看部署状态
helm status my-release -n myapp
# 查看生成的资源
kubectl get all -n myapp
# 运行Helm测试
helm test my-release -n myapp
# 查看测试结果
kubectl logs -n myapp -l "app.kubernetes.io/name=myapp-test"
# 升级Chart
helm upgrade my-release myapp/ \
--namespace myapp \
--reuse-values \
--set image.tag=v2.1
# 回滚到上一个版本
helm rollback my-release -n myapp
# 回滚到指定版本
helm rollback my-release 2 -n myapp
# 查看历史版本
helm history my-release -n myapp
# 卸载
helm uninstall my-release -n myapp
三、示例代码和配置
3.1 完整配置示例
◆ 3.1.1 高级Deployment模板
# 文件路径:myapp/templates/deployment.yaml
# 功能丰富的Deployment模板
{{ include "myapp.validate" . }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
{{- if .Values.deploymentLabels }}
{{- toYaml .Values.deploymentLabels | nindent 4 }}
{{- end }}
annotations:
{{- if .Values.deploymentAnnotations }}
{{- toYaml .Values.deploymentAnnotations | nindent 4 }}
{{- end }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
{{- if .Values.strategy }}
strategy:
{{- toYaml .Values.strategy | nindent 4 }}
{{- end }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- if .Values.podAnnotations }}
{{- toYaml .Values.podAnnotations | nindent 8 }}
{{- end }}
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
{{- if .Values.podLabels }}
{{- toYaml .Values.podLabels | nindent 8 }}
{{- end }}
spec:
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.imagePullSecrets | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "myapp.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- if .Values.initContainers }}
initContainers:
{{- tpl (toYaml .Values.initContainers) . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- include "myapp.securityContext" . | nindent 10 }}
image: {{ include "myapp.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.command }}
command:
{{- toYaml .Values.command | nindent 10 }}
{{- end }}
{{- if .Values.args }}
args:
{{- toYaml .Values.args | nindent 10 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- if .Values.metrics.enabled }}
- name: metrics
containerPort: {{ .Values.metrics.port }}
protocol: TCP
{{- end }}
env:
{{- include "myapp.env" . | nindent 10 }}
{{- if .Values.envFrom }}
envFrom:
{{- toYaml .Values.envFrom | nindent 10 }}
{{- end }}
{{- if .Values.livenessProbe }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 10 }}
{{- end }}
{{- if .Values.readinessProbe }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 10 }}
{{- end }}
{{- if .Values.startupProbe }}
startupProbe:
{{- toYaml .Values.startupProbe | nindent 10 }}
{{- end }}
resources:
{{- include "myapp.resources" . | nindent 10 }}
{{- if .Values.volumeMounts }}
volumeMounts:
{{- toYaml .Values.volumeMounts | nindent 10 }}
{{- end }}
{{- if .Values.sidecars }}
{{- tpl (toYaml .Values.sidecars) . | nindent 6 }}
{{- end }}
{{- if .Values.volumes }}
volumes:
{{- toYaml .Values.volumes | nindent 8 }}
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{- toYaml .Values.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.affinity }}
affinity:
{{- toYaml .Values.affinity | nindent 8 }}
{{- else if .Values.podAntiAffinity.enabled }}
affinity:
podAntiAffinity:
{{- if eq .Values.podAntiAffinity.type "hard" }}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 16 }}
topologyKey: {{ .Values.podAntiAffinity.topologyKey }}
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 18 }}
topologyKey: {{ .Values.podAntiAffinity.topologyKey }}
{{- end }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{- toYaml .Values.tolerations | nindent 8 }}
{{- end }}
{{- if .Values.priorityClassName }}
priorityClassName: {{ .Values.priorityClassName }}
{{- end }}
{{- if .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml .Values.topologySpreadConstraints | nindent 8 }}
{{- end }}
◆ 3.1.2 生命周期Hooks示例
# 文件路径:myapp/templates/hooks/pre-install-job.yaml
# 安装前执行数据库迁移
{{- if .Values.hooks.preInstall.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-pre-install
namespace: {{ .Release.Namespace }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
app.kubernetes.io/component: pre-install-hook
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: {{ .Values.hooks.preInstall.backoffLimit | default 3 }}
template:
metadata:
name: {{ include "myapp.fullname" . }}-pre-install
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: pre-install-hook
spec:
restartPolicy: Never
serviceAccountName: {{ include "myapp.serviceAccountName" . }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: db-migration
image: {{ include "myapp.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/sh
- -c
- |
echo "Running database migrations..."
/app/migrate up
echo "Migration completed successfully"
env:
- name: DB_HOST
value: {{ include "myapp.fullname" . }}-postgresql
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: {{ .Values.postgresql.auth.database }}
- name: DB_USER
value: {{ .Values.postgresql.auth.username }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "myapp.fullname" . }}-postgresql
key: password
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
{{- end }}
---
# 文件路径:myapp/templates/hooks/post-upgrade-job.yaml
# 升级后执行缓存清理
{{- if .Values.hooks.postUpgrade.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-post-upgrade
namespace: {{ .Release.Namespace }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
app.kubernetes.io/component: post-upgrade-hook
annotations:
"helm.sh/hook": post-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: 2
template:
metadata:
name: {{ include "myapp.fullname" . }}-post-upgrade
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
restartPolicy: Never
containers:
- name: cache-clear
image: redis:7-alpine
command:
- redis-cli
- -h
- {{ include "myapp.fullname" . }}-redis-master
- FLUSHALL
env:
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: {{ include "myapp.fullname" . }}-redis
key: redis-password
{{- end }}
3.2 实际应用案例
◆ 案例一:多环境配置管理
场景描述:企业需要在开发、测试、预发布、生产四个环境部署同一应用,每个环境的配置差异较大(副本数、资源限制、域名、数据库连接等),需要通过values文件管理不同环境的配置。
实现代码:
# 文件路径:environments/dev-values.yaml
# 开发环境配置
replicaCount: 1
environment: development
logLevel: debug
image:
tag: develop # 使用develop分支镜像
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
ingress:
enabled: true
hosts:
- host: myapp-dev.internal.example.com
paths:
- path: /
tls: [] # 开发环境不使用TLS
# 开发环境使用外部数据库
postgresql:
enabled: false
externalDatabase:
host: dev-db.example.com
port: 5432
database: myapp_dev
username: dev_user
autoscaling:
enabled: false
monitoring:
enabled: false
# 文件路径:environments/prod-values.yaml
# 生产环境配置
replicaCount: 10
environment: production
logLevel: warn
image:
tag: v2.5.1 # 固定版本标签
resources:
requests:
cpu: 1000m
memory: 2Gi
limits:
cpu: 2000m
memory: 4Gi
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/rate-limit: "100"
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
# 生产环境使用HA PostgreSQL集群
postgresql:
enabled: true
architecture: replication
replication:
numSynchronousReplicas: 2
primary:
persistence:
size: 100Gi
storageClass: fast-ssd
resources:
requests:
cpu: 2000m
memory: 4Gi
readReplicas:
replicaCount: 2
persistence:
size: 100Gi
resources:
requests:
cpu: 1000m
memory: 2Gi
redis:
enabled: true
architecture: replication
master:
persistence:
size: 20Gi
replica:
replicaCount: 3
autoscaling:
enabled: true
minReplicas: 10
maxReplicas: 50
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
podDisruptionBudget:
enabled: true
minAvailable: 5
monitoring:
enabled: true
serviceMonitor:
enabled: true
interval: 30s
backup:
enabled: true
schedule: "0 2 * * *"
retention: 30
# 生产环境Pod拓扑分布约束
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: myapp
- maxSkew: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: myapp
# 部署到不同环境
# 开发环境
helm upgrade --install myapp ./myapp \
--namespace dev \
--create-namespace \
-f environments/dev-values.yaml \
--set externalDatabase.password=$DEV_DB_PASSWORD
# 生产环境
helm upgrade --install myapp ./myapp \
--namespace production \
--create-namespace \
-f environments/prod-values.yaml \
--set postgresql.auth.password=$PROD_DB_PASSWORD \
--set redis.auth.password=$PROD_REDIS_PASSWORD \
--wait --timeout 10m
运行结果:
部署结果对比:
开发环境:
- Pods: 1 replica
- Resources: 100m CPU, 128Mi Memory
- Database: External (dev-db.example.com)
- Monitoring: Disabled
- Ingress: http://myapp-dev.internal.example.com
生产环境:
- Pods: 10 replicas (HPA可扩展到50)
- Resources: 1000m CPU, 2Gi Memory
- Database: HA PostgreSQL cluster (1 primary + 2 replicas)
- Redis: Master-Replica (1 master + 3 replicas)
- Monitoring: Enabled (Prometheus ServiceMonitor)
- Ingress: https://myapp.example.com (TLS enabled)
- PodDisruptionBudget: Min 5 available
- TopologySpreadConstraints: 跨可用区均匀分布
◆ 案例二:Library Chart实现配置复用
场景描述:企业内部有多个微服务,都需要相似的Deployment、Service、Ingress配置。为避免重复定义,创建一个Library Chart封装通用模板,各微服务Chart通过依赖引用。
实现步骤:
- 创建Library Chart
# 创建library-common Chart
helm create library-common
rm -rf library-common/templates/* # 删除默认模板
# library-common/Chart.yaml
apiVersion: v2
name: library-common
description: Common library chart for microservices
type: library # 类型设置为library
version: 1.0.0
# library-common/templates/_deployment.tpl
# 通用Deployment模板
{{- define "common.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common.fullname" . }}
labels:
{{- include "common.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount | default 1 }}
selector:
matchLabels:
{{- include "common.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "common.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
ports:
- containerPort: {{ .Values.service.targetPort }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
{{- end -}}
# library-common/templates/_helpers.tpl
{{- define "common.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end -}}
{{- define "common.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "common.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
- 微服务Chart使用Library
# service-a/Chart.yaml
apiVersion: v2
name: service-a
version: 1.0.0
appVersion: "1.0.0"
dependencies:
- name: library-common
version: "1.0.0"
repository: "file://../library-common"
# service-a/templates/deployment.yaml
# 引用library的模板
{{ include "common.deployment" . }}
# service-a/values.yaml
image:
repository: mycompany/service-a
tag: "1.0.0"
replicaCount: 3
service:
targetPort: 8080
resources:
requests:
cpu: 200m
memory: 256Mi
- 更新依赖并部署
cd service-a
helm dependency update
helm template my-release . | head -50
helm install service-a-release .
四、最佳实践和注意事项
4.1 最佳实践
◆ 4.1.1 模板开发最佳实践
- 实践一:使用命名模板避免重复:将常用的配置片段(标签、选择器、镜像地址等)抽取为命名模板,通过
include引用
# 不推荐:重复定义
# deployment.yaml
labels:
app: myapp
version: v1
# service.yaml
labels:
app: myapp
version: v1
# 推荐:使用命名模板
# _helpers.tpl
{{- define "myapp.labels" -}}
app: myapp
version: v1
{{- end -}}
# deployment.yaml和service.yaml都使用
labels:
{{- include "myapp.labels" . | nindent 4 }}
- 实践二:善用管道符优化模板可读性:链式调用函数,使模板逻辑更清晰
# 不推荐:嵌套函数调用
{{ upper (trunc 63 (trimSuffix "-" .Release.Name)) }}
# 推荐:使用管道符
{{ .Release.Name | trimSuffix "-" | trunc 63 | upper }}
# 复杂示例:生成环境变量
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key | upper | replace "." "_" }}
value: {{ $value | quote }}
{{- end }}
- 实践三:在模板中添加验证逻辑:使用
fail函数验证必需配置,提前发现错误
{{- if not .Values.image.repository -}}
{{- fail "image.repository is required" -}}
{{- end -}}
{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) -}}
{{- fail "ingress.hosts must be set when ingress is enabled" -}}
{{- end -}}
{{- if semverCompare "<1.23.0" .Capabilities.KubeVersion.Version -}}
{{- fail (printf "Kubernetes version %s is not supported (requires >=1.23.0)" .Capabilities.KubeVersion.Version) -}}
{{- end -}}
◆ 4.1.2 依赖管理最佳实践
- 实践一:锁定依赖版本:使用
Chart.lock确保依赖版本一致性
# 生成Chart.lock文件
helm dependency update
# Chart.lock内容示例
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 12.1.9
digest: sha256:abc123...
# 在CI/CD中使用locked版本
helm dependency build # 使用Chart.lock中的版本
- 实践二:使用条件和标签灵活控制依赖:通过
condition和tags实现可选依赖
# Chart.yaml
dependencies:
- name: postgresql
condition: postgresql.enabled
tags:
- database
- name: mysql
condition: mysql.enabled
tags:
- database
# values.yaml
postgresql:
enabled: true
mysql:
enabled: false
tags:
database: true # 启用所有database标签的依赖
# 安装时动态控制
helm install myapp . --set postgresql.enabled=false --set mysql.enabled=true
- 实践三:合理使用import-values避免配置重复:从子Chart导入值到父Chart
# Chart.yaml
dependencies:
- name: postgresql
import-values:
- child: postgresql.auth.password
parent: database.password
- child: postgresql.primary.service.port
parent: database.port
# 现在可以在父Chart中使用
# {{ .Values.database.password }}
# {{ .Values.database.port }}
◆ 4.1.3 版本管理和发布
版本格式:MAJOR.MINOR.PATCH
MAJOR: 不兼容的API变更(如删除配置项、修改模板结构)
MINOR: 向下兼容的功能新增(如添加新配置项、新资源)
PATCH: 向下兼容的问题修复(如修复bug、优化文档)
示例:
1.0.0 -> 1.0.1 # 修复Ingress模板bug
1.0.1 -> 1.1.0 # 添加HPA支持
1.1.0 -> 2.0.0 # 移除旧的配置项,不兼容升级
- 使用values.schema.json验证配置:定义JSON Schema验证用户输入
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["image"],
"properties": {
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": {
"type": "string",
"minLength": 1
},
"tag": {
"type": "string",
"pattern": "^v?[0-9]+\\.[0-9]+\\.[0-9]+$"
}
}
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
}
4.2 注意事项
◆ 4.2.1 配置注意事项
⚠️ 警告:使用tpl函数动态渲染用户提供的值时,存在模板注入风险。应仅对可信来源的值使用tpl,或进行严格的输入验证。
- 注意事项一:引用顺序和范围:
include命名模板时,传递正确的上下文(.或自定义dict)
# 错误:缺少上下文
{{- include "myapp.labels" }} # 无法访问.Values
# 正确:传递完整上下文
{{- include "myapp.labels" . | nindent 4 }}
# 自定义上下文
{{- include "myapp.name.with.suffix" (dict "context" . "suffix" "worker") }}
- 注意事项二:YAML缩进问题:使用
nindent而非indent避免首行缩进错误
# 错误:使用indent会导致首行不缩进
labels:
{{ include "myapp.labels" . | indent 2 }}
# 渲染结果(错误):
# labels:
# app: myapp
# version: v1
# 正确:使用nindent
labels:
{{- include "myapp.labels" . | nindent 4 }}
# 渲染结果(正确):
# labels:
# app: myapp
# version: v1
- 注意事项三:Helm内置对象的作用域:理解
.Chart, .Release, .Values, .Capabilities等对象的可用范围
# .Chart: Chart.yaml的内容(全局可用)
{{ .Chart.Name }}
{{ .Chart.Version }}
# .Release: Release信息(全局可用)
{{ .Release.Name }}
{{ .Release.Namespace }}
# .Values: values.yaml的值(需正确传递上下文)
{{ .Values.replicaCount }}
# .Capabilities: Kubernetes集群能力
{{ .Capabilities.KubeVersion.Version }}
{{ .Capabilities.APIVersions.Has "autoscaling/v2" }}
# .Files: 访问Chart中的非模板文件
{{ .Files.Get "config/app.conf" }}
{{ .Files.Glob "config/*.yaml" }}
◆ 4.2.2 常见错误
| 错误现象 |
原因分析 |
解决方案 |
helm install失败:“unable to build kubernetes objects” |
1. 模板语法错误 2. YAML格式问题 3. 引用了不存在的值 |
1. 使用helm template预览渲染结果 2. 使用helm lint检查语法 3. 检查.Values引用是否存在,使用default提供默认值 |
| 依赖Chart未下载 |
执行helm install前未运行helm dependency update |
运行helm dependency update下载依赖到charts/目录 |
| 子Chart的值未生效 |
values.yaml中子Chart配置结构错误 |
确保子Chart配置以子Chart名称为顶级key,如postgresql.auth.password |
| 模板渲染空白或缺失资源 |
条件判断错误导致资源未渲染 |
检查if条件,使用helm template --debug查看详细信息 |
| Hooks执行失败 |
Hook Job因权限或配置问题失败 |
检查ServiceAccount权限,查看Job日志:kubectl logs job/<job-name> |
◆ 4.2.3 兼容性问题
-
版本兼容:
- Helm 2与Helm 3不兼容(Helm 3移除了Tiller),Chart
apiVersion应为v2
- 不同Kubernetes版本支持的API不同,使用
.Capabilities.APIVersions.Has检测API可用性
- 某些Helm函数在旧版本不可用,如
mustRegexMatch在Helm 3.7+才引入
-
平台兼容:
- Ingress API版本:Kubernetes 1.19+使用
networking.k8s.io/v1,旧版本使用networking.k8s.io/v1beta1
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" -}}
apiVersion: networking.k8s.io/v1
{{- else -}}
apiVersion: networking.k8s.io/v1beta1
{{- end -}}
- PodDisruptionBudget:Kubernetes 1.25+使用
policy/v1,旧版本使用policy/v1beta1
- 组件依赖:
- 子Chart的Kubernetes版本要求可能高于父Chart,确保集群版本满足所有依赖
- 某些Chart依赖特定的CRD(如cert-manager的Certificate),需提前安装
五、故障排查和监控
5.1 故障排查
◆ 5.1.1 日志查看
# 查看Helm操作历史
helm history my-release -n myapp
# 查看特定版本的配置
helm get values my-release -n myapp --revision 2
# 查看渲染后的manifest
helm get manifest my-release -n myapp
# 查看Hooks执行日志
kubectl logs -n myapp job/my-release-pre-install
# 查看Chart安装时的输出
helm install my-release ./myapp --debug --dry-run 2>&1 | tee install-debug.log
# 查看模板渲染详细过程
helm template my-release ./myapp --debug 2>&1 | less
◆ 5.1.2 常见问题排查
问题一:Chart安装后部分资源未创建
# 诊断命令
# 查看所有资源
kubectl get all -n myapp -l app.kubernetes.io/instance=my-release
# 对比预期和实际资源
helm template my-release ./myapp | kubectl apply --dry-run=client -f -
# 检查条件判断
helm template my-release ./myapp --show-only templates/ingress.yaml
解决方案:
- 检查
values.yaml中的条件开关(如ingress.enabled)
- 验证模板中的
if条件逻辑
- 使用
helm template --debug查看详细渲染过程
- 确认Kubernetes版本支持该资源类型
问题二:依赖Chart配置未生效
# 诊断命令
# 查看最终合并的values
helm get values my-release -n myapp --all
# 检查子Chart的默认值
helm show values bitnami/postgresql
# 验证values结构
cat values.yaml | yq eval '.postgresql' -
解决方案:
- 确保子Chart配置在
values.yaml中以子Chart名称为顶级key
- 检查
Chart.yaml中的import-values配置
- 使用
--set显式覆盖:helm install ... --set postgresql.auth.password=xxx
- 查看
Chart.lock确认依赖版本正确
问题三:模板函数报错或返回意外结果
- 症状:模板渲染失败,错误信息类似"error calling include: template xxx not defined"
- 排查:
# 检查_helpers.tpl中的定义
grep -n "define \"myapp.fullname\"" templates/_helpers.tpl
# 验证函数调用语法
helm template my-release ./myapp --show-only templates/deployment.yaml
# 测试特定函数
helm template my-release ./myapp --set image.tag=test | grep "image:"
- 解决:
- 确认命名模板已定义且名称匹配(区分大小写)
- 检查模板文件是否以
_开头(如_helpers.tpl不会被渲染为资源)
- 验证传递给
include的上下文是否正确
- 使用Helm内置函数库文档核对函数用法
◆ 5.1.3 调试模式
# 启用详细日志
export HELM_DEBUG=true
helm install my-release ./myapp --debug
# 渲染模板但不安装
helm install my-release ./myapp --dry-run --debug > dry-run.yaml
# 只渲染特定模板文件
helm template my-release ./myapp \
--show-only templates/deployment.yaml \
--show-only templates/service.yaml
# 设置特定值进行测试
helm template my-release ./myapp \
--set replicaCount=5 \
--set image.tag=debug \
--debug
# 使用不同的values文件
helm template my-release ./myapp \
-f ci/test-values.yaml \
--debug
# 验证Chart结构和语法
helm lint ./myapp --strict
# 使用kubeval验证Kubernetes资源
helm template my-release ./myapp | kubeval --strict
# 使用chart-testing工具全面测试
ct lint --chart-dirs . --charts myapp
ct install --chart-dirs . --charts myapp
5.2 性能监控
◆ 5.2.1 关键指标监控
# 监控Chart部署时间
time helm install my-release ./myapp
# 查看Release存储大小(Helm 3使用Secret存储)
kubectl get secrets -n myapp -l owner=helm
# 监控Hooks执行时间
kubectl get jobs -n myapp -l "helm.sh/hook"
kubectl describe job my-release-pre-install -n myapp
# 检查Chart包大小
helm package ./myapp
ls -lh myapp-*.tgz
# 统计Chart中的资源数量
helm template my-release ./myapp | grep "^kind:" | sort | uniq -c
# 分析Chart复杂度
find ./myapp/templates -name "*.yaml" -o -name "*.tpl" | xargs wc -l | sort -n
◆ 5.2.2 监控指标说明
| 指标名称 |
正常范围 |
告警阈值 |
说明 |
| Chart包大小 |
<5MB |
>20MB |
超大Chart会影响下载和部署速度 |
| 模板文件数量 |
<50 |
>100 |
过多模板文件影响可维护性 |
| 依赖Chart数量 |
<10 |
>20 |
过多依赖增加复杂度 |
| Helm部署时间 |
<2min |
>10min |
包含所有Hooks和资源创建的时间 |
| Hook Job执行时间 |
<5min |
>15min |
Pre/Post Hooks的执行时长 |
| values.yaml配置项 |
<200 |
>500 |
配置项过多难以理解和维护 |
◆ 5.2.3 监控告警配置
# Prometheus监控Helm部署指标(需安装helm-exporter)
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-monitoring-rules
data:
helm-rules.yaml: |
groups:
- name: helm_deployment
interval: 60s
rules:
# Helm部署失败告警
- alert: HelmDeploymentFailed
expr: helm_chart_info{status="failed"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Helm deployment failed: {{ $labels.release }}"
description: "Chart {{ $labels.chart }} deployment failed in namespace {{ $labels.namespace }}"
# Chart版本落后告警
- alert: HelmChartOutdated
expr: (time() - helm_chart_timestamp) > 86400 * 30
for: 1h
labels:
severity: warning
annotations:
summary: "Helm chart {{ $labels.chart }} is outdated"
description: "Chart has not been updated for 30+ days"
# 过多Helm releases
- alert: TooManyHelmReleases
expr: count(helm_chart_info) > 100
for: 10m
labels:
severity: warning
annotations:
summary: "Too many Helm releases in cluster"
description: "Total releases: {{ $value }}"
---
# Grafana Dashboard配置(JSON片段)
{
"dashboard": {
"title": "Helm Chart Monitoring",
"panels": [
{
"title": "Helm Releases by Status",
"targets": [
{
"expr": "count by (status) (helm_chart_info)"
}
]
},
{
"title": "Chart Deployment Duration",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(helm_install_duration_seconds_bucket[5m]))"
}
]
},
{
"title": "Failed Deployments",
"targets": [
{
"expr": "helm_chart_info{status=\"failed\"}"
}
]
}
]
}
}
5.3 备份与恢复
◆ 5.3.1 备份策略
#!/bin/bash
# Chart和Release备份脚本:helm-backup.sh
BACKUP_DIR="/backup/helm/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo "Starting Helm backup..."
echo "Backing up Chart sources..."
tar -czf "$BACKUP_DIR/charts-source.tar.gz" ./charts/
echo "Exporting Helm releases..."
for ns in $(kubectl get namespaces -o jsonpath='{.items.metadata.name}'); do
releases=$(helm list -n "$ns" -q)
if [ -n "$releases" ]; then
mkdir -p "$BACKUP_DIR/releases/$ns"
for release in $releases; do
echo "Exporting $ns/$release..."
# 导出values
helm get values "$release" -n "$ns" -o yaml > "$BACKUP_DIR/releases/$ns/$release-values.yaml"
# 导出manifest
helm get manifest "$release" -n "$ns" > "$BACKUP_DIR/releases/$ns/$release-manifest.yaml"
# 导出release元数据
helm get all "$release" -n "$ns" > "$BACKUP_DIR/releases/$ns/$release-all.yaml"
# 记录release信息
helm list -n "$ns" -o json | jq ".[] | select(.name==\"$release\")" > "$BACKUP_DIR/releases/$ns/$release-info.json"
done
fi
done
echo "Backing up Helm repositories..."
helm repo list -o yaml > "$BACKUP_DIR/repositories.yaml"
echo "Backing up cached charts..."
cp -r ~/.cache/helm/repository "$BACKUP_DIR/cached-charts" 2>/dev/null || true
# 生成备份清单
cat > "$BACKUP_DIR/backup-manifest.txt" <<EOF
Helm Backup Manifest
Date: $(date)
Backup Directory: $BACKUP_DIR
Helm Version: $(helm version --short)
Kubernetes Version: $(kubectl version --short 2>/dev/null | grep Server)
Releases Backed Up:
$(find "$BACKUP_DIR/releases" -name "*-info.json" | wc -l)
Namespaces:
$(ls "$BACKUP_DIR/releases" 2>/dev/null)
EOF
echo "Backup completed: $BACKUP_DIR"
# 可选:上传到对象存储
# aws s3 cp "$BACKUP_DIR" s3://my-backup-bucket/helm/ --recursive
◆ 5.3.2 恢复流程
- 恢复Chart仓库配置
RESTORE_DIR="/backup/helm/20250115-120000"
# 恢复仓库列表
while IFS= read -r repo; do
name=$(echo "$repo" | yq eval '.name' -)
url=$(echo "$repo" | yq eval '.url' -)
helm repo add "$name" "$url"
done < <(yq eval '.repositories[]' "$RESTORE_DIR/repositories.yaml")
helm repo update
- 恢复Helm releases
# 选择要恢复的namespace和release
NAMESPACE="production"
RELEASE="myapp"
# 方法1:使用原始values重新安装
helm install "$RELEASE" <chart-name> \
--namespace "$NAMESPACE" \
--create-namespace \
-f "$RESTORE_DIR/releases/$NAMESPACE/$RELEASE-values.yaml"
# 方法2:直接应用manifest(绕过Helm)
kubectl apply -f "$RESTORE_DIR/releases/$NAMESPACE/$RELEASE-manifest.yaml"
# 如果使用方法2,需要手动重建Helm release secret
# (高级操作,需要了解Helm内部机制)
- 验证恢复结果
# 验证Release状态
helm list -n "$NAMESPACE"
# 验证资源创建
kubectl get all -n "$NAMESPACE" -l app.kubernetes.io/instance="$RELEASE"
# 对比配置
diff <(helm get values "$RELEASE" -n "$NAMESPACE") "$RESTORE_DIR/releases/$NAMESPACE/$RELEASE-values.yaml"
# 运行测试
helm test "$RELEASE" -n "$NAMESPACE"
六、总结
6.1 技术要点回顾
- ✅ 模板函数是核心:通过命名模板(
define/include)实现代码复用,自定义函数封装复杂逻辑,掌握Sprig函数库提升开发效率,善用管道符和dict传递参数
- ✅ 依赖管理至关重要:理解
Chart.yaml的dependencies配置,使用condition和tags灵活控制子Chart,通过import-values实现值传递,利用Chart.lock锁定版本确保一致性
- ✅ 生命周期Hooks扩展能力:
pre-install/post-install等Hooks实现复杂部署流程,如数据库迁移、备份、缓存清理,合理设置hook-weight控制执行顺序
- ✅ 多环境配置管理:通过values文件分离环境配置,使用
values.schema.json验证输入,善用default函数提供默认值,避免配置爆炸
- ✅ 调试和测试工具链:
helm lint/template/install --dry-run进行验证,helm-unittest编写单元测试,chart-testing实现CI/CD集成,kubeval验证Kubernetes资源
- ✅ Library Chart促进复用:将通用模板抽取为library类型Chart,避免跨项目重复定义,构建企业级Chart库
6.2 进阶学习方向
-
Helm Operator和CRD开发:将Helm Chart封装为Kubernetes Operator,实现声明式管理
-
Chart安全加固和签名:使用Cosign、GPG签名Chart,实现供应链安全
-
复杂多集群部署编排:结合ArgoCD、FluxCD实现GitOps工作流
6.3 参考资料
附录
A. 命令速查表
# Chart开发
helm create mychart # 创建新Chart
helm lint mychart/ # 检查Chart语法
helm template my-release mychart/ # 渲染模板
helm package mychart/ # 打包Chart
# 依赖管理
helm dependency list # 查看依赖
helm dependency update # 下载依赖
helm dependency build # 使用Chart.lock构建
# 部署管理
helm install my-release mychart/ # 安装
helm upgrade my-release mychart/ # 升级
helm rollback my-release 1 # 回滚
helm uninstall my-release # 卸载
# 调试
helm install my-release mychart/ --dry-run --debug # 模拟安装
helm get values my-release # 查看配置
helm get manifest my-release # 查看资源
helm history my-release # 查看历史
# 仓库管理
helm repo add stable https://charts.helm.sh/stable # 添加仓库
helm repo update # 更新仓库索引
helm search repo nginx # 搜索Chart
helm pull stable/nginx # 下载Chart
# 测试
helm test my-release # 运行测试
helm-unittest mychart/ # 单元测试
B. 配置参数详解
Helm内置对象:
.Release.Name: Release名称(helm install时指定)
.Release.Namespace: 部署的命名空间
.Release.Service: 始终为"Helm"
.Release.IsUpgrade: 是否为升级操作(布尔值)
.Release.IsInstall: 是否为安装操作(布尔值)
.Chart: Chart.yaml的内容
.Values: values.yaml和用户提供的值合并结果
.Capabilities.KubeVersion: Kubernetes版本信息
.Capabilities.APIVersions: 集群支持的API版本列表
.Files: 访问Chart中的非模板文件
.Template: 当前模板文件信息
常用Sprig函数:
- 字符串:
upper, lower, trim, trunc, substr, replace
- 默认值:
default, empty, coalesce
- 逻辑:
and, or, not, eq, ne, lt, gt
- 列表:
list, append, concat, first, last, uniq, sortAlpha
- 字典:
dict, set, unset, hasKey, keys, values
- 类型转换:
toString, toJson, toYaml, fromJson, fromYaml
- 加密:
sha256sum, sha1sum, md5sum, bcrypt
- 日期:
now, date, dateModify
C. 术语表
| 术语 |
英文 |
解释 |
| Chart |
Helm Chart |
Helm的打包格式,包含Kubernetes资源模板和配置 |
| Release |
Helm Release |
Chart在Kubernetes集群中的一个实例化部署 |
| Repository |
Chart Repository |
Chart的存储和分发服务器,类似于Docker Registry |
| Values |
Values |
Chart的配置参数,通过values.yaml文件定义 |
| Template |
Template |
Go template格式的Kubernetes资源定义文件 |
| Hook |
Lifecycle Hook |
在Chart生命周期特定阶段执行的任务(如pre-install) |
| Dependency |
Chart Dependency |
Chart依赖的其他Chart(子Chart) |
| Library Chart |
Library Chart |
仅包含模板定义、不生成资源的特殊Chart类型 |
