This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2026-05-18 00:17:59 +08:00

13 KiB
Raw Permalink Blame History

tags, create time
tags create time
helm
kubernetes
templating
kustomize
devops
2026-05-17 10:00

Helm 模板管理

概述

本文档系统讲解 Helm Chart 的编写与管理实践。内容覆盖:Chart 骨架、values.yaml 参数化、Go 模板语法、多环境值覆盖、Chart 依赖管理、Hooks 机制,以及与 ArgoCD 的集成方式。架构决策参见 ../03-CICD与GitOps。

为什么需要 Helm?

[!question] 每个服务几十个 YAML 文件,怎么管理?

一个典型的 order-service 部署包含:Deployment、Service、Ingress、ConfigMap、HPA、NetworkPolicy、PDB……当你有 50 个服务,每个都要维护这么一堆模板——这是重复劳动的噩梦。

Helm 提供了两个核心抽象:

概念 类比 说明
Chart NPM 包 / Maven jar 打包格式,定义"我要部署什么"
Release npm install 的实例 运行实例,同一个 Chart 可以有多个 Release(staging/prod)
# 创建 Chart 骨架
helm create order-service

# 目录结构一览
charts/order-service/
├── Chart.yaml          # 元信息 (name, version, description)
├── values.yaml         # 默认值 —— 你唯一需要经常改的文件
├── templates/          # Go 模板文件
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ingress.yaml
└── .helmignore         # 类似 .gitignore

Helm Release 生命周期

flowchart LR
    A["install"] --> B["running"]
    C["upgrade"] --> B
    D["rollback"] --> B
    E["uninstall"] --> B
    style A fill:#e1f5fe
    style C fill:#fff3e0
    style D fill:#fce4ec
    style E fill:#f3e5f5

第一步:让配置全部参数化

values.yaml 中存放所有可变参数,遵循"一处定义、多处引用"原则:

# values.yaml
replicaCount: 2

image:
  repository: registry.example.com/order-service
  tag: "v1.2.3"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 8080

resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 500m
    memory: 512Mi

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

