伦伦影院久久影视,天天操天天干天天射,ririsao久久精品一区 ,一本大道香蕉大久在红桃,999久久久免费精品国产色夜,色悠悠久久综合88,亚洲国产精品久久无套麻豆,亚洲香蕉毛片久久网站,一本一道久久综合狠狠老

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

企業級應用模板化部署與Helm包管理實戰

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 2026-03-17 14:32 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、概述

1.1 背景介紹

生產環境中一個微服務體系動輒幾十個 Deployment、Service、ConfigMap、Secret、Ingress,如果全部用裸 YAML 手工維護,版本迭代時改錯一個 label selector 就能導致滾動更新斷流。Helm 作為 Kubernetes 的包管理器,把一組關聯資源打包成 Chart,通過模板變量實現環境差異化渲染,通過 Release 管理實現一鍵部署、升級、回滾。從 Helm v3 開始去掉了集群端的 Tiller 組件,直接使用 kubeconfig 鑒權,安全模型大幅簡化。v3.17+ 進一步將 OCI Registry 作為原生存儲后端,Chart 的分發方式與容器鏡像完全對齊。

本文面向一線運維和 SRE,以排障手冊的形式覆蓋 Helm Chart 從設計、開發、測試到部署上線的全鏈路,重點放在企業級場景下的 Chart 模板化設計模式、多環境分層覆蓋策略、OCI Registry 集成、Helmfile 編排以及常見部署故障的根因定位。

1.2 核心語義

概念 語義 排障關聯
Chart 一組 Kubernetes 資源模板的打包單元 Chart 結構錯誤直接導致 helm install 失敗
Release Chart 的一次安裝實例,帶版本歷史 Release 狀態異常是回滾和升級故障的入口
Values 模板渲染參數,支持多層覆蓋 Values 層級錯誤導致渲染結果不符合預期
Repository / OCI Registry Chart 的存儲和分發通道 認證、網絡問題導致 chart pull 失敗
Hook Release 生命周期中的自定義動作 Hook 執行失敗會阻塞整個 install/upgrade 流程

1.3 適用場景

多環境(dev/staging/prod)同一套應用需要差異化配置部署

微服務體系需要統一模板、批量發布、版本化管理

需要在 CI/CD 流水線中實現聲明式部署并支持自動回滾

跨團隊共享基礎設施組件(中間件、監控棧、日志采集器)的標準化交付

需要將內部 Chart 推送到私有 OCI Registry 進行版本管理

1.4 環境要求

組件 版本要求 說明
Kubernetes 1.32+ 目標集群版本,需與 Chart apiVersion 兼容
Helm v3.17+ OCI registry 默認支持,無需額外 feature gate
kubectl 匹配集群版本 helm 底層依賴 kubectl 的 kubeconfig
Docker Engine 27.x 用于構建鏡像和本地 OCI registry 測試
Container Registry Harbor 2.x / ECR / ACR / GCR OCI 兼容的 Chart 存儲后端
Helmfile v0.169+ 多 Release 批量編排(可選)

1.5 排障坐標系

Helm 部署故障的排查可以沿兩個軸展開:

時間軸(Release 生命周期):

helm install → pre-install hooks → 資源創建 → post-install hooks → Ready 檢查
helm upgrade → pre-upgrade hooks → 資源更新 → post-upgrade hooks → Ready 檢查
helm rollback → pre-rollback hooks → 資源回退 → post-rollback hooks
helm uninstall → pre-delete hooks → 資源刪除 → post-delete hooks

空間軸(問題域分層):

Layer 0: Chart 結構 / 語法錯誤   → helm lint / helm template 可發現
Layer 1: Values 渲染結果異常    → helm template + diff 可發現
Layer 2: Kubernetes API 拒絕    → kubectl apply --dry-run=server 級別
Layer 3: 資源運行時異常      → Pod/Service/Ingress 層面的運行時排查
Layer 4: Hook 執行失敗       → Job/Pod 日志層面

二、詳細步驟

2.1 觀測面:Chart 結構與 Release 狀態

2.1.1 Chart 目錄結構

一個標準的 Helm Chart 目錄結構:

mychart/
├── Chart.yaml       # Chart 元數據:名稱、版本、依賴
├── Chart.lock       # 依賴鎖定文件
├── values.yaml      # 默認參數
├── values-dev.yaml    # 開發環境覆蓋(可選,推薦放 Chart 外部)
├── values-prod.yaml    # 生產環境覆蓋
├── charts/        # 子 Chart(依賴)
├── crds/         # CRD 定義(install 時自動應用)
├── templates/       # 模板文件
│  ├── _helpers.tpl    # 命名模板(partial templates)
│  ├── deployment.yaml
│  ├── service.yaml
│  ├── ingress.yaml
│  ├── configmap.yaml
│  ├── secret.yaml
│  ├── hpa.yaml
│  ├── serviceaccount.yaml
│  ├── NOTES.txt     # install 后的提示信息
│  └── tests/
│    └──test-connection.yaml
└── .helmignore      # 打包時忽略的文件

2.1.2 Chart.yaml 詳解

apiVersion:v2         # Helm v3 固定為 v2
name:myapp
description:Aproduction-gradewebapplicationchart
type:application        # application 或 library
version:1.4.2         # Chart 版本,遵循 SemVer
appVersion:"3.8.1"       # 應用程序版本,僅展示用途
kubeVersion:">=1.28.0-0"    # 兼容的 K8s 版本范圍
home:https://github.com/myorg/myapp
maintainers:
-name:sre-team
 email:sre@myorg.com
dependencies:
-name:postgresql
 version:"15.5.x"
 repository:"oci://registry.myorg.com/charts"
 condition:postgresql.enabled
 tags:
  -database
-name:redis
 version:"19.x.x"
 repository:"oci://registry.myorg.com/charts"
 condition:redis.enabled
 tags:
  -cache
-name:common
 version:"2.x.x"
 repository:"oci://registry.myorg.com/charts"
 tags:
  -infra

version和appVersion的區別經常混淆:version是 Chart 自身的版本號,每次模板變更都應遞增;appVersion是 Chart 部署的應用版本,變更應用鏡像 tag 時更新。CI/CD 中應該自動同步這兩個版本號。

2.1.3 Release 狀態查看

# 列出所有 namespace 的 release
helm list -A

# 查看特定 release 的詳細信息
helm status myapp -n production

# 查看 release 歷史版本
helmhistorymyapp -n production

# 輸出示例
# REVISION  UPDATED           STATUS    CHART      APP VERSION  DESCRIPTION
# 1      2026-02-10 1000     superseded  myapp-1.2.0   3.6.0     Install complete
# 2      2026-02-15 1400     superseded  myapp-1.3.0   3.7.0     Upgrade complete
# 3      2026-03-01 0900     deployed   myapp-1.4.2   3.8.1     Upgrade complete

Release 的 STATUS 字段是排障第一入口:

STATUS 含義 下一步動作
deployed 當前活躍版本 正常,無需處理
superseded 已被新版本取代 保留用于回滾參考
failed 安裝或升級失敗 查看 helm status 中的 NOTES 和 events
pending-install 安裝中 檢查 hook Job 是否卡住
pending-upgrade 升級中 檢查 hook Job 和資源創建狀態
pending-rollback 回滾中 檢查回滾目標版本的資源兼容性
uninstalling 刪除中 檢查 finalizer 和 pre-delete hook

2.2 第一輪判斷:模板渲染與語法檢查

2.2.1 helm lint — 靜態檢查

# 基礎 lint
helm lint ./mychart

# 帶 values 文件的 lint
helm lint ./mychart -f values-prod.yaml

# 嚴格模式
helm lint ./mychart --strict

# 輸出示例(有問題時)
# ==> Linting ./mychart
# [ERROR] Chart.yaml: version is required
# [WARNING] templates/deployment.yaml: object name does not conform to Kubernetes naming requirements
# [INFO] Chart.yaml: icon is recommended
#
# Error: 1 chart(s) linted, 1 chart(s) failed

helm lint能捕獲的問題:Chart.yaml 必填字段缺失、模板語法錯誤(括號未閉合、函數不存在)、資源名稱不符合 K8s 命名規范。它不能捕獲的問題:Values 值類型不匹配、運行時資源沖突、鏡像不存在。

2.2.2 helm template — 本地渲染

# 完整渲染輸出
helm template myapp ./mychart -f values-prod.yaml -n production

# 只渲染特定模板
helm template myapp ./mychart -s templates/deployment.yaml -f values-prod.yaml

# 渲染并校驗 API 版本(連接集群)
helm template myapp ./mychart -f values-prod.yaml --validate

# 渲染結果重定向到文件,方便 diff
helm template myapp ./mychart -f values-dev.yaml > rendered-dev.yaml
helm template myapp ./mychart -f values-prod.yaml > rendered-prod.yaml
diff rendered-dev.yaml rendered-prod.yaml

helm template是排查 Values 渲染問題的核心工具。在 CI/CD 中,建議每次 merge request 都執行helm template并與主分支的渲染結果做 diff,任何意外的資源變更都應觸發人工審核。

