Helm Chart 开发指南:从入门到生产就绪
写在前面 :这篇文章源于今晚(2026-03-10)的 Helm Chart 开发实战。我们为 OpenClaw 创建了完整的 Helm Chart,支持多环境配置、 Harbor 发布和一键部署。
一、为什么选择 Helm? 1.1 真实痛点 没有 Helm 时 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 kubectl create configmap openclaw-config --from-file=openclaw.json kubectl create secret generic openclaw-secrets --from-literal=... kubectl apply -f deployment.yaml kubectl apply -f service.yaml kubectl apply -f ingress.yaml kubectl apply -f rbac.yaml ...
有了 Helm 后 :
1 2 3 4 5 6 7 8 9 10 11 helm install openclaw ./openclaw-chart -n openclaw helm upgrade openclaw ./openclaw-chart -n openclaw --set image.tag=1.0.4 helm rollback openclaw 1 -n openclaw helm status openclaw -n openclaw
1.2 Helm 的核心价值 模板化 :
一套 Chart,多环境复用
参数化配置,避免硬编码
版本管理 :
Chart 有版本号(SemVer)
Release 有修订历史
支持回滚到任意版本
依赖管理 :
Chart 可以依赖其他 Chart
自动安装依赖
版本锁定
生态丰富 :
Artifact Hub 有海量 Chart
企业可以搭建私有仓库(Harbor)
二、Chart 结构详解 2.1 标准目录结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 openclaw/ ├── Chart.yaml # Chart 元数据(必需) ├── values.yaml # 默认配置值(必需) ├── values-prod.yaml # 生产环境配置(可选) ├── values-staging.yaml # 测试环境配置(可选) ├── .helmignore # 忽略文件(可选) ├── charts/ # 依赖的 Charts(可选) └── templates/ # Kubernetes 模板(必需) ├── _helpers.tpl # 模板辅助函数 ├── deployment.yaml ├── service.yaml ├── configmap.yaml ├── secret.yaml ├── serviceaccount.yaml ├── rbac.yaml └── ingress.yaml
2.2 Chart.yaml 详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 apiVersion: v2 name: openclaw description: OpenClaw AI Agent - K8s 部署 Chart type: application version: 1.0 .0 appVersion: "1.0.3-feishu" keywords: - ai - agent - openclaw - kubernetes maintainers: - name: John email: john@example.com url: https://blog.sharezone.cn dependencies: - name: postgresql version: "10.0.0" repository: "https://charts.bitnami.com/bitnami" condition: postgresql.enabled - name: redis version: "17.0.0" repository: "https://charts.bitnami.com/bitnami" condition: redis.enabled
关键字段说明 :
apiVersion: Helm v3 必须是 v2
version: Chart 版本号,遵循 SemVer
appVersion: 应用版本号,通常对应 Docker 镜像 tag
dependencies: 依赖的其他 Chart
2.3 values.yaml 详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 replicaCount: 1 image: repository: hb.test/crystalforge/openclaw-cn-base tag: "1.0.3-feishu" pullPolicy: IfNotPresent pullSecrets: [] service: type: ClusterIP port: 18789 targetPort: 18789 resources: limits: cpu: 2000m memory: 4Gi requests: cpu: 500m memory: 2Gi livenessProbe: httpGet: path: /health port: 18789 initialDelaySeconds: 60 periodSeconds: 30 readinessProbe: httpGet: path: /ready port: 18789 initialDelaySeconds: 15 periodSeconds: 15 persistence: enabled: true storageClass: "csi-cephfs-sc" size: 50Gi accessModes: - ReadWriteMany env: TZ: "Asia/Shanghai" OPENCLAW_HOME: "/root/.openclaw" secrets: feishu: appId: "" appSecret: "" dashscope: apiKey: "" postgresql: enabled: false redis: enabled: false ingress: enabled: false className: "nginx" hosts: - host: openclaw.example.com paths: - path: / pathType: ImplementationSpecific tls: []
2.4 多环境配置 values-staging.yaml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 replicaCount: 1 image: tag: "1.0.3-feishu" resources: limits: cpu: 1000m memory: 2Gi requests: cpu: 250m memory: 1Gi persistence: size: 20Gi ingress: enabled: true hosts: - host: openclaw-staging.example.com
values-prod.yaml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 replicaCount: 2 image: tag: "1.0.3-feishu" resources: limits: cpu: 4000m memory: 8Gi requests: cpu: 1000m memory: 4Gi persistence: size: 100Gi ingress: enabled: true hosts: - host: openclaw.example.com tls: - secretName: openclaw-tls hosts: - openclaw.example.com
三、模板开发实战 3.1 _helpers.tpl 辅助函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 {{/* 展开 Chart 名称 */}} {{- define "openclaw.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* 创建完整的 Chart 名称(包含版本) */}} {{- define "openclaw.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* 创建标准标签 */}} {{- define "openclaw.labels" -}} helm.sh/chart: {{ include "openclaw.chart" . }} {{ include "openclaw.selectorLabels" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* 选择器标签 */}} {{- define "openclaw.selectorLabels" -}} app.kubernetes.io/name: {{ include "openclaw.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* 创建 ServiceAccount 名称 */}} {{- define "openclaw.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "openclaw.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }}
3.2 deployment.yaml 模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "openclaw.fullname" . }} labels: {{- include "openclaw.labels" . | nindent 4 }} annotations: description: "OpenClaw Gateway - AI 助手服务" spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "openclaw.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "openclaw.selectorLabels" . | nindent 8 }} annotations: prometheus.io/scrape: "true" prometheus.io/port: "18790" spec: serviceAccountName: {{ include "openclaw.serviceAccountName" . }} {{- with .Values.image.pullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }} :{{ .Values.image.tag }} " imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: gateway containerPort: {{ .Values.service.port }} protocol: TCP - name: metrics containerPort: 18790 protocol: TCP env: - name: TZ value: {{ .Values.env.TZ | quote }} - name: OPENCLAW_HOME value: {{ .Values.env.OPENCLAW_HOME | quote }} {{- range $key , $value := .Values.secrets }} {{- range $subKey , $subValue := $value }} - name: {{ $key | upper }}_{{ $subKey | upper }} valueFrom: secretKeyRef: name: {{ include "openclaw.fullname" $ }}-secrets key: {{ $key }}-{{ $subKey }} {{- end }} {{- end }} {{- with .Values.livenessProbe }} livenessProbe: {{- toYaml . | nindent 12 }} {{- end }} {{- with .Values.readinessProbe }} readinessProbe: {{- toYaml . | nindent 12 }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: - name: config-volume mountPath: /root/.openclaw subPath: config - name: workspace-volume mountPath: /openclaw/workspace subPath: workspace volumes: - name: config-volume persistentVolumeClaim: claimName: {{ include "openclaw.fullname" . }}-config-pvc - name: workspace-volume persistentVolumeClaim: claimName: {{ include "openclaw.fullname" . }}-workspace-pvc {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }}
3.3 service.yaml 模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Service metadata: name: {{ include "openclaw.fullname" . }} labels: {{- include "openclaw.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: {{ .Values.service.targetPort }} protocol: TCP name: gateway - port: 18790 targetPort: 18790 protocol: TCP name: metrics selector: {{- include "openclaw.selectorLabels" . | nindent 4 }}
3.4 secret.yaml 模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 {{- if .Values.secrets }} apiVersion: v1 kind: Secret metadata: name: {{ include "openclaw.fullname" . }}-secrets labels: {{- include "openclaw.labels" . | nindent 4 }} type: Opaque stringData: {{- range $key , $value := .Values.secrets }} {{- range $subKey , $subValue := $value }} {{ $key }}-{{ $subKey }}: {{ $subValue | quote }} {{- end }} {{- end }} {{- end }}
四、Harbor 发布流程 4.1 打包 Chart 1 2 3 4 5 6 7 helm lint ./openclaw helm package ./openclaw --version 1.0.0 --app-version "1.0.3-feishu"
4.2 推送到 Harbor 1 2 3 4 5 6 7 8 9 10 11 12 13 helm registry login hb.test \ --username crystalcreator \ --password 'glpat-FxE8Ka5HaApPLHnAQDtz' helm repo add harbor oci://hb.test/crystalforge helm push openclaw-1.0.0.tgz harbor helm search repo harbor/openclaw
4.3 从 Harbor 安装 1 2 3 4 5 6 7 8 9 10 11 helm install openclaw harbor/openclaw \ --version 1.0.0 \ -n openclaw \ --create-namespace helm install openclaw harbor/openclaw \ --version 1.0.0 \ -n openclaw \ -f values-prod.yaml
五、实战案例 5.1 部署 OpenClaw 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 helm repo add harbor oci://hb.test/crystalforge helm repo update kubectl create namespace openclaw kubectl apply -f pvc.yaml -n openclaw helm install openclaw harbor/openclaw \ --version 1.0.0 \ -n openclaw \ -f values-prod.yaml helm status openclaw -n openclaw kubectl get pods -n openclaw
5.2 升级应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 helm upgrade openclaw harbor/openclaw \ --version 1.0.1 \ -n openclaw \ --set image.tag=1.0.4-feishu helm history openclaw -n openclaw helm rollback openclaw -n openclaw helm rollback openclaw 1 -n openclaw
5.3 多环境部署 1 2 3 4 5 6 7 8 9 10 11 helm install openclaw-staging harbor/openclaw \ --version 1.0.0 \ -n openclaw-staging \ -f values-staging.yaml helm install openclaw-prod harbor/openclaw \ --version 1.0.0 \ -n openclaw-prod \ -f values-prod.yaml
六、踩坑记录 6.1 模板语法错误 问题 :
1 2 3 Error: template: openclaw/templates/deployment.yaml:25:12: executing "openclaw/templates/deployment.yaml" at <.Values.image.repository>: nil pointer evaluating interface {}.repository
原因 :
values.yaml 中缺少 image.repository 字段
模板中引用了不存在的值
解决 :
1 2 3 4 image: repository: hb.test/crystalforge/openclaw-cn-base tag: "1.0.3-feishu"
6.2 缩进错误 问题 :
1 2 Error: YAML parse error on openclaw/templates/deployment.yaml: error converting YAML to JSON: yaml: line 35: mapping values are not allowed in this context
原因 :
解决 :
1 2 3 4 5 helm template openclaw ./openclaw > /dev/null vim -c "set list" templates/deployment.yaml
6.3 依赖版本冲突 问题 :
1 Error: found in Chart.yaml, but missing in charts/ directory: postgresql, redis
原因 :
Chart.yaml 声明了依赖
但没有下载依赖 Chart
解决 :
1 2 3 4 5 6 7 helm dependency update ./openclaw
七、最佳实践 7.1 Chart 开发 ✅ 推荐做法 :
使用 _helpers.tpl 封装重复逻辑
为所有 values 提供默认值
使用条件判断控制资源创建
添加完整的 labels 和 annotations
提供多环境 values 文件
❌ 避免做法 :
硬编码值(应该参数化)
缺少默认值
复杂的模板逻辑(应该简化)
不验证 Chart(应该 helm lint)
7.2 版本管理 Chart 版本 :
遵循 SemVer(MAJOR.MINOR.PATCH)
破坏性变更升级 MAJOR
新功能升级 MINOR
Bug 修复升级 PATCH
App 版本 :
对应 Docker 镜像 tag
可以是 SemVer 或 Git commit hash
7.3 安全配置 Secrets :
不要提交到 Git
使用 values-secret.yaml(加入.gitignore)
或使用外部密钥管理(Vault)
RBAC :
最小权限原则
创建专用 ServiceAccount
限制命名空间范围
八、总结 8.1 核心要点
Chart 结构 :Chart.yaml、values.yaml、templates/
模板开发 :_helpers.tpl、条件判断、循环
多环境 :values-staging.yaml、values-prod.yaml
Harbor 发布 :打包、推送、安装
8.2 实战经验 这篇文章是今晚 1 小时实战的总结 :
创建 OpenClaw Helm Chart
配置 Harbor 仓库
测试多环境部署
记录踩坑经验
8.3 后续优化 本周 :
本月 :
作者 :John,高级技术架构师,CrystalForge 项目负责人时间 :2026-03-11 02:30地点 :深圳项目 :OpenClaw Helm Chart 开发实战