Helm包管理實(shí)戰(zhàn)
一、概述
1.1 背景介紹
直接用kubectl管理K8s資源,10個(gè)微服務(wù)就要維護(hù)幾十個(gè)YAML文件,版本管理靠文件夾命名,回滾靠手動(dòng)替換文件。Helm把一組相關(guān)的K8s資源打包成Chart,支持模板化、版本管理、一鍵部署和回滾,是K8s生態(tài)中事實(shí)上的包管理標(biāo)準(zhǔn)。
生產(chǎn)環(huán)境中的痛點(diǎn):開發(fā)團(tuán)隊(duì)每次部署都要改十幾個(gè)YAML里的鏡像版本、副本數(shù)、資源限制,改漏一個(gè)就出問(wèn)題。用Helm后,這些可變參數(shù)抽成values.yaml,部署時(shí)只需要helm upgrade -f prod-values.yaml一條命令,CI/CD流水線也更容易集成。
本文基于Helm v3.13.x版本,覆蓋Chart開發(fā)、倉(cāng)庫(kù)管理、生產(chǎn)部署、回滾策略等內(nèi)容。Helm v2已于2020年停止維護(hù),不再討論。
1.2 技術(shù)特點(diǎn)
模板引擎:基于Go template,支持變量、條件判斷、循環(huán)、函數(shù),一套模板適配多環(huán)境
Release管理:每次安裝/升級(jí)都是一個(gè)Release,記錄完整的版本歷史,支持一鍵回滾到任意版本
依賴管理:Chart可以聲明對(duì)其他Chart的依賴,自動(dòng)拉取和安裝子Chart
倉(cāng)庫(kù)生態(tài):Artifact Hub上有數(shù)千個(gè)社區(qū)Chart,常用中間件(MySQL、Redis、Kafka)開箱即用
1.3 適用場(chǎng)景
場(chǎng)景一:多環(huán)境部署(dev/staging/prod),同一套Chart用不同的values文件區(qū)分配置
場(chǎng)景二:CI/CD流水線集成,Jenkins/GitLab CI中用helm命令自動(dòng)化部署
場(chǎng)景三:復(fù)雜應(yīng)用編排,一個(gè)Chart包含Deployment、Service、ConfigMap、Ingress等多個(gè)資源
場(chǎng)景四:第三方中間件部署,用社區(qū)Chart快速部署Prometheus、Grafana、Nginx Ingress等
1.4 環(huán)境要求
| 組件 | 版本要求 | 說(shuō)明 |
|---|---|---|
| Kubernetes | 1.24+ | Helm 3.13支持K8s 1.24-1.28 |
| Helm | 3.13+ | 當(dāng)前穩(wěn)定版本 |
| kubectl | 與集群版本匹配 | Helm依賴kubeconfig訪問(wèn)集群 |
| OCI Registry | Harbor 2.x / Docker Registry | 存儲(chǔ)Helm Chart的OCI倉(cāng)庫(kù) |
二、詳細(xì)步驟
2.1 準(zhǔn)備工作
2.1.1 安裝Helm
# 方式一:官方腳本安裝 curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # 方式二:手動(dòng)下載 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 # 驗(yàn)證 helm version # version.BuildInfo{Version:"v3.13.3", ...} # 配置命令補(bǔ)全 helm completion bash > /etc/bash_completion.d/helm source/etc/bash_completion.d/helm
2.1.2 配置Chart倉(cāng)庫(kù)
# 添加常用倉(cāng)庫(kù) 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 # 更新倉(cāng)庫(kù)索引 helm repo update # 查看已添加的倉(cāng)庫(kù) 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 # 查看默認(rèn)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 # 升級(jí)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 創(chuàng)建自定義Chart
# 創(chuàng)建Chart骨架 helm create myapp # 目錄結(jié)構(gòu) # myapp/ # ├── Chart.yaml # Chart元數(shù)據(jù) # ├── values.yaml # 默認(rèn)配置值 # ├── charts/ # 依賴Chart # ├── templates/ # 模板文件 # │ ├── deployment.yaml # │ ├── service.yaml # │ ├── ingress.yaml # │ ├── hpa.yaml # │ ├── serviceaccount.yaml # │ ├── configmap.yaml # │ ├── _helpers.tpl # 模板輔助函數(shù) # │ ├── NOTES.txt # 安裝后提示信息 # │ └── tests/ # │ └── test-connection.yaml # └── .helmignore # 打包時(shí)忽略的文件
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:""# 默認(rèn)使用Chart的appVersion pullPolicy:IfNotPresent imagePullSecrets: -name:registry-secret # 副本數(shù) 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自動(dòng)伸縮 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 # 環(huán)境變量 env: -name:APP_ENV value:"production" -name:LOG_LEVEL value:"info" -name:DB_HOST valueFrom: secretKeyRef: name:myapp-db-secret key:host # ConfigMap數(shù)據(jù) config: app.conf:| server.port=8080 server.graceful-shutdown=30s cache.ttl=300 # 持久化存儲(chǔ) persistence: enabled:false storageClass:"ceph-rbd" size:10Gi # Pod調(diào)度 nodeSelector:{} tolerations:[] affinity:{} topologySpreadConstraints: -maxSkew:1 topologyKey:topology.kubernetes.io/zone whenUnsatisfiable:DoNotSchedule labelSelector: matchLabels: app.kubernetes.io/name:myapp # 依賴服務(wù)開關(guān) 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}}
說(shuō)明:
checksum/config注解:ConfigMap內(nèi)容變化時(shí)自動(dòng)觸發(fā)Pod滾動(dòng)更新,不加這個(gè)的話改了ConfigMap但Pod不會(huì)重啟
maxUnavailable: 0:滾動(dòng)更新時(shí)不允許有Pod不可用,保證零停機(jī)部署
securityContext:以非root用戶運(yùn)行,生產(chǎn)安全基線
輔助函數(shù)模板:
# 文件:myapp/templates/_helpers.tpl
{{/*
生成應(yīng)用全名
*/}}
{{-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}}
{{/*
通用標(biāo)簽
*/}}
{{-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}}
{{/*
選擇器標(biāo)簽
*/}}
{{-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版本標(biāo)簽
*/}}
{{-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 多環(huán)境values文件
# 文件:values-dev.yaml(開發(fā)環(huán)境覆蓋)
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(生產(chǎn)環(huán)境覆蓋) 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打包和發(fā)布
# 更新依賴 helm dependency update myapp/ helm dependency build myapp/ # 模板渲染測(cè)試(不實(shí)際部署,只看生成的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倉(cāng)庫(kù)(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 啟動(dòng)和驗(yàn)證
2.3.1 部署到各環(huán)境
# 部署到開發(fā)環(huán)境 helm upgrade --install myapp-dev myapp/ -f values-dev.yaml -n dev --create-namespace --wait--timeout 5m # 部署到生產(chǎn)環(huán)境 helm upgrade --install myapp-prod myapp/ -f values-prod.yaml -n production --create-namespace --wait--timeout 10m --atomic # --atomic:部署失敗自動(dòng)回滾到上一個(gè)版本 # --wait:等待所有Pod Ready才算成功 # --timeout:超時(shí)時(shí)間,超時(shí)視為失敗
2.3.2 驗(yàn)證部署
# 查看Release狀態(tài) helm status myapp-prod -n production # 查看實(shí)際生成的資源 helm get manifest myapp-prod -n production # 查看使用的values helm get values myapp-prod -n production # 查看所有信息 helm get all myapp-prod -n production # 驗(yàn)證Pod運(yùn)行 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 # 查看兩個(gè)版本的差異 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分鐘,防止流量波動(dòng)導(dǎo)致頻繁縮擴(kuò)容。生產(chǎn)環(huán)境這個(gè)值不要設(shè)太小,實(shí)測(cè)設(shè)為60秒時(shí)一天內(nèi)縮擴(kuò)容了200多次。
3.1.3 CI/CD集成腳本
#!/bin/bash
# 文件:deploy.sh
# GitLab CI/Jenkins中調(diào)用的部署腳本
set-euo pipefail
# 參數(shù)
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驗(yàn)證
helm upgrade --install"${RELEASE_NAME}""${CHART_PATH}"
-f"${VALUES_FILE}"
-n"${NAMESPACE}"
--setimage.tag="${IMAGE_TAG}"
--dry-run
# 實(shí)際部署
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 實(shí)際應(yīng)用案例
案例一:用Helm部署Prometheus監(jiān)控棧
場(chǎng)景描述:用kube-prometheus-stack Chart一鍵部署Prometheus + Grafana + AlertManager,替代手動(dòng)維護(hù)幾十個(gè)YAML文件。
實(shí)現(xiàn)代碼:
# 添加倉(cāng)庫(kù) 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 # 驗(yàn)證 kubectl get pods -n monitoring helm list -n monitoring
案例二:Helm Hooks實(shí)現(xiàn)數(shù)據(jù)庫(kù)遷移
場(chǎng)景描述:應(yīng)用升級(jí)時(shí)需要先執(zhí)行數(shù)據(jù)庫(kù)Schema遷移,遷移成功后才部署新版本。用Helm的pre-upgrade Hook實(shí)現(xiàn)。
實(shí)現(xiàn)代碼:
# 文件: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
說(shuō)明:
helm.sh/hook: pre-upgrade:在upgrade之前執(zhí)行
helm.sh/hook-weight: "-5":多個(gè)Hook時(shí)按weight排序,數(shù)字小的先執(zhí)行
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded:下次執(zhí)行前刪除舊Job,成功后也刪除
四、最佳實(shí)踐和注意事項(xiàng)
4.1 最佳實(shí)踐
4.1.1 性能優(yōu)化
限制Release歷史數(shù)量:每次upgrade都會(huì)在K8s Secret中存儲(chǔ)一份Release記錄,默認(rèn)不限制。集群運(yùn)行一年后,頻繁部署的服務(wù)可能積累幾百個(gè)歷史版本,占用etcd空間。
# 部署時(shí)限制歷史版本數(shù) helm upgrade --install myapp ./myapp -n production --history-max 10 # 清理已有的舊版本 helmhistorymyapp -n production # 手動(dòng)無(wú)法直接刪除歷史,只能通過(guò)--history-max在下次upgrade時(shí)自動(dòng)清理
使用OCI Registry替代ChartMuseum:Helm 3.8+原生支持OCI格式存儲(chǔ)Chart,Harbor 2.x直接支持。OCI Registry比ChartMuseum性能更好,且不需要額外維護(hù)一個(gè)服務(wù)。
# 登錄OCI倉(cāng)庫(kù) helm registry login harbor.company.com # 推送Chart helm push myapp-1.0.0.tgz oci://harbor.company.com/helm-charts # 從OCI倉(cāng)庫(kù)安裝 helm install myapp oci://harbor.company.com/helm-charts/myapp --version 1.0.0
模板渲染緩存:大型Chart(50+模板文件)的渲染時(shí)間可能超過(guò)10秒,CI/CD中頻繁的helm template會(huì)拖慢流水線。把渲染結(jié)果緩存,只在Chart或values變化時(shí)重新渲染。
4.1.2 安全加固
values中不存儲(chǔ)敏感信息:數(shù)據(jù)庫(kù)密碼、API Key等不要寫在values.yaml中,用External Secrets Operator或Sealed Secrets從外部密鑰管理系統(tǒng)注入。
# 不推薦:密碼明文寫在values中 database: password:"my-secret-password" # 推薦:引用已存在的Secret env: -name:DB_PASSWORD valueFrom: secretKeyRef: name:myapp-db-secret# 由External Secrets創(chuàng)建 key:password
Chart簽名驗(yàn)證:用GPG簽名Chart包,安裝時(shí)驗(yàn)證簽名防止篡改。
# 簽名 helm package --sign --key'ops-team'--keyring ~/.gnupg/secring.gpg myapp/ # 驗(yàn)證 helm verify myapp-1.0.0.tgz --keyring ~/.gnupg/pubring.gpg # 安裝時(shí)驗(yàn)證 helm install myapp myapp-1.0.0.tgz --verify --keyring ~/.gnupg/pubring.gpg
RBAC限制Helm操作權(quán)限:不同團(tuán)隊(duì)只能在自己的namespace中操作Helm Release,通過(guò)K8s RBAC限制ServiceAccount權(quán)限。
4.1.3 高可用配置
HA方案一:--atomic參數(shù)保證部署失敗自動(dòng)回滾,不會(huì)出現(xiàn)半部署狀態(tài)
HA方案二:GitOps模式(ArgoCD + Helm),Chart和values存儲(chǔ)在Git倉(cāng)庫(kù),ArgoCD自動(dòng)同步,避免手動(dòng)操作失誤
備份策略:Chart源碼納入Git版本管理,Release歷史保留10個(gè)版本,values文件按環(huán)境分別管理
4.2 注意事項(xiàng)
4.2.1 配置注意事項(xiàng)
警告:Helm upgrade操作會(huì)直接修改集群資源,生產(chǎn)環(huán)境務(wù)必先dry-run確認(rèn)變更內(nèi)容。
注意helm upgrade默認(rèn)會(huì)合并新舊values,不是完全替換。如果舊版本有個(gè)參數(shù)新版本刪掉了,upgrade后舊參數(shù)仍然存在。用--reset-values可以只使用新的values,但要確保新values是完整的。
注意helm install和helm upgrade是兩個(gè)不同的命令,用helm upgrade --install可以合并為一個(gè),首次執(zhí)行是install,后續(xù)是upgrade。CI/CD中統(tǒng)一用這個(gè)命令。
注意Chart的version(Chart版本)和appVersion(應(yīng)用版本)是兩個(gè)獨(dú)立的字段。Chart模板改了要升version,只改鏡像tag不需要升version但建議升appVersion。
4.2.2 常見錯(cuò)誤
| 錯(cuò)誤現(xiàn)象 | 原因分析 | 解決方案 |
|---|---|---|
| Error: UPGRADE FAILED: another operation is in progress | 上次操作未完成或異常中斷 |
helm rollback |
| Error: rendered manifests contain a resource that already exists | 資源已存在但不屬于當(dāng)前Release | 給已有資源添加Helm標(biāo)簽:app.kubernetes.io/managed-by: Helm |
| 模板渲染報(bào)nil pointer evaluating interface | values中缺少必要字段 | 模板中用{{ default "" .Values.xxx }}或{{- if .Values.xxx }}做空值判斷 |
| upgrade后Pod沒(méi)有更新 | ConfigMap/Secret內(nèi)容變了但Pod沒(méi)重啟 | 在Deployment模板中加checksum/config注解 |
| 依賴Chart下載失敗 | 倉(cāng)庫(kù)不可達(dá)或版本不存在 | helm repo update 更新索引;檢查Chart.yaml中的依賴版本 |
4.2.3 兼容性問(wèn)題
版本兼容:Helm 3.13.x支持K8s 1.24-1.28,不同Helm版本生成的Release Secret格式可能不同
平臺(tái)兼容:Helm CLI支持Linux/macOS/Windows,但Windows下路徑分隔符可能導(dǎo)致模板渲染問(wèn)題
組件依賴:helm-diff插件版本需要和Helm版本匹配;ArgoCD的Helm支持有版本要求
五、故障排查和監(jiān)控
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 # 對(duì)比當(dāng)前Release和Chart的差異 helm diff upgrade myapp ./myapp -f values-prod.yaml -n production
5.1.2 常見問(wèn)題排查
問(wèn)題一:upgrade卡住不動(dòng),超時(shí)失敗
# 診斷命令 helm status myapp -n production kubectl get pods -n production -l app.kubernetes.io/name=myapp kubectl describe pod-n production
解決方案:
Pod鏡像拉取失敗:檢查鏡像地址和imagePullSecrets
Pod資源不足:檢查節(jié)點(diǎn)可用資源
readinessProbe失?。簷z查健康檢查路徑和端口
Hook Job失敗:kubectl logs job/
問(wèn)題二:Release狀態(tài)為failed,無(wú)法upgrade
# 診斷命令 helmhistorymyapp -n production helm status myapp -n production
解決方案:
# 方式一:回滾到上一個(gè)成功版本 helm rollback myapp-n production # 方式二:強(qiáng)制替換(謹(jǐn)慎使用) helm upgrade --install myapp ./myapp -n production --force # 方式三:卸載后重裝(會(huì)短暫中斷服務(wù)) helm uninstall myapp -n production helm install myapp ./myapp -f values-prod.yaml -n production
問(wèn)題三:模板渲染錯(cuò)誤
癥狀:helm upgrade報(bào)模板語(yǔ)法錯(cuò)誤
排查:
# 本地渲染模板,查看具體錯(cuò)誤 helm template myapp ./myapp -f values-prod.yaml --debug # 只渲染特定模板 helm template myapp ./myapp -s templates/deployment.yaml -f values-prod.yaml
解決:檢查Go template語(yǔ)法,常見錯(cuò)誤是縮進(jìn)不對(duì)、變量名拼寫錯(cuò)誤、缺少{{- end }}
5.1.3 調(diào)試模式
# 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 # 驗(yàn)證Chart結(jié)構(gòu) helm lint ./myapp --strict
5.2 性能監(jiān)控
5.2.1 關(guān)鍵指標(biāo)監(jiān)控
# 查看所有Release狀態(tài) helm list -A --all # 查看failed的Release helm list -A --failed # 查看pending的Release helm list -A --pending # 統(tǒng)計(jì)各namespace的Release數(shù)量 helm list -A -o json | jq -r'.[].namespace'| sort | uniq -c | sort -rn
5.2.2 監(jiān)控指標(biāo)說(shuō)明
| 指標(biāo)名稱 | 正常范圍 | 告警閾值 | 說(shuō)明 |
|---|---|---|---|
| Release狀態(tài) | deployed | failed/pending | failed需要排查,pending說(shuō)明操作卡住 |
| Release歷史版本數(shù) | <10 | >20 | 過(guò)多歷史版本占用etcd空間 |
| 部署耗時(shí) | <5分鐘 | >10分鐘 | 超時(shí)通常是Pod啟動(dòng)慢或鏡像拉取慢 |
| Hook執(zhí)行時(shí)間 | <2分鐘 | >5分鐘 | 數(shù)據(jù)庫(kù)遷移等Hook不應(yīng)該太慢 |
| Chart Lint警告數(shù) | 0 | >0 | 警告可能導(dǎo)致部署異常 |
5.2.3 Prometheus監(jiān)控規(guī)則
# 文件:helm-alerts.yaml
# 通過(guò)kube-state-metrics監(jiān)控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 備份與恢復(fù)
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 恢復(fù)流程
停止服務(wù):通知相關(guān)團(tuán)隊(duì)準(zhǔn)備維護(hù)窗口
恢復(fù)數(shù)據(jù):helm upgrade --install
驗(yàn)證完整性:helm status
重啟服務(wù):確認(rèn)所有Pod Running
六、總結(jié)
6.1 技術(shù)要點(diǎn)回顧
要點(diǎn)一:helm upgrade --install統(tǒng)一安裝和升級(jí)操作,配合--atomic保證部署失敗自動(dòng)回滾
要點(diǎn)二:values.yaml按環(huán)境拆分(dev/staging/prod),敏感信息不寫在values中,用External Secrets注入
要點(diǎn)三:Deployment模板中加checksum/config注解,ConfigMap變更時(shí)自動(dòng)觸發(fā)Pod滾動(dòng)更新
要點(diǎn)四:--history-max 10限制Release歷史版本數(shù)量,避免占用過(guò)多etcd空間
要點(diǎn)五:Chart源碼納入Git版本管理,CI/CD中用helm lint和--dry-run做部署前驗(yàn)證
6.2 進(jìn)階學(xué)習(xí)方向
Helmfile多Release編排:管理多個(gè)Helm Release的聲明式工具,一個(gè)helmfile.yaml定義整個(gè)環(huán)境的所有服務(wù)
學(xué)習(xí)資源:Helmfile GitHub
實(shí)踐建議:從單個(gè)環(huán)境開始,逐步擴(kuò)展到多環(huán)境管理
ArgoCD + Helm GitOps:用ArgoCD監(jiān)控Git倉(cāng)庫(kù)中的Chart和values變更,自動(dòng)同步到集群
學(xué)習(xí)資源:ArgoCD Helm支持
實(shí)踐建議:先在staging環(huán)境實(shí)踐GitOps流程
自定義Chart Library:把公司通用的模板抽成Library Chart,業(yè)務(wù)Chart引用Library減少重復(fù)代碼
6.3 參考資料
Helm官方文檔- 完整的Helm使用指南
Chart開發(fā)指南- 模板語(yǔ)法詳解
Artifact Hub- Helm Chart公共倉(cāng)庫(kù)
helm-diff插件- Release差異對(duì)比工具
附錄
A. 命令速查表
# 倉(cāng)庫(kù)管理 helm repo add# 添加倉(cāng)庫(kù) helm repo update # 更新倉(cāng)庫(kù)索引 helm repo list # 查看倉(cāng)庫(kù)列表 helm search repo # 搜索Chart # Release管理 helm install -n # 安裝 helm upgrade --install -n # 安裝或升級(jí) helm upgrade -n -f values.yaml # 升級(jí) helm rollback -n # 回滾 helm uninstall -n # 卸載 helm list -A # 查看所有Release helmhistory -n # 查看歷史 helm status -n # 查看狀態(tài) # Chart開發(fā) helm create # 創(chuàng)建Chart骨架 helm lint # 語(yǔ)法檢查 helm template # 本地渲染模板 helm package # 打包 helm push # 推送到OCI倉(cāng)庫(kù) helm dependency update # 更新依賴 # 調(diào)試 helm get values -n # 查看values helm get manifest -n # 查看manifest helm get all -n # 查看所有信息 helm diff upgrade # 差異對(duì)比(需要插件)
B. 配置參數(shù)詳解
helm upgrade常用參數(shù):
| 參數(shù) | 說(shuō)明 | 建議 |
|---|---|---|
| --install | 不存在時(shí)自動(dòng)install | CI/CD中必用 |
| --atomic | 失敗自動(dòng)回滾 | 生產(chǎn)環(huán)境必用 |
| --wait | 等待Pod Ready | 生產(chǎn)環(huán)境必用 |
| --timeout | 超時(shí)時(shí)間 | 生產(chǎn)設(shè)10m,開發(fā)設(shè)5m |
| --history-max | 歷史版本上限 | 建議10 |
| --dry-run | 模擬執(zhí)行 | 部署前驗(yàn)證 |
| --debug | 調(diào)試輸出 | 排查問(wèn)題時(shí)用 |
| --force | 強(qiáng)制替換資源 | 謹(jǐn)慎使用 |
| --reset-values | 不合并舊values | 完整values時(shí)使用 |
| --reuse-values | 復(fù)用舊values | 只改個(gè)別參數(shù)時(shí)使用 |
| -f | 指定values文件 | 可多次使用,后面覆蓋前面 |
| --set | 命令行設(shè)置值 | 優(yōu)先級(jí)最高 |
Helm Hook類型:
| Hook | 執(zhí)行時(shí)機(jī) | 典型用途 |
|---|---|---|
| pre-install | install前 | 創(chuàng)建前置資源 |
| post-install | install后 | 發(fā)送通知 |
| pre-upgrade | upgrade前 | 數(shù)據(jù)庫(kù)遷移 |
| post-upgrade | upgrade后 | 清理緩存 |
| pre-delete | uninstall前 | 數(shù)據(jù)備份 |
| post-delete | uninstall后 | 清理外部資源 |
| pre-rollback | rollback前 | 數(shù)據(jù)庫(kù)回滾 |
| post-rollback | rollback后 | 通知 |
| test | helm test時(shí) | 連通性測(cè)試 |
C. 術(shù)語(yǔ)表
| 術(shù)語(yǔ) | 英文 | 解釋 |
|---|---|---|
| Chart | - | Helm的包格式,包含K8s資源模板和配置 |
| Release | - | Chart的一次安裝實(shí)例,每次install/upgrade產(chǎn)生新版本 |
| Repository | - | Chart倉(cāng)庫(kù),存儲(chǔ)和分發(fā)Chart包 |
| Values | - | Chart的配置參數(shù),通過(guò)values.yaml或--set傳入 |
| Template | - | Go template格式的K8s資源模板 |
| Hook | - | 在Release生命周期特定階段執(zhí)行的操作 |
| Dependency | - | Chart對(duì)其他Chart的依賴關(guān)系 |
| OCI | Open Container Initiative | 容器鏡像標(biāo)準(zhǔn),Helm 3.8+支持OCI格式存儲(chǔ)Chart |
-
鏡像
+關(guān)注
關(guān)注
0文章
180瀏覽量
11630 -
微服務(wù)
+關(guān)注
關(guān)注
0文章
150瀏覽量
8096
原文標(biāo)題:別再手工改 YAML:Helm 包管理與模板化部署全解
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Kubernetes Helm入門指南
【「?jìng)}頡編程快速上手」閱讀體驗(yàn)】簡(jiǎn)潔包管理的命脈
HarmonyOS5云服務(wù)技術(shù)分享--Serverless抽獎(jiǎng)模板部署
使用Helm 在容器服務(wù)k8s集群一鍵部署wordpress
Python之包管理工具快速入門
Helm的一些概念及用法
使用Jenkins和單個(gè)模板部署多個(gè)Kubernetes組件
鴻蒙開發(fā)實(shí)戰(zhàn):【包管理子系統(tǒng)】
Helm包管理與模板化部署實(shí)戰(zhàn)
評(píng)論