2.2.3 helm diff 插件

# 安裝 diff 插件
helm plugin install https://github.com/databus23/helm-diff

# 查看升級將產生的差異(不實際執行)
helm diff upgrade myapp ./mychart -f values-prod.yaml -n production

# 輸出示例
# production, myapp-deployment, Deployment (apps) has changed:
#  spec:
#   template:
#    spec:
#     containers:
#     - name: myapp
# -     image: registry.myorg.com/myapp:3.7.0
# +     image: registry.myorg.com/myapp:3.8.1
# -     resources:
# -      limits:
# -       memory: 512Mi
# +     resources:
# +      limits:
# +       memory: 1Gi

helm diff在執行helm upgrade之前展示所有即將變更的資源,是生產環境變更審批的必備環節。

2.3 第二輪下鉆:Go 模板語法與 Values 設計

2.3.1 Go 模板語法核心

變量引用與內置對象

# templates/deployment.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:{{include"myapp.fullname".}}
labels:
 {{-include"myapp.labels".|nindent4}}
annotations:
 helm.sh/chart:{{include"myapp.chart".}}
 app.kubernetes.io/managed-by:{{.Release.Service}}
spec:
{{-ifnot.Values.autoscaling.enabled}}
replicas:{{.Values.replicaCount}}
{{-end}}
selector:
 matchLabels:
  {{-include"myapp.selectorLabels".|nindent6}}
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".}}
  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
    {{-if.Values.probes.liveness.enabled}}
    livenessProbe:
     httpGet:
      path:{{.Values.probes.liveness.path}}
      port:http
     initialDelaySeconds:{{.Values.probes.liveness.initialDelaySeconds}}
     periodSeconds:{{.Values.probes.liveness.periodSeconds}}
     failureThreshold:{{.Values.probes.liveness.failureThreshold}}
    {{-end}}
    {{-if.Values.probes.readiness.enabled}}
    readinessProbe:
     httpGet:
      path:{{.Values.probes.readiness.path}}
      port:http
     initialDelaySeconds:{{.Values.probes.readiness.initialDelaySeconds}}
     periodSeconds:{{.Values.probes.readiness.periodSeconds}}
    {{-end}}
    resources:
     {{-toYaml.Values.resources|nindent12}}
    {{-if.Values.extraEnv}}
    env:
     {{-range.Values.extraEnv}}
     -name:{{.name}}
      value:{{.value|quote}}
     {{-end}}
    {{-end}}
    volumeMounts:
     -name:config
      mountPath:/etc/myapp
      readOnly:true
  volumes:
   -name:config
    configMap:
     name:{{include"myapp.fullname".}}-config
  {{-with.Values.nodeSelector}}
  nodeSelector:
   {{-toYaml.|nindent8}}
  {{-end}}
  {{-with.Values.tolerations}}
  tolerations:
   {{-toYaml.|nindent8}}
  {{-end}}
  {{-with.Values.affinity}}
  affinity:
   {{-toYaml.|nindent8}}
  {{-end}}

關鍵語法點

語法 作用 常見錯誤
{{ .Values.xxx }} 引用 values 值 路徑寫錯不報錯,渲染為空
{{- ... -}} 去除左/右空白 過度使用導致 YAML 縮進錯誤
{{ include "tpl" . }} 調用命名模板 忘記傳遞.上下文
{{ toYaml . | nindent N }} 轉 YAML 并縮進 N 格 nindent 值錯誤導致 YAML 解析失敗
{{ with .Values.xxx }} 條件塊并切換上下文 塊內無法訪問.Release等頂級對象
{{ range .Values.list }} 遍歷列表 塊內.指向當前元素,需$.Values訪問頂級
{{ default "val" .Values.xxx }} 默認值 空字符串不觸發 default
{{ required "msg" .Values.xxx }} 必填校驗 僅在渲染時觸發,lint 不檢查
{{ quote .Values.xxx }} 加引號 數字類型意外變字符串

2.3.2 _helpers.tpl 命名模板

# templates/_helpers.tpl

{{/*
Chart完整名稱,帶releasename前綴
*/}}
{{-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}}

{{/*
Chart標識
*/}}
{{-define"myapp.chart"-}}
{{-printf"%s-%s".Chart.Name.Chart.Version|replace"+""_"|trunc63|trimSuffix"-"}}
{{-end}}

{{/*
通用標簽
*/}}
{{-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}}
{{-end}}

{{/*
Selector標簽—不可變,用于Deploymentselector
*/}}
{{-define"myapp.selectorLabels"-}}
app.kubernetes.io/name:{{include"myapp.name".}}
app.kubernetes.io/instance:{{.Release.Name}}
{{-end}}

{{/*
ServiceAccount名稱
*/}}
{{-define"myapp.serviceAccountName"-}}
{{-if.Values.serviceAccount.create}}
{{-default(include"myapp.fullname".).Values.serviceAccount.name}}
{{-else}}
{{-default"default".Values.serviceAccount.name}}
{{-end}}
{{-end}}

{{/*
名稱截斷
*/}}
{{-define"myapp.name"-}}
{{-default.Chart.Name.Values.nameOverride|trunc63|trimSuffix"-"}}
{{-end}}

63 字符截斷是 Kubernetes 對 label value 的硬限制,在命名模板中必須始終保留這個約束。

2.3.3 Values 分層覆蓋設計

Helm Values 的合并順序(后者覆蓋前者):

Chart 內 values.yaml(默認值)
↓ 被覆蓋
父 Chart 的 values.yaml(子 Chart 依賴場景)
↓ 被覆蓋
-f / --values 指定的文件(可多個,后指定的優先)
↓ 被覆蓋
--set/ --set-string / --set-json(命令行直接設置)

values.yaml 設計原則

# values.yaml — 默認值,適配 dev 環境
replicaCount:1

image:
repository:registry.myorg.com/myapp
pullPolicy:IfNotPresent
tag:""# 留空,默認使用 Chart.AppVersion

imagePullSecrets:
-name:registry-credentials

serviceAccount:
create:true
name:""
annotations:{}

service:
type:ClusterIP
port:8080

ingress:
enabled:false
className:nginx
annotations:{}
hosts:
 -host:myapp.dev.myorg.com
  paths:
   -path:/
    pathType:Prefix
tls:[]

resources:
limits:
 cpu:500m
 memory:512Mi
requests:
 cpu:100m
 memory:128Mi

autoscaling:
enabled:false
minReplicas:2
maxReplicas:10
targetCPUUtilizationPercentage:70
targetMemoryUtilizationPercentage:80

probes:
liveness:
 enabled:true
 path:/healthz
 initialDelaySeconds:10
 periodSeconds:15
 failureThreshold:3
readiness:
 enabled:true
 path:/readyz
 initialDelaySeconds:5
 periodSeconds:10

extraEnv:[]

nodeSelector:{}
tolerations:[]
affinity:{}

# 子 Chart 開關
postgresql:
enabled:true
auth:
 database:myapp
 username:myapp
primary:
 persistence:
  size:10Gi

redis:
enabled:false

values-prod.yaml — 生產環境覆蓋

# values-prod.yaml
replicaCount:3

image:
tag:"3.8.1"

ingress:
enabled:true
className:nginx
annotations:
 cert-manager.io/cluster-issuer:letsencrypt-prod
 nginx.ingress.kubernetes.io/rate-limit:"100"
 nginx.ingress.kubernetes.io/ssl-redirect:"true"
hosts:
 -host:myapp.myorg.com
  paths:
   -path:/
    pathType:Prefix
tls:
 -secretName:myapp-tls
  hosts:
   -myapp.myorg.com

resources:
limits:
 cpu:"2"
 memory:2Gi
requests:
 cpu:500m
 memory:512Mi

autoscaling:
enabled:true
minReplicas:3
maxReplicas:20
targetCPUUtilizationPercentage:65

probes:
liveness:
 initialDelaySeconds:30
 failureThreshold:5

nodeSelector:
node-role:app

tolerations:
-key:dedicated
 operator:Equal
 value:app
 effect:NoSchedule

affinity:
podAntiAffinity:
 preferredDuringSchedulingIgnoredDuringExecution:
  -weight:100
   podAffinityTerm:
    labelSelector:
     matchExpressions:
      -key:app.kubernetes.io/name
       operator:In
       values:
        -myapp
    topologyKey:kubernetes.io/hostname

postgresql:
primary:
 persistence:
  size:100Gi
 resources:
  limits:
   cpu:"4"
   memory:8Gi
  requests:
   cpu:"1"
   memory:2Gi

敏感值處理

生產環境的 Secret 不應寫在 values 文件中。推薦方案:

# 方案一:通過 --set 在 CI/CD 中注入
helm upgrade myapp ./mychart 
 -f values-prod.yaml 
 --setsecrets.dbPassword="${DB_PASSWORD}"
 --setsecrets.apiKey="${API_KEY}"
 -n production

