Helm包管理实战
一、概述
1.1 背景介绍
直接用kubectl管理K8s资源,10个微服务就要维护几十个YAML文件,版本管理靠文件夹命名,回滚靠手动替换文件。Helm把一组相关的K8s资源打包成Chart,支持模板化、版本管理、一键部署和回滚,是K8s生态中事实上的包管理标准。
生产环境中的痛点:开发团队每次部署都要改十几个YAML里的镜像版本、副本数、资源限制,改漏一个就出问题。用Helm后,这些可变参数抽成values.yaml,部署时只需要helm upgrade -f prod-values.yaml一条命令,CI/CD流水线也更容易集成。
本文基于Helm v3.13.x版本,覆盖Chart开发、仓库管理、生产部署、回滚策略等内容。Helm v2已于2020年停止维护,不再讨论。
1.2 技术特点
模板引擎:基于Go template,支持变量、条件判断、循环、函数,一套模板适配多环境
Release管理:每次安装/升级都是一个Release,记录完整的版本历史,支持一键回滚到任意版本
依赖管理:Chart可以声明对其他Chart的依赖,自动拉取和安装子Chart
仓库生态:Artifact Hub上有数千个社区Chart,常用中间件(MySQL、Redis、Kafka)开箱即用
1.3 适用场景
场景一:多环境部署(dev/staging/prod),同一套Chart用不同的values文件区分配置
场景二:CI/CD流水线集成,Jenkins/GitLab CI中用helm命令自动化部署
场景三:复杂应用编排,一个Chart包含Deployment、Service、ConfigMap、Ingress等多个资源
场景四:第三方中间件部署,用社区Chart快速部署Prometheus、Grafana、Nginx Ingress等
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| Kubernetes | 1.24+ | Helm 3.13支持K8s 1.24-1.28 |
| Helm | 3.13+ | 当前稳定版本 |
| kubectl | 与集群版本匹配 | Helm依赖kubeconfig访问集群 |
| OCI Registry | Harbor 2.x / Docker Registry | 存储Helm Chart的OCI仓库 |
二、详细步骤
2.1 准备工作
2.1.1 安装Helm
# 方式一:官方脚本安装 curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # 方式二:手动下载 wget https://get.helm.sh/helm-v3.13.3-linux-amd64.tar.gz tar xzf helm-v3.13.3-linux-amd64.tar.gz mv linux-amd64/helm /usr/local/bin/helm # 验证 helm version # version.BuildInfo{Version:"v3.13.3", ...} # 配置命令补全 helm completion bash > /etc/bash_completion.d/helm source/etc/bash_completion.d/helm
2.1.2 配置Chart仓库
# 添加常用仓库 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo add grafana https://grafana.github.io/helm-charts helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx # 更新仓库索引 helm repo update # 查看已添加的仓库 helm repo list # 搜索Chart helm search repo nginx helm search repo mysql --versions # 查看所有版本 # 搜索Artifact Hub helm search hub prometheus
2.1.3 Helm基本操作
# 查看Chart信息 helm show chart bitnami/nginx helm show values bitnami/nginx # 查看默认values helm show readme bitnami/nginx # 安装Chart helm install my-nginx bitnami/nginx -n web --create-namespace # 查看Release helm list -A helm status my-nginx -n web # 查看Release历史 helmhistorymy-nginx -n web # 升级Release helm upgrade my-nginx bitnami/nginx -n web --setreplicaCount=3 # 回滚 helm rollback my-nginx 1 -n web # 回滚到版本1 # 卸载 helm uninstall my-nginx -n web
2.2 核心配置
2.2.1 创建自定义Chart
# 创建Chart骨架 helm create myapp # 目录结构 # myapp/ # ├── Chart.yaml # Chart元数据 # ├── values.yaml # 默认配置值 # ├── charts/ # 依赖Chart # ├── templates/ # 模板文件 # │ ├── deployment.yaml # │ ├── service.yaml # │ ├── ingress.yaml # │ ├── hpa.yaml # │ ├── serviceaccount.yaml # │ ├── configmap.yaml # │ ├── _helpers.tpl # 模板辅助函数 # │ ├── NOTES.txt # 安装后提示信息 # │ └── tests/ # │ └── test-connection.yaml # └── .helmignore # 打包时忽略的文件
Chart.yaml定义:
# 文件:myapp/Chart.yaml apiVersion:v2 name:myapp description:ProductionbackendAPIservice type:application version:1.0.0 appVersion:"2.1.0" keywords: -api -backend maintainers: -name:ops-team email:ops@company.com dependencies: -name:redis version:"18.x.x" repository:"https://charts.bitnami.com/bitnami" condition:redis.enabled -name:postgresql version:"13.x.x" repository:"https://charts.bitnami.com/bitnami" condition:postgresql.enabled
2.2.2 编写values.yaml
# 文件:myapp/values.yaml # 镜像配置 image: repository:registry.company.com/backend/myapp tag:""# 默认使用Chart的appVersion pullPolicy:IfNotPresent imagePullSecrets: -name:registry-secret # 副本数 replicaCount:3 # 资源限制 resources: requests: cpu:200m memory:256Mi limits: cpu:"1" memory:1Gi # Service配置 service: type:ClusterIP port:8080 # Ingress配置 ingress: enabled:true className:nginx annotations: nginx.ingress.kubernetes.io/proxy-body-size:"50m" nginx.ingress.kubernetes.io/proxy-read-timeout:"60" hosts: -host:api.company.com paths: -path:/ pathType:Prefix tls: -secretName:api-tls hosts: -api.company.com # HPA自动伸缩 autoscaling: enabled:true minReplicas:3 maxReplicas:20 targetCPUUtilizationPercentage:70 targetMemoryUtilizationPercentage:80 # 健康检查 livenessProbe: httpGet: path:/health port:http initialDelaySeconds:30 periodSeconds:10 failureThreshold:3 readinessProbe: httpGet: path:/ready port:http initialDelaySeconds:10 periodSeconds:5 failureThreshold:3 # 环境变量 env: -name:APP_ENV value:"production" -name:LOG_LEVEL value:"info" -name:DB_HOST valueFrom: secretKeyRef: name:myapp-db-secret key:host # ConfigMap数据 config: app.conf:| server.port=8080 server.graceful-shutdown=30s cache.ttl=300 # 持久化存储 persistence: enabled:false storageClass:"ceph-rbd" size:10Gi # Pod调度 nodeSelector:{} tolerations:[] affinity:{} topologySpreadConstraints: -maxSkew:1 topologyKey:topology.kubernetes.io/zone whenUnsatisfiable:DoNotSchedule labelSelector: matchLabels: app.kubernetes.io/name:myapp # 依赖服务开关 redis: enabled:true architecture:standalone auth: password:"redis-password" postgresql: enabled:false
2.2.3 编写模板文件
Deployment模板:
# 文件:myapp/templates/deployment.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:{{include"myapp.fullname".}}
labels:
{{-include"myapp.labels".|nindent4}}
spec:
{{-ifnot.Values.autoscaling.enabled}}
replicas:{{.Values.replicaCount}}
{{-end}}
selector:
matchLabels:
{{-include"myapp.selectorLabels".|nindent6}}
strategy:
type:RollingUpdate
rollingUpdate:
maxSurge:25%
maxUnavailable:0
template:
metadata:
annotations:
checksum/config:{{include(print$.Template.BasePath"/configmap.yaml").|sha256sum}}
labels:
{{-include"myapp.selectorLabels".|nindent8}}
spec:
{{-with.Values.imagePullSecrets}}
imagePullSecrets:
{{-toYaml.|nindent8}}
{{-end}}
serviceAccountName:{{include"myapp.serviceAccountName".}}
securityContext:
runAsNonRoot:true
runAsUser:1000
fsGroup:1000
containers:
-name:{{.Chart.Name}}
image:"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy:{{.Values.image.pullPolicy}}
ports:
-name:http
containerPort:{{.Values.service.port}}
protocol:TCP
{{-with.Values.env}}
env:
{{-toYaml.|nindent12}}
{{-end}}
{{-with.Values.livenessProbe}}
livenessProbe:
{{-toYaml.|nindent12}}
{{-end}}
{{-with.Values.readinessProbe}}
readinessProbe:
{{-toYaml.|nindent12}}
{{-end}}
resources:
{{-toYaml.Values.resources|nindent12}}
volumeMounts:
-name:config
mountPath:/app/config
readOnly:true
{{-if.Values.persistence.enabled}}
-name:data
mountPath:/app/data
{{-end}}
volumes:
-name:config
configMap:
name:{{include"myapp.fullname".}}-config
{{-if.Values.persistence.enabled}}
-name:data
persistentVolumeClaim:
claimName:{{include"myapp.fullname".}}-data
{{-end}}
{{-with.Values.nodeSelector}}
nodeSelector:
{{-toYaml.|nindent8}}
{{-end}}
{{-with.Values.tolerations}}
tolerations:
{{-toYaml.|nindent8}}
{{-end}}
{{-with.Values.topologySpreadConstraints}}
topologySpreadConstraints:
{{-toYaml.|nindent8}}
{{-end}}
说明:
checksum/config注解:ConfigMap内容变化时自动触发Pod滚动更新,不加这个的话改了ConfigMap但Pod不会重启
maxUnavailable: 0:滚动更新时不允许有Pod不可用,保证零停机部署
securityContext:以非root用户运行,生产安全基线
辅助函数模板:
# 文件:myapp/templates/_helpers.tpl
{{/*
生成应用全名
*/}}
{{-define"myapp.fullname"-}}
{{-if.Values.fullnameOverride}}
{{-.Values.fullnameOverride|trunc63|trimSuffix"-"}}
{{-else}}
{{-$name:=default.Chart.Name.Values.nameOverride}}
{{-ifcontains$name.Release.Name}}
{{-.Release.Name|trunc63|trimSuffix"-"}}
{{-else}}
{{-printf"%s-%s".Release.Name$name|trunc63|trimSuffix"-"}}
{{-end}}
{{-end}}
{{-end}}
{{/*
通用标签
*/}}
{{-define"myapp.labels"-}}
helm.sh/chart:{{include"myapp.chart".}}
{{include"myapp.selectorLabels".}}
app.kubernetes.io/version:{{.Chart.AppVersion|quote}}
app.kubernetes.io/managed-by:{{.Release.Service}}
{{-end}}
{{/*
选择器标签
*/}}
{{-define"myapp.selectorLabels"-}}
app.kubernetes.io/name:{{include"myapp.name".}}
app.kubernetes.io/instance:{{.Release.Name}}
{{-end}}
{{/*
Chart名称
*/}}
{{-define"myapp.name"-}}
{{-default.Chart.Name.Values.nameOverride|trunc63|trimSuffix"-"}}
{{-end}}
{{/*
Chart版本标签
*/}}
{{-define"myapp.chart"-}}
{{-printf"%s-%s".Chart.Name.Chart.Version|replace"+""_"|trunc63|trimSuffix"-"}}
{{-end}}
{{/*
ServiceAccount名称
*/}}
{{-define"myapp.serviceAccountName"-}}
{{-default(include"myapp.fullname".).Values.serviceAccount.name}}
{{-end}}
2.2.4 多环境values文件
# 文件:values-dev.yaml(开发环境覆盖)
replicaCount:1
image:
tag:"latest"
pullPolicy:Always
resources:
requests:
cpu:100m
memory:128Mi
limits:
cpu:500m
memory:512Mi
ingress:
enabled:true
hosts:
-host:api-dev.company.com
paths:
-path:/
pathType:Prefix
tls:[]
autoscaling:
enabled:false
env:
-name:APP_ENV
value:"development"
-name:LOG_LEVEL
value:"debug"
# 文件:values-prod.yaml(生产环境覆盖) replicaCount:5 image: tag:"2.1.0" resources: requests: cpu:500m memory:512Mi limits: cpu:"2" memory:2Gi autoscaling: enabled:true minReplicas:5 maxReplicas:30 env: -name:APP_ENV value:"production" -name:LOG_LEVEL value:"warn"
2.2.5 Chart打包和发布
# 更新依赖 helm dependency update myapp/ helm dependency build myapp/ # 模板渲染测试(不实际部署,只看生成的YAML) helm template myapp-release myapp/ -f values-prod.yaml -n production # Lint检查 helm lint myapp/ # 打包 helm package myapp/ # 输出:myapp-1.0.0.tgz # 推送到OCI仓库(Harbor) helm push myapp-1.0.0.tgz oci://harbor.company.com/helm-charts # 推送到ChartMuseum curl --data-binary"@myapp-1.0.0.tgz"http://chartmuseum.company.com/api/charts
2.3 启动和验证
2.3.1 部署到各环境
# 部署到开发环境 helm upgrade --install myapp-dev myapp/ -f values-dev.yaml -n dev --create-namespace --wait--timeout 5m # 部署到生产环境 helm upgrade --install myapp-prod myapp/ -f values-prod.yaml -n production --create-namespace --wait--timeout 10m --atomic # --atomic:部署失败自动回滚到上一个版本 # --wait:等待所有Pod Ready才算成功 # --timeout:超时时间,超时视为失败
2.3.2 验证部署
# 查看Release状态 helm status myapp-prod -n production # 查看实际生成的资源 helm get manifest myapp-prod -n production # 查看使用的values helm get values myapp-prod -n production # 查看所有信息 helm get all myapp-prod -n production # 验证Pod运行 kubectl get pods -n production -l app.kubernetes.io/name=myapp kubectl get svc -n production -l app.kubernetes.io/name=myapp kubectl get ingress -n production
2.3.3 版本管理和回滚
# 查看Release历史 helmhistorymyapp-prod -n production # REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION # 1 2024-01-15 1000 superseded myapp-1.0.0 2.0.0 Install complete # 2 2024-01-20 1400 superseded myapp-1.1.0 2.1.0 Upgrade complete # 3 2024-01-25 0900 deployed myapp-1.2.0 2.2.0 Upgrade complete # 回滚到指定版本 helm rollback myapp-prod 2 -n production --wait # 查看两个版本的差异 helm diff upgrade myapp-prod myapp/ -f values-prod.yaml -n production # 需要安装helm-diff插件:helm plugin install https://github.com/databus23/helm-diff
三、示例代码和配置
3.1 完整配置示例
3.1.1 Ingress模板
# 文件:myapp/templates/ingress.yaml
{{-if.Values.ingress.enabled-}}
apiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
name:{{include"myapp.fullname".}}
labels:
{{-include"myapp.labels".|nindent4}}
{{-with.Values.ingress.annotations}}
annotations:
{{-toYaml.|nindent4}}
{{-end}}
spec:
{{-if.Values.ingress.className}}
ingressClassName:{{.Values.ingress.className}}
{{-end}}
{{-if.Values.ingress.tls}}
tls:
{{-range.Values.ingress.tls}}
-hosts:
{{-range.hosts}}
-{{.|quote}}
{{-end}}
secretName:{{.secretName}}
{{-end}}
{{-end}}
rules:
{{-range.Values.ingress.hosts}}
-host:{{.host|quote}}
http:
paths:
{{-range.paths}}
-path:{{.path}}
pathType:{{.pathType}}
backend:
service:
name:{{include"myapp.fullname"$}}
port:
number:{{$.Values.service.port}}
{{-end}}
{{-end}}
{{-end}}
3.1.2 HPA模板
# 文件:myapp/templates/hpa.yaml
{{-if.Values.autoscaling.enabled}}
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:{{include"myapp.fullname".}}
labels:
{{-include"myapp.labels".|nindent4}}
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:{{include"myapp.fullname".}}
minReplicas:{{.Values.autoscaling.minReplicas}}
maxReplicas:{{.Values.autoscaling.maxReplicas}}
metrics:
{{-if.Values.autoscaling.targetCPUUtilizationPercentage}}
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:{{.Values.autoscaling.targetCPUUtilizationPercentage}}
{{-end}}
{{-if.Values.autoscaling.targetMemoryUtilizationPercentage}}
-type:Resource
resource:
name:memory
target:
type:Utilization
averageUtilization:{{.Values.autoscaling.targetMemoryUtilizationPercentage}}
{{-end}}
behavior:
scaleDown:
stabilizationWindowSeconds:300
policies:
-type:Percent
value:10
periodSeconds:60
scaleUp:
stabilizationWindowSeconds:30
policies:
-type:Percent
value:50
periodSeconds:60
-type:Pods
value:4
periodSeconds:60
selectPolicy:Max
{{-end}}
注意:HPA的scaleDown.stabilizationWindowSeconds: 300表示缩容前等待5分钟,防止流量波动导致频繁缩扩容。生产环境这个值不要设太小,实测设为60秒时一天内缩扩容了200多次。
3.1.3 CI/CD集成脚本
#!/bin/bash
# 文件:deploy.sh
# GitLab CI/Jenkins中调用的部署脚本
set-euo pipefail
# 参数
RELEASE_NAME="${1:?Usage: deploy.sh }"
NAMESPACE="${2:?}"
ENV="${3:?}"
CHART_PATH="./helm/myapp"
VALUES_FILE="./helm/values-${ENV}.yaml"
IMAGE_TAG="${CI_COMMIT_SHORT_SHA:-latest}"
echo"=== Deploying${RELEASE_NAME}to${NAMESPACE}(${ENV}) ==="
echo"Image tag:${IMAGE_TAG}"
# Lint检查
helm lint"${CHART_PATH}"-f"${VALUES_FILE}"
# Dry-run验证
helm upgrade --install"${RELEASE_NAME}""${CHART_PATH}"
-f"${VALUES_FILE}"
-n"${NAMESPACE}"
--setimage.tag="${IMAGE_TAG}"
--dry-run
# 实际部署
helm upgrade --install"${RELEASE_NAME}""${CHART_PATH}"
-f"${VALUES_FILE}"
-n"${NAMESPACE}"--create-namespace
--setimage.tag="${IMAGE_TAG}"
--wait--timeout 10m
--atomic
--history-max 10
echo"=== Deployment completed ==="
helm status"${RELEASE_NAME}"-n"${NAMESPACE}"
3.2 实际应用案例
案例一:用Helm部署Prometheus监控栈
场景描述:用kube-prometheus-stack Chart一键部署Prometheus + Grafana + AlertManager,替代手动维护几十个YAML文件。
实现代码:
# 添加仓库 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update
# 文件:prometheus-values.yaml
prometheus:
prometheusSpec:
retention:30d
storageSpec:
volumeClaimTemplate:
spec:
storageClassName:ceph-rbd
accessModes:["ReadWriteOnce"]
resources:
requests:
storage:100Gi
resources:
requests:
cpu:"1"
memory:2Gi
limits:
cpu:"2"
memory:4Gi
grafana:
adminPassword:"your-secure-password"
persistence:
enabled:true
storageClassName:ceph-rbd
size:10Gi
ingress:
enabled:true
ingressClassName:nginx
hosts:
-grafana.company.com
tls:
-secretName:grafana-tls
hosts:
-grafana.company.com
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
storageClassName:ceph-rbd
accessModes:["ReadWriteOnce"]
resources:
requests:
storage:10Gi
config:
route:
receiver:'slack-notifications'
group_by:['alertname','namespace']
group_wait:30s
group_interval:5m
repeat_interval:4h
receivers:
-name:'slack-notifications'
slack_configs:
-api_url:'https://hooks.slack.com/services/xxx/yyy/zzz'
channel:'#alerts'
send_resolved:true
# 部署 helm upgrade --install prometheus prometheus-community/kube-prometheus-stack -f prometheus-values.yaml -n monitoring --create-namespace --wait--timeout 10m # 验证 kubectl get pods -n monitoring helm list -n monitoring
案例二:Helm Hooks实现数据库迁移
场景描述:应用升级时需要先执行数据库Schema迁移,迁移成功后才部署新版本。用Helm的pre-upgrade Hook实现。
实现代码:
# 文件:myapp/templates/db-migration-job.yaml
apiVersion:batch/v1
kind:Job
metadata:
name:{{include"myapp.fullname".}}-db-migrate
labels:
{{-include"myapp.labels".|nindent4}}
annotations:
"helm.sh/hook":pre-upgrade
"helm.sh/hook-weight":"-5"
"helm.sh/hook-delete-policy":before-hook-creation,hook-succeeded
spec:
backoffLimit:3
activeDeadlineSeconds:300
template:
spec:
restartPolicy:Never
containers:
-name:migrate
image:"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command:["./migrate","--direction=up"]
env:
-name:DB_HOST
valueFrom:
secretKeyRef:
name:myapp-db-secret
key:host
-name:DB_PASSWORD
valueFrom:
secretKeyRef:
name:myapp-db-secret
key:password
resources:
requests:
cpu:100m
memory:128Mi
说明:
helm.sh/hook: pre-upgrade:在upgrade之前执行
helm.sh/hook-weight: "-5":多个Hook时按weight排序,数字小的先执行
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded:下次执行前删除旧Job,成功后也删除
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
限制Release历史数量:每次upgrade都会在K8s Secret中存储一份Release记录,默认不限制。集群运行一年后,频繁部署的服务可能积累几百个历史版本,占用etcd空间。
# 部署时限制历史版本数 helm upgrade --install myapp ./myapp -n production --history-max 10 # 清理已有的旧版本 helmhistorymyapp -n production # 手动无法直接删除历史,只能通过--history-max在下次upgrade时自动清理
使用OCI Registry替代ChartMuseum:Helm 3.8+原生支持OCI格式存储Chart,Harbor 2.x直接支持。OCI Registry比ChartMuseum性能更好,且不需要额外维护一个服务。
# 登录OCI仓库 helm registry login harbor.company.com # 推送Chart helm push myapp-1.0.0.tgz oci://harbor.company.com/helm-charts # 从OCI仓库安装 helm install myapp oci://harbor.company.com/helm-charts/myapp --version 1.0.0
模板渲染缓存:大型Chart(50+模板文件)的渲染时间可能超过10秒,CI/CD中频繁的helm template会拖慢流水线。把渲染结果缓存,只在Chart或values变化时重新渲染。
4.1.2 安全加固
values中不存储敏感信息:数据库密码、API Key等不要写在values.yaml中,用External Secrets Operator或Sealed Secrets从外部密钥管理系统注入。
# 不推荐:密码明文写在values中 database: password:"my-secret-password" # 推荐:引用已存在的Secret env: -name:DB_PASSWORD valueFrom: secretKeyRef: name:myapp-db-secret# 由External Secrets创建 key:password
Chart签名验证:用GPG签名Chart包,安装时验证签名防止篡改。
# 签名 helm package --sign --key'ops-team'--keyring ~/.gnupg/secring.gpg myapp/ # 验证 helm verify myapp-1.0.0.tgz --keyring ~/.gnupg/pubring.gpg # 安装时验证 helm install myapp myapp-1.0.0.tgz --verify --keyring ~/.gnupg/pubring.gpg
RBAC限制Helm操作权限:不同团队只能在自己的namespace中操作Helm Release,通过K8s RBAC限制ServiceAccount权限。
4.1.3 高可用配置
HA方案一:--atomic参数保证部署失败自动回滚,不会出现半部署状态
HA方案二:GitOps模式(ArgoCD + Helm),Chart和values存储在Git仓库,ArgoCD自动同步,避免手动操作失误
备份策略:Chart源码纳入Git版本管理,Release历史保留10个版本,values文件按环境分别管理
4.2 注意事项
4.2.1 配置注意事项
警告:Helm upgrade操作会直接修改集群资源,生产环境务必先dry-run确认变更内容。
注意helm upgrade默认会合并新旧values,不是完全替换。如果旧版本有个参数新版本删掉了,upgrade后旧参数仍然存在。用--reset-values可以只使用新的values,但要确保新values是完整的。
注意helm install和helm upgrade是两个不同的命令,用helm upgrade --install可以合并为一个,首次执行是install,后续是upgrade。CI/CD中统一用这个命令。
注意Chart的version(Chart版本)和appVersion(应用版本)是两个独立的字段。Chart模板改了要升version,只改镜像tag不需要升version但建议升appVersion。
4.2.2 常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| Error: UPGRADE FAILED: another operation is in progress | 上次操作未完成或异常中断 |
helm rollback |
| Error: rendered manifests contain a resource that already exists | 资源已存在但不属于当前Release | 给已有资源添加Helm标签:app.kubernetes.io/managed-by: Helm |
| 模板渲染报nil pointer evaluating interface | values中缺少必要字段 | 模板中用{{ default "" .Values.xxx }}或{{- if .Values.xxx }}做空值判断 |
| upgrade后Pod没有更新 | ConfigMap/Secret内容变了但Pod没重启 | 在Deployment模板中加checksum/config注解 |
| 依赖Chart下载失败 | 仓库不可达或版本不存在 | helm repo update 更新索引;检查Chart.yaml中的依赖版本 |
4.2.3 兼容性问题
版本兼容:Helm 3.13.x支持K8s 1.24-1.28,不同Helm版本生成的Release Secret格式可能不同
平台兼容:Helm CLI支持Linux/macOS/Windows,但Windows下路径分隔符可能导致模板渲染问题
组件依赖:helm-diff插件版本需要和Helm版本匹配;ArgoCD的Helm支持有版本要求
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
# 查看Helm操作日志(增加debug输出) helm upgrade --install myapp ./myapp -n production --debug 2>&1 | tee helm-debug.log # 查看Release的manifest helm get manifest myapp -n production # 查看Release的hooks helm get hooks myapp -n production # 查看Release的notes helm get notes myapp -n production # 对比当前Release和Chart的差异 helm diff upgrade myapp ./myapp -f values-prod.yaml -n production
5.1.2 常见问题排查
问题一:upgrade卡住不动,超时失败
# 诊断命令 helm status myapp -n production kubectl get pods -n production -l app.kubernetes.io/name=myapp kubectl describe pod-n production
解决方案:
Pod镜像拉取失败:检查镜像地址和imagePullSecrets
Pod资源不足:检查节点可用资源
readinessProbe失败:检查健康检查路径和端口
Hook Job失败:kubectl logs job/
问题二:Release状态为failed,无法upgrade
# 诊断命令 helmhistorymyapp -n production helm status myapp -n production
解决方案:
# 方式一:回滚到上一个成功版本 helm rollback myapp-n production # 方式二:强制替换(谨慎使用) helm upgrade --install myapp ./myapp -n production --force # 方式三:卸载后重装(会短暂中断服务) helm uninstall myapp -n production helm install myapp ./myapp -f values-prod.yaml -n production
问题三:模板渲染错误
症状:helm upgrade报模板语法错误
排查:
# 本地渲染模板,查看具体错误 helm template myapp ./myapp -f values-prod.yaml --debug # 只渲染特定模板 helm template myapp ./myapp -s templates/deployment.yaml -f values-prod.yaml
解决:检查Go template语法,常见错误是缩进不对、变量名拼写错误、缺少{{- end }}
5.1.3 调试模式
# Helm debug模式 helm install myapp ./myapp --debug --dry-run -n production -f values-prod.yaml # 查看Helm使用的kubeconfig helm env # 查看Helm缓存 ls ~/.cache/helm/ # 查看Helm插件 helm plugin list # 验证Chart结构 helm lint ./myapp --strict
5.2 性能监控
5.2.1 关键指标监控
# 查看所有Release状态 helm list -A --all # 查看failed的Release helm list -A --failed # 查看pending的Release helm list -A --pending # 统计各namespace的Release数量 helm list -A -o json | jq -r'.[].namespace'| sort | uniq -c | sort -rn
5.2.2 监控指标说明
| 指标名称 | 正常范围 | 告警阈值 | 说明 |
|---|---|---|---|
| Release状态 | deployed | failed/pending | failed需要排查,pending说明操作卡住 |
| Release历史版本数 | <10 | >20 | 过多历史版本占用etcd空间 |
| 部署耗时 | <5分钟 | >10分钟 | 超时通常是Pod启动慢或镜像拉取慢 |
| Hook执行时间 | <2分钟 | >5分钟 | 数据库迁移等Hook不应该太慢 |
| Chart Lint警告数 | 0 | >0 | 警告可能导致部署异常 |
5.2.3 Prometheus监控规则
# 文件:helm-alerts.yaml
# 通过kube-state-metrics监控Helm管理的资源
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:helm-release-alerts
namespace:monitoring
spec:
groups:
-name:helm-releases
rules:
-alert:HelmReleaseDeploymentUnavailable
expr:|
kube_deployment_status_replicas_unavailable{namespace=~"production|staging"}
* on(deployment, namespace) group_left()
label_replace(
kube_deployment_labels{label_app_kubernetes_io_managed_by="Helm"},
"deployment", "$1", "deployment", "(.*)"
) > 0
for:10m
labels:
severity:warning
annotations:
summary:"Helm-managed deployment{{ $labels.deployment }}has unavailable replicas"
-alert:HelmReleasePodCrashLooping
expr:|
increase(kube_pod_container_status_restarts_total{namespace=~"production|staging"}[1h]) > 5
for:5m
labels:
severity:warning
annotations:
summary:"Pod{{ $labels.pod }}in{{ $labels.namespace }}is crash looping"
5.3 备份与恢复
5.3.1 备份策略
#!/bin/bash
# Helm Release备份脚本
# 文件:/opt/scripts/helm-backup.sh
set-euo pipefail
BACKUP_DIR="/data/helm-backup/$(date +%Y%m%d)"
mkdir -p"${BACKUP_DIR}"
# 备份所有Release的values和manifest
fornsin$(kubectl get ns -o jsonpath='{.items[*].metadata.name}');do
forreleasein$(helm list -n"$ns"-q 2>/dev/null);do
mkdir -p"${BACKUP_DIR}/${ns}"
helm get values"$release"-n"$ns"-o yaml >"${BACKUP_DIR}/${ns}/${release}-values.yaml"2>/dev/null ||true
helm get manifest"$release"-n"$ns">"${BACKUP_DIR}/${ns}/${release}-manifest.yaml"2>/dev/null ||true
echo"[$(date)] Backed up:${ns}/${release}"
done
done
# 压缩
tar czf"/data/helm-backup/helm-backup-$(date +%Y%m%d).tar.gz"-C"/data/helm-backup""$(date +%Y%m%d)"
rm -rf"${BACKUP_DIR}"
echo"[$(date)] Helm backup completed"
5.3.2 恢复流程
停止服务:通知相关团队准备维护窗口
恢复数据:helm upgrade --install
验证完整性:helm status
重启服务:确认所有Pod Running
六、总结
6.1 技术要点回顾
要点一:helm upgrade --install统一安装和升级操作,配合--atomic保证部署失败自动回滚
要点二:values.yaml按环境拆分(dev/staging/prod),敏感信息不写在values中,用External Secrets注入
要点三:Deployment模板中加checksum/config注解,ConfigMap变更时自动触发Pod滚动更新
要点四:--history-max 10限制Release历史版本数量,避免占用过多etcd空间
要点五:Chart源码纳入Git版本管理,CI/CD中用helm lint和--dry-run做部署前验证
6.2 进阶学习方向
Helmfile多Release编排:管理多个Helm Release的声明式工具,一个helmfile.yaml定义整个环境的所有服务
学习资源:Helmfile GitHub
实践建议:从单个环境开始,逐步扩展到多环境管理
ArgoCD + Helm GitOps:用ArgoCD监控Git仓库中的Chart和values变更,自动同步到集群
学习资源:ArgoCD Helm支持
实践建议:先在staging环境实践GitOps流程
自定义Chart Library:把公司通用的模板抽成Library Chart,业务Chart引用Library减少重复代码
6.3 参考资料
Helm官方文档- 完整的Helm使用指南
Chart开发指南- 模板语法详解
Artifact Hub- Helm Chart公共仓库
helm-diff插件- Release差异对比工具
附录
A. 命令速查表
# 仓库管理 helm repo add# 添加仓库 helm repo update # 更新仓库索引 helm repo list # 查看仓库列表 helm search repo # 搜索Chart # Release管理 helm install -n # 安装 helm upgrade --install -n # 安装或升级 helm upgrade -n -f values.yaml # 升级 helm rollback -n # 回滚 helm uninstall -n # 卸载 helm list -A # 查看所有Release helmhistory -n # 查看历史 helm status -n # 查看状态 # Chart开发 helm create # 创建Chart骨架 helm lint # 语法检查 helm template # 本地渲染模板 helm package # 打包 helm push # 推送到OCI仓库 helm dependency update # 更新依赖 # 调试 helm get values -n # 查看values helm get manifest -n # 查看manifest helm get all -n # 查看所有信息 helm diff upgrade # 差异对比(需要插件)
B. 配置参数详解
helm upgrade常用参数:
| 参数 | 说明 | 建议 |
|---|---|---|
| --install | 不存在时自动install | CI/CD中必用 |
| --atomic | 失败自动回滚 | 生产环境必用 |
| --wait | 等待Pod Ready | 生产环境必用 |
| --timeout | 超时时间 | 生产设10m,开发设5m |
| --history-max | 历史版本上限 | 建议10 |
| --dry-run | 模拟执行 | 部署前验证 |
| --debug | 调试输出 | 排查问题时用 |
| --force | 强制替换资源 | 谨慎使用 |
| --reset-values | 不合并旧values | 完整values时使用 |
| --reuse-values | 复用旧values | 只改个别参数时使用 |
| -f | 指定values文件 | 可多次使用,后面覆盖前面 |
| --set | 命令行设置值 | 优先级最高 |
Helm Hook类型:
| Hook | 执行时机 | 典型用途 |
|---|---|---|
| pre-install | install前 | 创建前置资源 |
| post-install | install后 | 发送通知 |
| pre-upgrade | upgrade前 | 数据库迁移 |
| post-upgrade | upgrade后 | 清理缓存 |
| pre-delete | uninstall前 | 数据备份 |
| post-delete | uninstall后 | 清理外部资源 |
| pre-rollback | rollback前 | 数据库回滚 |
| post-rollback | rollback后 | 通知 |
| test | helm test时 | 连通性测试 |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| Chart | - | Helm的包格式,包含K8s资源模板和配置 |
| Release | - | Chart的一次安装实例,每次install/upgrade产生新版本 |
| Repository | - | Chart仓库,存储和分发Chart包 |
| Values | - | Chart的配置参数,通过values.yaml或--set传入 |
| Template | - | Go template格式的K8s资源模板 |
| Hook | - | 在Release生命周期特定阶段执行的操作 |
| Dependency | - | Chart对其他Chart的依赖关系 |
| OCI | Open Container Initiative | 容器镜像标准,Helm 3.8+支持OCI格式存储Chart |
-
镜像
+关注
关注
0文章
182浏览量
11716 -
微服务
+关注
关注
0文章
151浏览量
8150
原文标题:别再手工改 YAML:Helm 包管理与模板化部署全解
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
【「仓颉编程快速上手」阅读体验】简洁包管理的命脉
HarmonyOS5云服务技术分享--Serverless抽奖模板部署
使用Helm 在容器服务k8s集群一键部署wordpress
使用Jenkins和单个模板部署多个Kubernetes组件
Helm包管理与模板化部署实战
评论