templates/deployment.yaml 中使用 {{ }} 引用:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  namespace: {{ .Release.Namespace }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          resources: {{ toYaml .Values.resources | nindent 12 }}

[!tip] 两个关键技巧

  1. toYaml .X | nindent 12:将嵌套的对象序列化为 YAML 并缩进。如果直接写 {{ .Values.resources }},输出会压缩成一行,Kubernetes 无法解析。
  2. - 修剪空白:{{- if ... 和 {{- end }} 中的 - 会消除模板前后多余的换行符,避免生成空行导致 YAML 格式错误。

Helm 内置对象速查

Helm 对象 访问方式 用途
.Release .Release.Name, .Release.Namespace 当前发布实例的信息
.Chart .Chart.Name, .Chart.Version Chart 本身的元信息
.Values .Values.replicaCount 等 用户传入的值(合并了 values.yaml + --set)
.Capabilities .Capabilities.KubeVersion 集群 API 版本信息
.Files .Files.Get "config.ini" 读取同目录下的非模板文件
.Notes 渲染后显示给用户的提示文本 Post-install 使用说明

第二步:多环境值覆盖

通过 values 文件叠加实现不同环境的差异化:

# Staging 使用命令行临时覆盖
helm upgrade --install order-service ./charts/order-service \
  --namespace=staging \
  --set image.tag=v1.2.4-dev

# Production 叠加额外值文件
helm upgrade --install order-service ./charts/order-service \
  --namespace=production \
  --values charts/order-service/values.prod.yaml \
  --set image.tag=v1.2.3

典型 values.prod.yaml(只写与默认值不同的部分):

replicaCount: 5

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

autoscaling:
  minReplicas: 5
  maxReplicas: 50

[!tip] Values 文件合并优先级(低 → 高)

  1. values.yaml — 默认值
  2. values.prod.yaml — 环境变量覆盖文件
  3. --set image.tag=xxx — 命令行参数(最高优先级)
flowchart TD
    A["values.yaml"] --> D["合并结果"]
    B["values.prod.yaml"] --> D
    C["--set image.tag=v1.2.3"] --> D
    D --> E[".Values"]

[!warning] 常见陷阱:JSON Patch

当使用 --set 修改嵌套字段时,Helm 使用 JSON Patch,意味着它会替换整个对象而非合并。例如 --set resources.limits.cpu="1" 会将 memory 字段丢弃。对于复杂嵌套,优先使用独立的 values 文件。

第三步:条件渲染与循环

条件渲染

用 Helm 的条件语法处理可选资源:

# templates/ingress.yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}
spec:
  rules:
    - host: {{ .Values.ingress.host | quote }}
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ .Release.Name }}
                port:
                  number: {{ .Values.service.port }}
{{- end }}

[!note] quote 管道函数

当值的类型不确定时(可能是字符串也可能是数字),加 | quote 确保输出带引号,避免 true/false 被 YAML 解析为布尔值。

Range 循环生成多个资源

当需要批量创建多个 ServiceMonitor、ConfigMap 等时,range 非常有用:

# templates/servicemonitor.yaml
{{- range $key, $value := .Values.extraServices }}
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: {{ $.Release.Name }}-{{ $key }}
spec:
  selector:
    matchLabels:
      app: {{ $.Release.Name }}
  endpoints:
    - port: {{ $value.port }}
      interval: {{ $value.interval | default "30s" }}
{{- end }}

对应的 values:

extraServices:
  grpc-exporter:
    port: grpc
    interval: 15s
  prometheus-path:
    port: metrics
    interval: 10s

[!example] 生成结果

上面这段模板会为每个 extraServices 条目生成一个独立的 ServiceMonitor。注意 $key 绑定到键名(grpc-exporter),而 $value 绑定到对应对象。使用 $.Release.Name(带美元前缀)是因为在 range 内部 . 已被重绑定。

第四步:复用模板片段

随着 Chart 膨胀,deployment.yaml 和 service.yaml 之间会产生大量重复逻辑。Helm 提供 _helpers.tpl 来统一管理可复用的模板片段。

_helpers.tpl 标准写法

{{/*
Common labels — 所有资源统一标注 */}}
{{- define "order.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}

{{/*
Selector labels — Deployment selector 必须使用固定值 */}}
{{- define "order.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

在模板中引用

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  labels:
    {{- include "order.labels" . | nindent 4 }}
spec:
  selector:
    matchLabels:
      {{- include "order.selectorLabels" . | nindent 6 }}

[!tip] include vs template

  • include "name" .:返回值,可通过 | nindent 继续管道处理 —— 适合注入 label、annotation
  • template "name" .:直接输出,不返回结果 —— 很少用,通常只在特殊情况下需要

Chart 依赖与仓库管理

大型项目往往不会从零编写所有模板,而是复用社区已有的 Chart。

声明子依赖

# Chart.yaml
apiVersion: v2
name: order-service
type: application
version: 1.2.0
appVersion: "1.2.3"

dependencies:
  - name: postgresql
    version: "15.0.0"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: "18.0.0"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled
  • condition 告诉 Helm:当 values 中对应路径为 false 时,跳过安装该子 Chart。
  • 子 Chart 的 values 通过 postgresql.xxx / redis.xxx 命名空间传入。

常用命令

helm dependency update ./charts/order-service   # 拉取 / 更新子依赖
helm dependency build ./charts/order-service    # 仅从 Chart.lock 构建(无网络变更时使用)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo prometheus                      # 搜索公开 Chart

[!note] Chart.yaml 的 apiVersion

  • apiVersion: v1(Helm 2):依赖写在 dependencies: 数组里,语义不同
  • apiVersion: v2(Helm 3):引入 requirements.yaml 的概念整合到 Chart.yaml 自身,推荐使用 v2

与 ArgoCD 集成

ArgoCD 原生支持将 Helm Chart 作为 Application source,自动解析 values.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: order-service
spec:
  source:
    repoURL: https://github.com/team/k8s-manifests.git
    targetRevision: main
    path: charts/order-service
    helm:
      parameters:
        - name: image.tag
          value: v1.2.3
        - name: replicaCount
          value: "5"

[!tip] ArgoCD + Helm 进阶用法

  • helm.valueFrom:从 ConfigMap 或 Secret 取值,避免把敏感值暴露在 Git 中
  • helm.fileParameters:从文件加载大段配置值,适合 CI/CD 流水线场景
  • helm.ignoreMissingValueFiles: true:允许某些 values 文件按环境选择性存在

Hooks:在关键时机执行自定义逻辑

Helm Hooks 允许你在 Release 的生命周期事件中挂载自定义 Job 或 Pod:

# templates/migrate-db.yaml
{{- if .Values.dbMigration.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-db-migrate
  annotations:
    "hooks.helm.sh/hook": pre-upgrade,pre-install
    "hooks.helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          command: ["./migrate", "up"]
      restartPolicy: Never
{{- end }}
Hook 事件 触发时机
pre-install 资源创建之前(如数据库迁移)
post-install 资源创建之后(如初始化数据)
pre-upgrade 升级操作之前
post-upgrade 升级操作之后
pre-delete 删除操作之前(如优雅停机通知)
post-delete 删除操作之后

调试与测试

可视化渲染结果

# 查看渲染后的纯 YAML(不做实际部署)
helm template my-release ./charts/order-service

# 指定命名空间和 values 文件
helm template my-release ./charts/order-service \
  --namespace=staging \
  -f values.staging.yaml

[!tip] 调试三件套

  1. helm lint ./charts/order-service:静态检查,快速定位语法问题
  2. helm template:看渲染后的全量 YAML,逐段排查问题
  3. helm diff upgrade --install ...:配合 helm-diff 插件,对比升级前后的差异

Dry Run

# 模拟部署(服务端校验,但不会真正改变集群状态)
helm upgrade --install my-release ./charts/order-service \
  --dry-run --debug

最佳实践

原则 具体做法
单一真相源 所有可配参数集中在 values.yaml,模板层只做引用不做硬编码
Helper 集中管理 公共 labels / selectors / annotations 一律放在 _helpers.tpl
条件最小化 能用 defaults 解决的就不加 if;减少分支能大幅降低测试复杂度
版本号分离 Chart.yaml 的 version 管 Chart 本身迭代,appVersion 管应用版本
先 lint 再 push CI 中加入 helm lint + helm template --strict 门禁
值文件只写差异 values.prod.yaml 只覆盖与默认值不同的字段,保持可读性

何时用裸 YAML vs Helm vs Kustomize?

场景 推荐方案 理由
< 10 个服务 Kustomize / 裸 YAML 复杂度高于收益
10~50 个服务 Helm 模板复用价值明显
> 50 个服务 Helm + Kustomize overlays Helm 管模板,Kustomize 管环境差异

[!note] Kustomize 的定位

Kustomize 不依赖 Go templating,更轻量。适合"基于同一套模板,按环境覆盖差异配置"的场景。

常见组合:Helm 生成基础模板,Kustomize overlay 处理 staging/prod 差异。

关联笔记