# 方案二:External Secrets Operator(推薦)
# 在 Chart 模板中引用 ExternalSecret 資源,從 AWS Secrets Manager / Vault 同步
# templates/external-secret.yaml
{{-if.Values.externalSecrets.enabled}}
apiVersion:external-secrets.io/v1beta1
kind:ExternalSecret
metadata:
name:{{include"myapp.fullname".}}-secrets
labels:
 {{-include"myapp.labels".|nindent4}}
spec:
refreshInterval:1h
secretStoreRef:
 name:{{.Values.externalSecrets.storeRef}}
 kind:ClusterSecretStore
target:
 name:{{include"myapp.fullname".}}-secrets
data:
 {{-range.Values.externalSecrets.keys}}
 -secretKey:{{.secretKey}}
  remoteRef:
   key:{{.remoteKey}}
   property:{{.property}}
 {{-end}}
{{-end}}

2.4 根因矩陣:Helm 部署常見故障

故障現象 層級 可能原因 診斷命令 修復方向
helm install 報 YAML 解析錯誤 L0 模板縮進錯誤、nindent 值不對 helm template 查看渲染結果 修正模板縮進
helm install 報 "cannot re-use a name" L2 同名 Release 已存在(可能是 failed 狀態) helm list -A --all helm uninstall 舊 Release 或換名
upgrade 報 "has no deployed releases" L2 首次 install 失敗,Release 停在 pending-install helm list --pending -A helm uninstall --no-hooks 清理后重新 install
upgrade 后 Pod 未更新 L1 image.tag 未變更,Deployment spec 無變化 helm diff upgrade 檢查差異 在 annotation 中加 checksum/config 觸發滾動
hook Job 卡在 Pending L4 資源配額不足、nodeSelector 不匹配 kubectl describe job 調整 Job 資源或調度約束
"lookup function not supported" L0 使用lookup函數但在helm template中運行 改用helm install --dry-run lookup 只在連接集群時可用
"chart requires kubeVersion >=1.28" L0 集群版本低于 Chart 要求 kubectl version 升級集群或調整 Chart.yaml
OCI pull 401 Unauthorized L2 Registry 認證失敗 helm registry login 測試 檢查憑證配置
values 中的 list 被 --set 覆蓋而非追加 L1 Helm --set 對 list 是替換行為 helm template 對比渲染結果 使用 --set-json 或 -f 文件
CRD 升級未生效 L0 Helm 不管理 CRD 的升級(只負責首次安裝) kubectl get crd -o yaml 手動 kubectl apply CRD

2.5 處理與驗證

2.5.1 Release 部署操作

# 首次安裝
helm install myapp ./mychart 
 -f values-prod.yaml 
 -n production 
 --create-namespace 
 --wait
 --timeout 10m

# 升級(帶原子操作:失敗自動回滾)
helm upgrade myapp ./mychart 
 -f values-prod.yaml 
 -n production 
 --atomic 
 --timeout 10m

# 手動回滾到上一個版本
helm rollback myapp 0 -n production --wait

# 回滾到指定版本
helm rollback myapp 2 -n production --wait

# 卸載
helm uninstall myapp -n production --keep-history

關鍵參數說明:

--wait:等待所有 Pod 就緒后才標記 Release 為 deployed,否則即使 Pod 還沒啟動完就返回成功

--atomic:如果升級失敗(--wait 超時、hook 失敗),自動回滾到上一個成功版本

--timeout:與 --wait 配合,設置等待超時。默認 5m,復雜應用建議調到 10-15m

--keep-history:uninstall 時保留歷史,后續可以 rollback

2.5.2 部署后驗證清單

# 1. 確認 Release 狀態
helm status myapp -n production

# 2. 確認渲染結果符合預期
helm get values myapp -n production    # 查看當前生效的 values
helm get manifest myapp -n production   # 查看當前生效的完整 manifest

# 3. 確認 Pod 狀態
kubectl get pods -n production -l app.kubernetes.io/instance=myapp

# 4. 確認 Service 端點
kubectl get endpoints -n production -l app.kubernetes.io/instance=myapp

# 5. 確認 Ingress
kubectl get ingress -n production -l app.kubernetes.io/instance=myapp

# 6. 運行 Chart 測試
helmtestmyapp -n production

2.5.3 Helm Hooks 詳解

# templates/hooks/db-migration.yaml
apiVersion:batch/v1
kind:Job
metadata:
name:{{include"myapp.fullname".}}-db-migrate
labels:
 {{-include"myapp.labels".|nindent4}}
annotations:
 "helm.sh/hook":pre-upgrade,pre-install
 "helm.sh/hook-weight":"-5"     # 權重越小越先執行
 "helm.sh/hook-delete-policy":before-hook-creation,hook-succeeded
spec:
backoffLimit:3
activeDeadlineSeconds:600
template:
 metadata:
  labels:
   {{-include"myapp.selectorLabels".|nindent8}}
 spec:
  restartPolicy:Never
  containers:
   -name:migrate
    image:"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
    command:["./migrate","--direction","up"]
    env:
     -name:DATABASE_URL
      valueFrom:
       secretKeyRef:
        name:{{include"myapp.fullname".}}-secrets
        key:database-url
  {{-with.Values.imagePullSecrets}}
  imagePullSecrets:
   {{-toYaml.|nindent8}}
  {{-end}}

Hook 類型與執行時機:

Hook 觸發時機 典型用途
pre-install install 前,資源創建前 數據庫初始化、前置檢查
post-install install 后,所有資源創建后 注冊服務、發送通知
pre-upgrade upgrade 前 數據庫遷移、數據備份
post-upgrade upgrade 后 緩存預熱、健康檢查
pre-rollback rollback 前 數據庫回退遷移
post-rollback rollback 后 狀態清理
pre-delete uninstall 前 資源清理、數據導出
post-delete uninstall 后 外部資源清理
test helm test 執行時 連通性測試、功能驗證

Hook 刪除策略:

策略 含義
before-hook-creation 執行新 hook 前刪除舊的同名資源
hook-succeeded hook 成功后刪除
hook-failed hook 失敗后刪除

生產建議:始終設置before-hook-creation,否則第二次 upgrade 時會因為同名 Job 已存在而失敗。

三、示例代碼和配置

3.1 OCI Registry 操作

Helm v3.17+ 中 OCI 是默認支持的 Chart 存儲格式,不再需要設置環境變量HELM_EXPERIMENTAL_OCI=1。

# 登錄 OCI Registry
helm registry login registry.myorg.com 
 --username admin 
 --password-stdin <<

3.2 完整 CI/CD 集成示例

# .gitlab-ci.yml — Helm Chart CI/CD 流水線
stages:
-lint
-package
-deploy-staging
-deploy-prod

variables:
CHART_DIR:"./charts/myapp"
REGISTRY:"registry.myorg.com"
CHART_REPO:"oci://${REGISTRY}/charts"

lint:
stage:lint
image:alpine/helm:3.17.0
script:
 -helmlint${CHART_DIR}--strict
 -helmtemplatetest-release${CHART_DIR}-f${CHART_DIR}/values.yaml>/dev/null
 -helmtemplatetest-release${CHART_DIR}-fvalues-staging.yaml>/dev/null
 -helmtemplatetest-release${CHART_DIR}-fvalues-prod.yaml>/dev/null
rules:
 -changes:
   -"charts/**/*"

package-and-push:
stage:package
image:alpine/helm:3.17.0
script:
 -helmdependencyupdate${CHART_DIR}
 -helmpackage${CHART_DIR}
 -helmregistrylogin${REGISTRY}--username${REGISTRY_USER}--password${REGISTRY_PASSWORD}
 -helmpushmyapp-*.tgz${CHART_REPO}
rules:
 -if:'$CI_COMMIT_BRANCH == "main"'
  changes:
   -"charts/**/*"

deploy-staging:
stage:deploy-staging
image:alpine/helm:3.17.0
script:
 -helmregistrylogin${REGISTRY}--username${REGISTRY_USER}--password${REGISTRY_PASSWORD}
 -helmdiffupgrademyapp${CHART_REPO}/myapp
   --version${CHART_VERSION}
   -fvalues-staging.yaml
   -nstaging||true
 -helmupgrade--installmyapp${CHART_REPO}/myapp
   --version${CHART_VERSION}
   -fvalues-staging.yaml
   -nstaging
   --create-namespace
   --atomic
   --timeout10m
 -helmtestmyapp-nstaging
environment:
 name:staging
rules:
 -if:'$CI_COMMIT_BRANCH == "main"'

deploy-prod:
stage:deploy-prod
image:alpine/helm:3.17.0
script:
 -helmregistrylogin${REGISTRY}--username${REGISTRY_USER}--password${REGISTRY_PASSWORD}
 -helmdiffupgrademyapp${CHART_REPO}/myapp
   --version${CHART_VERSION}
   -fvalues-prod.yaml
   -nproduction
 -helmupgrade--installmyapp${CHART_REPO}/myapp
   --version${CHART_VERSION}
   -fvalues-prod.yaml
   -nproduction
   --atomic
   --timeout15m
   --setsecrets.dbPassword="${DB_PASSWORD_PROD}"
 -helmtestmyapp-nproduction
environment:
 name:production
rules:
 -if:'$CI_COMMIT_BRANCH == "main"'
  when:manual

3.3 Helmfile 多環境批量編排

# helmfile.yaml
---
repositories:[]# OCI registry 不需要 repositories 聲明

environments:
dev:
 values:
  -environments/dev/defaults.yaml
staging:
 values:
  -environments/staging/defaults.yaml
production:
 values:
  -environments/production/defaults.yaml
 secrets:
  -environments/production/secrets.yaml# sops 加密

---

helmDefaults:
wait:true
timeout:600
atomic:true
createNamespace:true

releases:
-name:myapp
 namespace:{{.Environment.Name}}
 chart:oci://registry.myorg.com/charts/myapp
 version:1.4.2
 values:
  -values-{{.Environment.Name}}.yaml
  -image:
    tag:{{requiredEnv"APP_VERSION"}}
 hooks:
  -events:["presync"]
   showlogs:true
   command:"kubectl"
   args:
    -"get"
    -"ns"
    -"{{ .Environment.Name }}"

-name:postgresql
 namespace:{{.Environment.Name}}
 chart:oci://registry.myorg.com/charts/postgresql
 version:15.5.38
 condition:postgresql.enabled
 values:
  -postgresql-values-{{.Environment.Name}}.yaml

-name:redis
 namespace:{{.Environment.Name}}
 chart:oci://registry.myorg.com/charts/redis
 version:19.6.4
 condition:redis.enabled
 values:
  -redis-values-{{.Environment.Name}}.yaml

-name:prometheus-stack
 namespace:monitoring
 chart:oci://registry.myorg.com/charts/kube-prometheus-stack
 version:65.8.1
 values:
  -monitoring-values.yaml
# Helmfile 操作命令
# 查看 diff
helmfile -e production diff

# 部署到指定環境
helmfile -e production apply

# 只部署特定 release
helmfile -e production -l name=myapp apply

# 銷毀環境
helmfile -e staging destroy

3.4 Library Chart 設計

Library Chart 不直接生成 Kubernetes 資源,而是提供可復用的模板片段給其他 Chart 引用。

# common-library/Chart.yaml
apiVersion:v2
name:common-library
version:2.1.0
type:library # 關鍵:聲明為 library 類型
description:Commontemplatesforallapplicationcharts
# common-library/templates/_deployment.tpl
{{-define"common.deployment"-}}
apiVersion:apps/v1
kind:Deployment
metadata:
name:{{include"common.fullname".}}
labels:
 {{-include"common.labels".|nindent4}}
spec:
{{-ifnot.Values.autoscaling.enabled}}
replicas:{{.Values.replicaCount|default1}}
{{-end}}
strategy:
 type:RollingUpdate
 rollingUpdate:
  maxSurge:25%
  maxUnavailable:0
selector:
 matchLabels:
  {{-include"common.selectorLabels".|nindent6}}
template:
 metadata:
  annotations:
   {{-if.Values.configHash}}
   checksum/config:{{.Values.configHash}}
   {{-end}}
  labels:
   {{-include"common.selectorLabels".|nindent8}}
 spec:
  serviceAccountName:{{include"common.serviceAccountName".}}
  {{-with.Values.imagePullSecrets}}
  imagePullSecrets:
   {{-toYaml.|nindent8}}
  {{-end}}
  securityContext:
   runAsNonRoot:true
   seccompProfile:
    type:RuntimeDefault
  containers:
   -name:{{.Chart.Name}}
    image:"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
    imagePullPolicy:{{.Values.image.pullPolicy|default"IfNotPresent"}}
    securityContext:
     allowPrivilegeEscalation:false
     capabilities:
      drop:
       -ALL
     readOnlyRootFilesystem:true
    {{-with.Values.ports}}
    ports:
     {{-toYaml.|nindent12}}
    {{-end}}
    {{-with.Values.resources}}
    resources:
     {{-toYaml.|nindent12}}
    {{-end}}
  {{-with.Values.nodeSelector}}
  nodeSelector:
   {{-toYaml.|nindent8}}
  {{-end}}
{{-end-}}

在應用 Chart 中引用 Library Chart:

# myapp/Chart.yaml
dependencies:
-name:common-library
 version:"2.x.x"
 repository:"oci://registry.myorg.com/charts"
# myapp/templates/deployment.yaml
{{-include"common.deployment".}}

這樣組織內所有微服務 Chart 只需維護 values 差異,Deployment 的基礎結構由 Library Chart 統一管理。

3.5 Chart 測試模板

# templates/tests/test-connection.yaml
apiVersion:v1
kind:Pod
metadata:
name:"{{ include "myapp.fullname" . }}-test-connection"
labels:
 {{-include"myapp.labels".|nindent4}}
annotations:
 "helm.sh/hook":test
 "helm.sh/hook-delete-policy":before-hook-creation,hook-succeeded
spec:
restartPolicy:Never
containers:
 -name:wget
  image:busybox:1.37
  command:['wget']
  args:['{{include"myapp.fullname".}}:{{.Values.service.port}}/healthz','-q','-O-','-T','10']
 -name:curl-api
  image:curlimages/curl:8.10.1
  command:['curl']
  args:['-sf','--max-time','10','http://{{ include "myapp.fullname" . }}:{{ .Values.service.port }}/api/v1/status']
# 運行測試
helmtestmyapp -n production --timeout 5m

# 查看測試 Pod 日志
kubectl logs myapp-test-connection -n production -c wget
kubectl logs myapp-test-connection -n production -c curl-api

3.6 自動化診斷腳本

#!/usr/bin/env bash
set-euo pipefail

# 文件名:helm-release-diagnose.sh
# 作用:對指定 Helm Release 進行全面健康檢查,輸出診斷報告
# 適用場景:Release 部署后狀態異常、Pod 未就緒、Service 無端點時使用
# 使用方法:./helm-release-diagnose.sh  
# 輸入參數:$1=Release名稱 $2=命名空間
# 輸出結果:終端輸出診斷報告,包含 Release 狀態、Pod 狀態、事件、日志摘要
# 風險提示:只讀操作,不修改任何資源。需要 helm 和 kubectl 權限

RELEASE="${1:?Usage: $0  }"
NAMESPACE="${2:?Usage: $0  }"

echo"=========================================="
echo"Helm Release 診斷報告"
echo"Release:${RELEASE}"
echo"Namespace:${NAMESPACE}"
echo"Time:$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo"=========================================="

echo""
echo"--- 1. Release 狀態 ---"
helm status"${RELEASE}"-n"${NAMESPACE}"2>&1 ||echo"ERROR: Release 不存在或無法訪問"

echo""
echo"--- 2. Release 歷史 ---"
helmhistory"${RELEASE}"-n"${NAMESPACE}"--max 5 2>&1 ||echo"ERROR: 無法獲取歷史"

echo""
echo"--- 3. 當前生效 Values(非默認值) ---"
helm get values"${RELEASE}"-n"${NAMESPACE}"2>&1 ||echo"ERROR: 無法獲取 values"

echo""
echo"--- 4. Release 關聯的 Pod ---"
PODS=$(kubectl get pods -n"${NAMESPACE}"-l"app.kubernetes.io/instance=${RELEASE}"-o wide 2>&1)
echo"${PODS}"

# 檢查是否有異常 Pod
echo""
echo"--- 5. 異常 Pod 詳情 ---"
ABNORMAL_PODS=$(kubectl get pods -n"${NAMESPACE}"-l"app.kubernetes.io/instance=${RELEASE}"
 --field-selector='status.phase!=Running,status.phase!=Succeeded'-o name 2>/dev/null ||true)

if[[ -z"${ABNORMAL_PODS}"]];then
 echo"所有 Pod 狀態正常"
else
 forpodin${ABNORMAL_PODS};do
   echo"---${pod}---"
    kubectl describe"${pod}"-n"${NAMESPACE}"| tail -30
   echo""
    kubectl logs"${pod}"-n"${NAMESPACE}"--tail=20 2>/dev/null ||echo"無法獲取日志"
   echo""
 done
fi

echo""
echo"--- 6. 近期事件(Warning) ---"
kubectl get events -n"${NAMESPACE}"
 --field-selector="type=Warning"
 --sort-by='.lastTimestamp'2>&1 | tail -20

echo""
echo"--- 7. Service 端點 ---"
kubectl get endpoints -n"${NAMESPACE}"-l"app.kubernetes.io/instance=${RELEASE}"2>&1

echo""
echo"--- 8. Ingress 狀態 ---"
kubectl get ingress -n"${NAMESPACE}"-l"app.kubernetes.io/instance=${RELEASE}"2>&1 ||echo"無 Ingress"

echo""
echo"=========================================="
echo"診斷完成"
echo"=========================================="
#!/usr/bin/env bash
set-euo pipefail

# 文件名:helm-chart-ci-validate.sh
# 作用:在 CI 流水線中對 Chart 執行完整的靜態驗證
# 適用場景:MR/PR 提交時自動觸發,防止有語法錯誤或渲染異常的 Chart 合入主分支
# 使用方法:./helm-chart-ci-validate.sh  [values-file1] [values-file2] ...
# 輸入參數:$1=Chart目錄 $2+=可選的 values 文件列表
# 輸出結果:通過則退出碼 0,失敗則退出碼 1 并輸出錯誤詳情
# 風險提示:只讀操作。需要 helm 3.17+

CHART_DIR="${1:?Usage: $0  [values-files...]}"
shift
VALUES_FILES=("$@")

ERRORS=0

echo"=== Helm Chart CI Validation ==="
echo"Chart:${CHART_DIR}"
echo"Values files:${VALUES_FILES[*]:-none}"
echo""

# Step 1: lint
echo"--- Step 1: helm lint (strict) ---"
if! helm lint"${CHART_DIR}"--strict;then
 echo"FAIL: helm lint failed"
  ((ERRORS++))
fi

# Step 2: dependency check
echo""
echo"--- Step 2: dependency update ---"
if! helm dependency update"${CHART_DIR}";then
 echo"FAIL: dependency update failed"
  ((ERRORS++))
fi

# Step 3: template render with default values
echo""
echo"--- Step 3: template render (default values) ---"
if! helm template ci-test"${CHART_DIR}"> /dev/null;then
 echo"FAIL: template render with default values failed"
  ((ERRORS++))
fi

# Step 4: template render with each values file
forvfin"${VALUES_FILES[@]}";do
 echo""
 echo"--- Step 4: template render with${vf}---"
 if! helm template ci-test"${CHART_DIR}"-f"${vf}"> /dev/null;then
   echo"FAIL: template render with${vf}failed"
    ((ERRORS++))
 fi
done

# Step 5: check for deprecated API versions
echo""
echo"--- Step 5: deprecated API check ---"
RENDERED=$(helm template ci-test"${CHART_DIR}"2>/dev/null ||true)
DEPRECATED_APIS=("extensions/v1beta1""apps/v1beta1""apps/v1beta2""networking.k8s.io/v1beta1")
forapiin"${DEPRECATED_APIS[@]}";do
 ifecho"${RENDERED}"| grep -q"apiVersion:${api}";then
   echo"WARN: Deprecated API found:${api}"
 fi
done

echo""
if[[${ERRORS}-gt 0 ]];then
 echo"RESULT: FAILED (${ERRORS}errors)"
 exit1
else
 echo"RESULT: PASSED"
 exit0
fi
#!/usr/bin/env bash
set-euo pipefail

# 文件名:helm-bulk-upgrade.sh
# 作用:批量升級多個 Helm Release,支持 dry-run 和回滾
# 適用場景:基礎組件(如 common-library)升級后需要批量更新所有引用該 library 的應用 Chart
# 使用方法:./helm-bulk-upgrade.sh --env production --chart-version 1.4.2 [--dry-run]
# 輸入參數:--env=目標環境 --chart-version=Chart版本 --dry-run=僅模擬不執行
# 輸出結果:每個 Release 的升級結果日志
# 風險提示:生產環境務必先 --dry-run。批量操作前確認變更窗口。升級順序:基礎設施 → 中間件 → 應用

ENV=""
CHART_VERSION=""
DRY_RUN=false

while[[$#-gt 0 ]];do
 case$1in
    --env) ENV="$2";shift2 ;;
    --chart-version) CHART_VERSION="$2";shift2 ;;
    --dry-run) DRY_RUN=true;shift;;
    *)echo"Unknown option:$1";exit1 ;;
 esac
done

[[ -z"${ENV}"]] && {echo"Error: --env is required";exit1; }
[[ -z"${CHART_VERSION}"]] && {echo"Error: --chart-version is required";exit1; }

REGISTRY="oci://registry.myorg.com/charts"
RELEASES=("myapp-api""myapp-web""myapp-worker""myapp-scheduler")
NAMESPACE="${ENV}"

echo"Bulk upgrade started"
echo"Environment:${ENV}"
echo"Chart version:${CHART_VERSION}"
echo"Dry run:${DRY_RUN}"
echo""

FAILED=()
SUCCEEDED=()

forreleasein"${RELEASES[@]}";do
 echo"--- Upgrading${release}---"
  VALUES_FILE="values/${release}-${ENV}.yaml"

 if[[ ! -f"${VALUES_FILE}"]];then
   echo"SKIP:${VALUES_FILE}not found"
   continue
 fi

  CMD="helm upgrade --install${release}${REGISTRY}/myapp 
    --version${CHART_VERSION}
    -f${VALUES_FILE}
    -n${NAMESPACE}
    --atomic 
    --timeout 10m"

 if[["${DRY_RUN}"=="true"]];then
    CMD="${CMD}--dry-run"
 fi

 ifeval"${CMD}";then
    SUCCEEDED+=("${release}")
   echo"OK:${release}upgraded successfully"
 else
    FAILED+=("${release}")
   echo"FAIL:${release}upgrade failed"
   if[["${DRY_RUN}"=="false"]];then
     echo"Note: --atomic flag should have triggered auto-rollback"
   fi
 fi
 echo""
done

echo"=========================================="
echo"Summary"
echo"Succeeded:${SUCCEEDED[*]:-none}"
echo"Failed:${FAILED[*]:-none}"
echo"=========================================="

[[${#FAILED[@]}-eq 0 ]] ||exit1
#!/usr/bin/env bash
set-euo pipefail

# 文件名:helm-values-diff.sh
# 作用:對比兩個環境的 values 渲染差異,輸出人類可讀的 diff 報告
# 適用場景:新環境上線前確認與已有環境的配置差異,防止遺漏關鍵配置
# 使用方法:./helm-values-diff.sh   
# 輸入參數:$1=Chart目錄 $2=環境A的values文件 $3=環境B的values文件
# 輸出結果:兩個環境渲染后的資源 diff(類似 kubectl diff 格式)
# 風險提示:只讀操作。大型 Chart 渲染可能消耗較多內存

CHART_DIR="${1:?Usage: $0   }"
VALUES_A="${2:?Usage: $0   }"
VALUES_B="${3:?Usage: $0   }"

TMPDIR=$(mktemp -d)
trap'rm -rf "${TMPDIR}"'EXIT

echo"Rendering${VALUES_A}..."
helm template compare"${CHART_DIR}"-f"${VALUES_A}">"${TMPDIR}/a.yaml"

echo"Rendering${VALUES_B}..."
helm template compare"${CHART_DIR}"-f"${VALUES_B}">"${TMPDIR}/b.yaml"

echo""
echo"=== Diff:${VALUES_A}vs${VALUES_B}==="
diff -u"${TMPDIR}/a.yaml""${TMPDIR}/b.yaml"
  --label"${VALUES_A}"
  --label"${VALUES_B}"||true
echo""
echo"=== End of diff ==="

四、實際應用案例

4.1 案例一:helm upgrade 后 Pod 未滾動更新

現場現象:執行helm upgrade myapp ./mychart -f values-prod.yaml -n production返回成功,但 Pod 仍在運行舊版本鏡像。helm history顯示新 Revision 為 deployed 狀態。

第一輪判斷

# 查看當前渲染的 manifest 中的鏡像
helm get manifest myapp -n production | grep"image:"
# 輸出:image: "registry.myorg.com/myapp:3.8.1" — 鏡像 tag 確實已更新

# 查看 Deployment 是否有變更
kubectl get deployment myapp -n production -o jsonpath='{.spec.template.spec.containers[0].image}'
# 輸出:registry.myorg.com/myapp:3.8.1 — Deployment spec 已更新

# 查看 Pod 的鏡像
kubectl get pods -n production -l app.kubernetes.io/instance=myapp -o jsonpath='{.items[*].spec.containers[0].image}'
# 輸出:registry.myorg.com/myapp:3.7.0 registry.myorg.com/myapp:3.7.0 — Pod 仍是舊版本

第二輪下鉆

# 查看 Deployment 的 rollout 狀態
kubectl rollout status deployment/myapp -n production
# 輸出:Waiting for deployment "myapp" rollout to finish: 0 of 3 updated replicas are available...

# 查看 ReplicaSet
kubectl get rs -n production -l app.kubernetes.io/instance=myapp
# NAME        DESIRED  CURRENT  READY  AGE
# myapp-6d4f8b7c9   3     3     3    10d   ← 舊 RS
# myapp-7a5e9c3d1   3     3     0    2m   ← 新 RS,Pod 未 Ready

# 查看新 RS 的 Pod 為什么未 Ready
kubectl describe pod myapp-7a5e9c3d1-xxxxx -n production
# Events:
#  Warning Failed 2m kubelet Failed to pull image "registry.myorg.com/myapp:3.8.1":
#      rpc error: code = NotFound desc = failed to pull and unpack image: not found

關鍵證據:鏡像3.8.1在 Registry 中不存在。CI/CD 流水線在鏡像構建完成前就觸發了 Helm upgrade。

根因:流水線中 build 和 deploy stage 的依賴關系配置錯誤,deploy 未等待 build 完成。

修復動作

先推送正確的鏡像到 Registry

新 Pod 會自動拉取成功并變為 Ready

修復 CI/CD 流水線中 stage 之間的依賴關系

修復后驗證

kubectl rollout status deployment/myapp -n production --timeout=5m
# deployment "myapp" successfully rolled out

kubectl get pods -n production -l app.kubernetes.io/instance=myapp -o jsonpath='{.items[*].spec.containers[0].image}'
# registry.myorg.com/myapp:3.8.1 registry.myorg.com/myapp:3.8.1 registry.myorg.com/myapp:3.8.1

防再發建議

CI/CD 中在 deploy 之前加鏡像存在性檢查:docker manifest inspect registry.myorg.com/myapp:3.8.1

Helm upgrade 使用--atomic,鏡像拉取失敗會自動回滾

4.2 案例二:pre-upgrade Hook Job 卡住導致 upgrade 超時

現場現象:helm upgrade myapp ./mychart -f values-prod.yaml -n production --timeout 10m --atomic執行 10 分鐘后超時,Release 狀態變為failed,然后自動回滾。但數據庫遷移 Job 一直卡在 Pending。

第一輪判斷

helmhistorymyapp -n production
# REVISION STATUS   DESCRIPTION
# 5     deployed  Upgrade complete
# 6     failed   pre-upgrade hook "myapp-db-migrate" timed out

kubectl getjobs-n production | grep migrate
# myapp-db-migrate  0/1  10m  10m

第二輪下鉆

kubectl describe job myapp-db-migrate -n production
# Events:
#  Warning FailedCreate 10m job-controller Error creating: pods "myapp-db-migrate-xxxxx"
#      is forbidden: exceeded quota: compute-quota, requested: cpu=2, used: cpu=14, limited: cpu=16

kubectl get resourcequota -n production
# NAME       AGE  REQUEST            LIMIT
# compute-quota  30d  requests.cpu: 14/16, ...   limits.cpu: 28/32, ...

關鍵證據:數據庫遷移 Job 請求 2 CPU,但命名空間的 ResourceQuota 已經用了 14/16,剩余 2 CPU 正好不夠(因為還有其他 Pending Pod 占用請求)。

根因:遷移 Job 的資源請求過高,且在 upgrade 期間新舊 Pod 同時存在導致 CPU 請求總量超限。

修復動作

# 降低遷移 Job 的資源請求
# templates/hooks/db-migration.yaml
spec:
template:
 spec:
  containers:
   -name:migrate
    resources:
     requests:
      cpu:200m  # 從 2 降到 200m
      memory:256Mi
     limits:
      cpu:"1"
      memory:512Mi

或者調整升級策略,確保新舊 Pod 不會同時大量并存:

# Deployment 使用 Recreate 策略或更保守的 RollingUpdate
spec:
strategy:
 type:RollingUpdate
 rollingUpdate:
  maxSurge:0    # 不額外創建新 Pod
  maxUnavailable:1 # 一次只替換一個

修復后驗證

helm upgrade myapp ./mychart -f values-prod.yaml -n production --atomic --timeout 10m
# Release "myapp" has been upgraded. Happy Helming!

kubectl getjobs-n production | grep migrate
# myapp-db-migrate  1/1  45s  1m

防再發建議

Hook Job 的資源請求應與 ResourceQuota 預留空間匹配

在 CI 中加入 ResourceQuota 余量檢查

考慮將數據庫遷移從 Helm Hook 移到獨立的 Job,在 upgrade 之前手動執行

4.3 案例三:Helmfile 多 Release 部署順序導致依賴未就緒

現場現象:使用 Helmfile 部署三個組件(postgresql、redis、myapp),myapp 部署后 Pod 反復 CrashLoopBackOff,日志顯示 "connection refused" 無法連接 PostgreSQL。

第一輪判斷

helmfile -e production status
# NAME     NAMESPACE  REVISION STATUS
# postgresql  production  1     deployed
# redis    production  1     deployed
# myapp    production  1     deployed

kubectl get pods -n production
# postgresql-0    1/1  Running      0  2m
# redis-master-0   1/1  Running      0  2m
# myapp-xxx      0/1  CrashLoopBackOff  3  2m

kubectl logs myapp-xxx -n production
# level=fatal msg="failed to connect to database" error="dial tcp 10.96.45.123 connection refused"

第二輪下鉆

# PostgreSQL Pod 雖然 Running,但實際還在初始化
kubectl logs postgresql-0 -n production | tail -5
# 2026-03-01 0930 UTC [1] LOG: database system is ready to accept connections
# 上面這行出現在 myapp 啟動之后

# 查看時間線
kubectl get events -n production --sort-by='.firstTimestamp'| grep -E"(postgresql|myapp)"
# 0900 postgresql-0 Created
# 0905 myapp-xxx Created      ← myapp 在 postgresql 初始化完成前就啟動了
# 0920 myapp-xxx BackOff
# 0930 postgresql-0 Ready

關鍵證據:Helmfile 默認并發安裝所有 Release,myapp 在 PostgreSQL 完成初始化之前就開始連接數據庫。

根因:Helmfile 缺少 Release 之間的依賴和順序聲明。

修復動作

# helmfile.yaml — 添加 needs 聲明
releases:
-name:postgresql
 namespace:production
 chart:oci://registry.myorg.com/charts/postgresql
 version:15.5.38
 values:
  -postgresql-values-production.yaml

-name:redis
 namespace:production
 chart:oci://registry.myorg.com/charts/redis
 version:19.6.4
 values:
  -redis-values-production.yaml

-name:myapp
 namespace:production
 chart:oci://registry.myorg.com/charts/myapp
 version:1.4.2
 needs:           # 聲明依賴
  -production/postgresql
  -production/redis
 values:
  -values-production.yaml

同時在 myapp 的 Chart 中增加 initContainer 做連接等待:

# values-production.yaml
initContainers:
-name:wait-for-db
 image:busybox:1.37
 command:['sh','-c','until nc -z postgresql 5432; do echo waiting for db; sleep 2; done']

修復后驗證

helmfile -e production apply
# postgresql deployed first, then redis, then myapp

kubectl get pods -n production
# postgresql-0  1/1  Running  0  3m
# redis-master-0 1/1  Running  0  2m
# myapp-xxx   1/1  Running  0  1m

防再發建議

始終在 Helmfile 中用needs聲明 Release 之間的啟動依賴

應用端使用 initContainer 或連接重試機制,不依賴部署順序保證可用性

設置合理的 readinessProbe,讓 K8s 在應用真正就緒后才接入流量

4.4 案例四:OCI Registry 推送 Chart 時 401 Unauthorized

現場現象:CI/CD 流水線中執行helm push myapp-1.4.2.tgz oci://registry.myorg.com/charts報錯Error: unexpected status from HEAD request to https://registry.myorg.com/v2/charts/myapp/blobs/sha256 401 Unauthorized。

第一輪判斷

# 測試 registry 登錄
helm registry login registry.myorg.com --username ci-bot --password"${REGISTRY_PASSWORD}"
# Login Succeeded — 登錄沒問題

# 手動推送
helm push myapp-1.4.2.tgz oci://registry.myorg.com/charts
# Error: unexpected status from HEAD request... 401 Unauthorized

第二輪下鉆

# 檢查 OCI 兼容性
curl -u"ci-bot:${REGISTRY_PASSWORD}"https://registry.myorg.com/v2/
# {"errors":[{"code":"UNAUTHORIZED","message":"authentication required"}]}

# 檢查是否需要 Bearer token
curl -v https://registry.myorg.com/v2/ 2>&1 | grep -i www-authenticate
# Www-Authenticate: Bearer realm="https://auth.myorg.com/service/token",service="registry.myorg.com"

# 檢查 helm 的認證配置
cat ~/.config/helm/registry/config.json
# 發現 auth 字段存在但對應的 credsStore 配置指向了 docker-credential-desktop

關鍵證據:Helm 的 registry 認證走的是 Docker credential store,在 CI 環境中docker-credential-desktop不存在,導致雖然helm registry login顯示成功(寫入了 config.json),但實際推送時 credential helper 找不到憑證。

根因:CI 環境缺少 Docker credential helper,Helm 的 registry 認證依賴 Docker 的憑證存儲體系。

修復動作

# 方案一:在 CI 中禁用 credential helper,使用純文件認證
exportDOCKER_CONFIG="${HOME}/.docker-helm"
mkdir -p"${DOCKER_CONFIG}"

# 直接寫入認證信息
echo'{"auths":{"registry.myorg.com":{"auth":"'$(echo-n"ci-bot:${REGISTRY_PASSWORD}"| base64)'"}}}'
 >"${DOCKER_CONFIG}/config.json"

# 重新執行 helm push
helm push myapp-1.4.2.tgz oci://registry.myorg.com/charts
# Pushed: registry.myorg.com/charts/myapp:1.4.2
# 方案二:使用 HELM_REGISTRY_CONFIG 環境變量
exportHELM_REGISTRY_CONFIG="/tmp/helm-registry-config.json"
helm registry login registry.myorg.com --username ci-bot --password"${REGISTRY_PASSWORD}"
helm push myapp-1.4.2.tgz oci://registry.myorg.com/charts

修復后驗證

# 確認推送成功
helm pull oci://registry.myorg.com/charts/myapp --version 1.4.2
# Pulled: registry.myorg.com/charts/myapp:1.4.2

helm show chart oci://registry.myorg.com/charts/myapp --version 1.4.2
# apiVersion: v2
# name: myapp
# version: 1.4.2

防再發建議

CI 環境中統一使用HELM_REGISTRY_CONFIG或DOCKER_CONFIG環境變量指定認證配置文件

不依賴系統級的 credential helper

在流水線開始時加helm registry login的連通性驗證步驟

五、最佳實踐和注意事項

5.1 Chart 設計最佳實踐

5.1.1 版本管理

Chart version 遵循 SemVer:Breaking change 升主版本,新功能升次版本,Bug 修復升補丁版本

appVersion 與應用鏡像 tag 保持同步

使用 Git tag 觸發 Chart 打包和推送

Chart.lock 提交到版本庫,保證依賴版本可復現

5.1.2 Values 設計規范

所有可配置項都應有默認值,helm install裸裝必須能跑起來(至少在 dev 環境)

使用required函數標記生產環境必填項

敏感值不寫入 values 文件,使用 External Secrets Operator 或 --set 注入

布爾開關命名統一:xxx.enabled

資源配置使用嵌套結構:resources.requests.cpu,不要拍平成requestsCpu

# values.yaml 中用 required 標記必填項的模板用法
# templates/deployment.yaml
env:
-name:DATABASE_URL
 value:{{required"database.url is required for production".Values.database.url}}

5.1.3 模板組織

公共邏輯提取到_helpers.tpl

每個 Kubernetes 資源一個模板文件,命名與資源類型對應

復雜的條件渲染用命名模板封裝,而不是在資源模板中嵌套大量 if-else

使用{{- include ... | nindent N }}而非{{ template ... }}(template 不能管道傳遞)

5.1.4 Umbrella Chart 架構

platform-chart/
├── Chart.yaml     # type: application
├── values.yaml    # 全局默認值
├── charts/
│  ├── api/      # 子 Chart: API 服務
│  ├── web/      # 子 Chart: Web 前端
│  ├── worker/    # 子 Chart: 后臺任務
│  └── common/    # Library Chart
└── templates/
  ├── _helpers.tpl
  └── shared-configmap.yaml # 跨子 Chart 共享的資源

Umbrella Chart 的 values.yaml 中通過子 Chart 名稱作為 key 傳遞參數:

# platform-chart/values.yaml
global:
imageRegistry:registry.myorg.com
environment:production

api:
replicaCount:3
image:
 repository:registry.myorg.com/myapp-api
 tag:"2.1.0"

web:
replicaCount:2
image:
 repository:registry.myorg.com/myapp-web
 tag:"1.8.0"

worker:
replicaCount:5
image:
 repository:registry.myorg.com/myapp-worker
 tag:"2.1.0"

5.2 安全加固

5.2.1 RBAC 最小權限

# 為 Helm 操作創建專用 ServiceAccount(CI/CD 場景)
apiVersion:v1
kind:ServiceAccount
metadata:
name:helm-deployer
namespace:kube-system
---
apiVersion:rbac.authorization.k8s.io/v1
kind:ClusterRole
metadata:
name:helm-deployer
rules:
-apiGroups:["","apps","batch","networking.k8s.io","autoscaling"]
 resources:["*"]
 verbs:["get","list","watch","create","update","patch","delete"]
-apiGroups:[""]
 resources:["secrets"]
 verbs:["get","list","watch","create","update","patch","delete"]
 # Helm 存儲 Release 信息到 Secret
---
apiVersion:rbac.authorization.k8s.io/v1
kind:ClusterRoleBinding
metadata:
name:helm-deployer
roleRef:
apiGroup:rbac.authorization.k8s.io
kind:ClusterRole
name:helm-deployer
subjects:
-kind:ServiceAccount
 name:helm-deployer
 namespace:kube-system

5.2.2 Chart 安全掃描

# 使用 kubeaudit 掃描渲染后的 manifest
helm template myapp ./mychart -f values-prod.yaml | kubeaudit all -f -

# 使用 checkov 做合規掃描
helm template myapp ./mychart -f values-prod.yaml > rendered.yaml
checkov -f rendered.yaml --framework kubernetes

# 使用 trivy 掃描 Chart 中的配置問題
trivy config ./mychart

5.3 注意事項

5.3.1 CRD 管理陷阱

Helm 對 CRD 的處理有特殊限制:

CRD 放在crds/目錄下,僅在首次helm install時安裝

helm upgrade不會更新 CRD

helm uninstall不會刪除 CRD

因此 CRD 的生命周期管理需要額外處理:

# 手動更新 CRD
kubectl apply -f mychart/crds/

# 或者將 CRD 從 crds/ 移到 templates/ 中,但需要加注解防止意外刪除
# templates/crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
 name: myresources.myorg.com
 annotations:
 "helm.sh/resource-policy": keep  # uninstall 時保留

5.3.2 常見配置錯誤

錯誤模式 表現 修正方法
nindent 值錯誤 YAML 渲染后縮進不對,K8s API 拒絕 用helm template檢查輸出格式
with 塊內訪問頂級對象 {{ .Values.xxx }} 變成空 使用{{ $.Values.xxx }}
range 塊內的. . 指向當前迭代元素而非根 使用$引用根上下文
數字值未加 quote YAML 解析為科學計數法 用{{ .Values.xxx | quote }}
list 值用 --set 覆蓋 整個 list 被替換而非追加 使用--set-json或 -f 文件
Release 歷史膨脹 etcd 存儲壓力增大 設置--history-max 10

5.3.3 Release 存儲與 etcd 壓力

Helm v3 將 Release 信息存儲在 Kubernetes Secret 中(Base64 編碼 + gzip 壓縮)。每次 upgrade/rollback 都會創建新的 Secret。對于頻繁發布的應用,需要限制歷史版本數:

# 全局設置最大歷史版本
helm upgrade myapp ./mychart 
 -f values-prod.yaml 
 -n production 
 --history-max 10

# 清理舊 Release Secret
kubectl get secrets -n production -l owner=helm -l name=myapp --sort-by=.metadata.creationTimestamp

六、故障排查和監控

6.1 故障排查決策樹

helm install/upgrade 失敗
├── 報錯"YAML parse error"/"template render error"
│  ├── 檢查 helm template 輸出
│  ├── 定位到具體模板文件和行號
│  └── 常見:nindent 錯誤、括號未閉合、函數名拼寫
├── 報錯"cannot re-use a name"/"has no deployed releases"
│  ├── helm list --all -n  查看現有 Release
│  ├── 如果是 failed 狀態:helm uninstall --no-hooks
│  └── 如果是 pending-* 狀態:等待或強制清理
├── 報錯"timed out waiting for the condition"
│  ├── 檢查 Pod 狀態:kubectl get pods
│  ├── Hook Job 卡住:kubectl describe job
│  ├── Pod 未 Ready:kubectl describe pod / kubectl logs
│  └── 資源配額不足:kubectl get resourcequota
├── 報錯"admission webhook denied"
│  ├── 檢查 ValidatingWebhookConfiguration
│  ├── 查看 webhook 服務是否可用
│  └── 臨時繞過:刪除 webhook 或加 exclude annotation
├── 部署成功但功能異常
│  ├── helm get values 確認 values 生效
│  ├── helm get manifest 確認渲染結果
│  ├── kubectl describe / logs 查看運行時狀態
│  └── Service/Endpoints/Ingress 鏈路檢查
└── OCI Registry 相關
  ├── 401 Unauthorized:檢查 helm registry login
  ├── not found:確認 Chart 名稱和版本號
  └── timeout:檢查網絡和 DNS

6.2 Helm 操作監控

6.2.1 Release 狀態監控

# Prometheus 告警規則:檢測 Helm Release 異常狀態
# 需要 helm-exporter(https://github.com/sstarber/helm-exporter)
groups:
-name:helm-release-alerts
 rules:
  -alert:HelmReleaseFailed
   expr:helm_chart_info{status="failed"}==1
   for:5m
   labels:
    severity:critical
   annotations:
    summary:"Helm Release{{ $labels.release }}處于 failed 狀態"
    description:"Release{{ $labels.release }}in namespace{{ $labels.namespace }}has been in failed state for 5 minutes. Chart:{{ $labels.chart }}"

  -alert:HelmReleasePending
   expr:helm_chart_info{status=~"pending-.*"}==1
   for:15m
   labels:
    severity:warning
   annotations:
    summary:"Helm Release{{ $labels.release }}處于 pending 狀態"
    description:"Release{{ $labels.release }}in namespace{{ $labels.namespace }}has been pending for 15 minutes."

6.2.2 CI/CD 部署指標

在 CI/CD 流水線中記錄 Helm 部署的關鍵指標:

# 部署耗時統計
START_TIME=$(date +%s)
helm upgrade --install myapp ./mychart -f values-prod.yaml -n production --atomic --timeout 10m
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))

# 推送指標到 Prometheus Pushgateway
cat <

6.3 日志排查路徑

# Helm 自身調試日志
helm upgrade myapp ./mychart -f values-prod.yaml -n production --debug 2>&1 | tee helm-debug.log

# 從 debug 日志中提取關鍵信息
grep -E"(error|Error|FAIL|WARNING)"helm-debug.log

# 查看 Helm 發送給 K8s API 的請求
helm upgrade myapp ./mychart -f values-prod.yaml -n production --debug --dry-run 2>&1 | head -100

6.4 常見問題速查

問題 快速診斷 快速修復
"release: already exists" helm list --all -n helm uninstall -n
"UPGRADE FAILED: has no deployed releases" helm list --pending -A helm uninstall --no-hooks -n 后重新 install
Secret 超過 1MB(etcd 限制) helm get manifest | wc -c 拆分 Chart 或減少嵌入數據
lookup 在 CI 中失敗 helm template 模式不支持 lookup 改用--dry-run=server或移除 lookup
子 Chart values 未生效 helm get values 檢查層級 確認 values 中用子 Chart 名稱作為 key

七、總結

7.1 技術要點回顧

Helm v3.17+ 去除 Tiller,直接使用 kubeconfig 鑒權,OCI Registry 作為原生 Chart 存儲后端

Chart 結構的核心三要素:Chart.yaml(元數據)、values.yaml(參數化)、templates/(模板渲染)

Go 模板語法中最常見的坑:with塊上下文切換、range塊中.的含義變化、nindent縮進值

Values 分層覆蓋遵循「默認值 → 父 Chart → -f 文件 → --set」的優先級鏈

Hook 的before-hook-creation刪除策略是防止 Job 沖突的關鍵配置

--atomic參數保證升級失敗時自動回滾,是生產環境部署的必選項

Library Chart + Umbrella Chart 是企業級多微服務 Chart 管理的標準架構模式

Release 歷史存儲在 etcd 中,必須通過--history-max限制版本數量

7.2 排障鏈路總結

完整的 Helm 部署排障鏈路:

helm lint → helm template → helm diff → helm upgrade --dry-run
→ helm upgrade(實際執行)
→ 失敗時:helm status → helmhistory→ kubectl describe/logs
→ 定位到具體層級(Chart 語法 / Values 渲染 / K8s API / 運行時 / Hook)
→ 修復后:helm upgrade --atomic → helmtest→ 驗證

7.3 進階學習方向

Helm SDK 集成:在 Go 程序中直接調用 Helm SDK 實現自定義部署控制器

Chart Testing(ct)工具:在 CI 中自動檢測 Chart 變更并運行集成測試

ArgoCD + Helm:GitOps 模式下的 Helm Chart 自動同步和漂移檢測

Sigstore 簽名驗證:對 Chart 進行數字簽名,在部署前驗證完整性

7.3 參考資料

Helm 官方文檔— Chart 開發、最佳實踐、命令參考

Helm GitHub— 源碼、Issue 跟蹤

Artifact Hub— 公共 Chart 搜索

Helmfile 文檔— 多 Release 編排

附錄

A. Helm 命令速查表

# Chart 開發
helm create mychart             # 創建 Chart 腳手架
helm lint ./mychart --strict         # 靜態檢查
helm template myapp ./mychart -f values.yaml # 本地渲染
helm package ./mychart            # 打包為 .tgz
helm dependency update ./mychart       # 更新依賴
helm dependency list ./mychart        # 查看依賴狀態
helm show chart ./mychart          # 查看 Chart.yaml
helm show values ./mychart          # 查看默認 values

# OCI Registry
helm registry login         # 登錄
helm push  oci:///# 推送
helm pull oci:/// --version X # 拉取
helm show chart oci:///    # 查看遠程 Chart

# Release 管理
helm install   -f  -n       # 安裝
helm upgrade --install   -f  -n  # 升級(不存在則安裝)
helm rollback   -n           # 回滾
helm uninstall  -n                # 卸載
helm list -A                        # 列出所有 Release
helm status  -n                  # 查看狀態
helmhistory -n                 # 查看歷史

# 調試
helm upgrade   --dry-run --debug       # 模擬升級
helm get values  -n                # 查看當前 values
helm get manifest  -n               # 查看當前 manifest
helm get hooks  -n                # 查看 hooks
helmtest -n                   # 運行測試

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 容器
    +關注

    關注

    0

    文章

    533

    瀏覽量

    23001
  • kubernetes
    +關注

    關注

    0

    文章

    268

    瀏覽量

    9516

原文標題:Helm包管理實戰:企業級應用模板化部署

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Kubernetes Helm入門指南

    Helm 是 Kubernetes 的包管理工具,它允許開發者和系統管理員通過定義、打包和部署應用程序來簡化 Kubernetes 應用的管理
    的頭像 發表于 04-30 13:42 ?3149次閱讀
    Kubernetes <b class='flag-5'>Helm</b>入門指南

    2017年企業級SaaS服務發展趨勢?

    域創業項目數量有近400家,涉及20余個領域,包括CRM、ERP、HR、OA、企業報銷、企業商旅、及協同辦公、收銀支付、考勤管理等。 在經過兩三年的市場洗禮后,2017企業級服務的市場
    發表于 07-17 10:22

    使用Helm 在容器服務k8s集群一鍵部署wordpress

    摘要: Helm 是啥? 微服務和容器給復雜應用部署管理帶來了極大的挑戰。Helm是目前Kubernetes服務編排領域的唯一開源子項目
    發表于 03-29 13:38

    企業級的LInux系統日志管理

    企業級LInux系統日志管理
    發表于 05-29 11:33

    iPhone OS企業級部署指南

    iPhone OS企業級部署指南
    發表于 12-10 14:51 ?57次下載

    庫神企業級包管理系統介紹

    針對目前的現狀,為了更好的服務于區塊鏈領域企業,包括加密資產交易平臺、托管平臺、支付平臺、金融衍生業務平臺等,庫神公司自主研發出一套綜合性加密資產管理系統,即庫神企業級包管理系統(C
    發表于 04-25 11:00 ?2154次閱讀

    Helm Kubernetes包管理

    helm.zip
    發表于 04-27 14:25 ?2次下載
    <b class='flag-5'>Helm</b> Kubernetes<b class='flag-5'>包管理</b>器

    Helm常用命令(chart安裝、升級、回滾、卸載等操作)

    Helm 針對 Kubernetes 的 Helm 包管理器。
    的頭像 發表于 09-13 14:54 ?9258次閱讀

    Helm部署MinIO集群

    Helm部署MinIO集群
    的頭像 發表于 12-03 09:44 ?1621次閱讀
    <b class='flag-5'>Helm</b><b class='flag-5'>部署</b>MinIO集群

    DeepSeek企業級部署實戰指南:以Raksmart企業服務器為例

    隨著人工智能技術的快速發展,DeepSeek作為一款強大的AI工具,正在成為企業智能轉型的重要驅動力。本文將結合Raksmart企業服務器的實際案例,詳細解析DeepSeek的企業級
    的頭像 發表于 03-12 11:33 ?1162次閱讀

    Kubernetes包管理工具Helm的安裝和使用

    Helm 可以幫助我們管理 Kubernetes 應用程序 - Helm Charts 可以定義、安裝和升級復雜的 Kubernetes 應用程序,Charts 包很容易創建、版本管理
    的頭像 發表于 03-13 16:06 ?2247次閱讀

    Helm倉庫管理常用配置

    Helm 倉庫(Repository)是存儲 Helm 圖表(Chart)的地方,類似于軟件包管理器的倉庫(如 apt、yum 倉庫)。
    的頭像 發表于 06-07 09:27 ?1382次閱讀

    Helm詳細介紹和使用

    Helm是Kubernetes 應用的包管理工具,主要用來管理 Charts,類似Linux系統的yum。
    的頭像 發表于 06-17 13:56 ?1348次閱讀

    企業級MySQL數據庫管理指南

    在當今數字化時代,MySQL作為全球最受歡迎的開源關系型數據庫,承載著企業核心業務數據的存儲與處理。作為數據庫管理員(DBA),掌握MySQL的企業級部署、優化、維護技能至關重要。本文
    的頭像 發表于 07-09 09:50 ?773次閱讀

    Helm包管理模板部署實戰

    直接用kubectl管理K8s資源,10個微服務就要維護幾十個YAML文件,版本管理靠文件夾命名,回滾靠手動替換文件。Helm把一組相關的K8s資源打包成Chart,支持模板
    的頭像 發表于 02-26 16:37 ?315次閱讀