Helm:Kubernetes 包管理利器

深入理解 Helm 的 Chart 结构、模板语法、values 配置管理,以及 Helm 在 CI/CD 中的最佳实践。

概述

Helm 是 Kubernetes 的包管理器,类似 Linux 的 apt/yum 或前端的 npm。它允许开发者将 Kubernetes 资源打包成可重用的 Chart,方便分发和部署。本文将深入探讨:

学习目标

  • 理解 Helm 的设计理念与核心概念
  • 掌握 Chart 的目录结构和模板语法
  • 学会使用 values 文件进行配置管理
  • 了解 Helm 仓库的创建与维护
  • 掌握 Helm 在 CI/CD 中的集成方法

Helm 核心概念

为什么需要 Helm?

┌─────────────────────────────────────────────────────────────────┐
│                    Helm 解决的问题                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   传统方式:直接部署 YAML                                        │
│                                                                 │
│   ├── deployment.yaml                                          │
│   ├── service.yaml                                            │
│   ├── configmap.yaml                                          │
│   ├── ingress.yaml                                            │
│   └── secret.yaml                                             │
│                                                                 │
│   问题:                                                         │
│   ✗ 大量文件难以管理                                           │
│   ✗ 环境差异需要复制多份                                        │
│   ✗ 版本跟踪困难                                               │
│   ✗ 难以复用和分享                                             │
│                                                                 │
│   Helm 方式:Chart 包                                          │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  myapp-1.0.0.tgz(Chart 包)                            │   │
│   │                                                          │   │
│   │  ├── Chart.yaml                                        │   │
│   │  ├── values.yaml                                      │   │
│   │  └── templates/                                        │   │
│   │       ├── deployment.yaml                              │   │
│   │       ├── service.yaml                                 │   │
│   │       └── ...                                          │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   优点:                                                         │
│   ✓ 一键部署/升级/回滚                                         │
│   ✓ 环境差异化配置                                             │
│   ✓ 版本管理和可复现                                          │
│   ✓ 社区分享和复用                                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Helm 核心术语

┌─────────────────────────────────────────────────────────────────┐
│                    Helm 核心概念                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────┐    ┌────────────┐    ┌────────────┐               │
│  │   Repo  │    │   Chart    │    │  Release   │               │
│  ├─────────┤    ├────────────┤    ├────────────┤               │
│  │ Chart   │    │ 打包格式   │    │ 已部署实例 │               │
│  │ 存储仓库 │    │ 包含模板和 │    │ Chart 运行 │               │
│  │         │    │ 配置       │    │ 在集群的   │               │
│  │ helm.sh │    │            │    │ 实例       │               │
│  └─────────┘    └────────────┘    └────────────┘               │
│                     │                    │                     │
│                     │ install            │                     │
│                     ▼                    ▼                     │
│                ┌────────────┐      ┌────────────┐             │
│                │   Version  │      │  History   │             │
│                ├────────────┤      ├────────────┤             │
│                │ Chart 版本 │      │ 升级/回滚  │             │
│                │ 1.0.0 →    │      │  历史记录   │             │
│                │ 2.0.0      │      │            │             │
│                └────────────┘      └────────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Helm 工作原理

┌─────────────────────────────────────────────────────────────────┐
│                    Helm 工作流程                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户命令:helm install myapp ./myapp-chart                     │
│                                                                 │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    Helm Client                          │   │
│  │                                                          │   │
│  │  1. 读取 Chart.yaml                                     │   │
│  │  2. 合并 values.yaml(+ 用户自定义值)                   │   │
│  │  3. 执行模板渲染(Go template)                          │   │
│  │  4. 生成最终 YAML                                       │   │
│  │  5. 发送到 kube-apiserver                               │   │
│  │  6. 保存 release 信息到 ConfigMap/Secret                │   │
│  │                                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                 Kubernetes Cluster                      │   │
│  │                                                          │   │
│  │  Deployment ← Service ← ConfigMap ← Secret              │   │
│  │                                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Chart 结构

Chart 目录结构

mychart/
├── Chart.yaml          # Chart 元数据(名称、版本、依赖等)
├── values.yaml         # 默认配置
├── values.schema.json   # 配置校验 schema(可选)
├── charts/             # 依赖的子 Chart
│   └── common-1.0.0.tgz
├── templates/          # Kubernetes 资源模板
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── _helpers.tpl    # 公共模板函数
│   └── NOTES.txt       # 部署后显示的说明
└── crds/              # 自定义 CRD(可选)
    └── mycrd.yaml

Chart.yaml 详解

# Chart.yaml
apiVersion: v2                    # Chart API 版本(v2 支持依赖)
name: myapp                       # Chart 名称
description: My Kubernetes application  # 描述
type: application                 # application 或 library
version: 1.0.0                    # Chart 版本(语义化)
appVersion: "2.0"                 # 应用版本
keywords:                         # 关键字(用于搜索)
  - web
  - http
  - api
home: https://github.com/myorg/myapp  # 主页
sources:                          # 代码仓库
  - https://github.com/myorg/myapp
maintainers:                      # 维护者
  - name: John Doe
    email: john@example.com
dependencies:                     # Chart 依赖(Helm v3)
  - name: common
    version: "1.x.x"
    repository: "https://charts.bitnami.com"
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com"
    condition: redis.enabled        # 条件启用
    tags:
      - cache

values.yaml 结构

# values.yaml - 默认配置
# 全局配置(所有模板可用)
global:
  imageRegistry: myregistry.com
  imagePullSecrets:
    - name: my-registry-secret

# 镜像配置
image:
  repository: myapp
  tag: "1.0.0"
  pullPolicy: IfNotPresent

# 副本数
replicaCount: 3

# 服务配置
service:
  type: ClusterIP
  port: 80

# 资源配置
resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 100m
    memory: 128Mi

# Ingress 配置
ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: myapp-tls
      hosts:
        - myapp.example.com

# 持久化存储
persistence:
  enabled: true
  size: 10Gi
  storageClass: "fast-storage"

# 标签和注解
labels: {}
annotations: {}

模板语法

Go 模板基础

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}         # Release 名称
  labels:
    app: {{ include "mychart.fullname" . }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
    version: {{ .Chart.AppVersion }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ include "mychart.fullname" . }}
  template:
    metadata:
      labels:
        app: {{ include "mychart.fullname" . }}
    spec:
      serviceAccountName: {{ .Release.Name }}-sa
      containers:
      - name: {{ .Chart.Name }}
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - name: http
          containerPort: {{ .Values.service.port }}
          protocol: TCP

内置对象

┌─────────────────────────────────────────────────────────────────┐
│                    Helm 内置对象                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  .Release                                                      │
│  ├── .Release.Name       # Release 名称                        │
│  ├── .Release.Namespace  # 命名空间                             │
│  ├── .Release.Service    # 服务名称(helm)                     │
│  ├── .Release.Revision   # 版本号(1, 2, 3...)                │
│  └── .Release.IsUpgrade  # 是否升级                             │
│                                                                 │
│  .Chart                                                        │
│  ├── .Chart.Name         # Chart 名称                          │
│  ├── .Chart.Version      # Chart 版本                          │
│  ├── .Chart.AppVersion   # 应用版本                            │
│  └── .Chart.Description  # 描述                                │
│                                                                 │
│  .Values                               # 用户提供的 values      │
│  .Values.image.repository               # 访问值                  │
│                                                                 │
│  .Capabilities                                          │
│  ├── .Capabilities.APIVersions.Has apps/v1        # API 可用     │
│  └── .Capabilities.KubeVersion           # K8s 版本             │
│                                                                 │
│  .Files                    # Chart 内的文件                      │
│  .Files.Get "config.yaml" # 获取文件内容                        │
│                                                                 │
│  .Capabilities.APIVersions.Has networking.k8s.io/v1/Ingress    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

模板函数

# 常用模板函数

# 字符串函数
{{ .Values.name | default "default-name" }}
{{ .Values.name | upper }}
{{ .Values.name | lower }}
{{ .Values.name | title }}
{{ .Values.name | trim }}
{{ "hello world" | replace "world" "helm" }}

# 数值函数
{{ .Values.replicaCount | int }}
{{ 1.234 | printf "%.2f" }}

# 条件判断
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}

# with 作用域
{{- with .Values.image }}
image: {{ .repository }}:{{ .tag }}
{{- end }}

# range 循环
{{- range .Values.env }}
- name: {{ .name }}
  value: {{ .value | quote }}
{{- end }}

# default 值
image: {{ .Values.image.repository | default "nginx" }}

# required 值(必须存在)
name: {{ required "image name is required" .Values.image.name }}

# lookup 获取资源
{{- $existing := lookup "v1" "ConfigMap" .Release.Namespace "my-config" }}
{{- if $existing }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
data: {{ $existing.data }}
{{- else }}
...
{{- end }}

命名模板(_helpers.tpl)

# templates/_helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
*/}}
{{- define "mychart.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 }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "mychart.labels" -}}
app: {{ include "mychart.name" . }}
chart: {{ include "mychart.chart" . }}
release: {{ .Release.Name }}
{{- end }}

NOTES.txt

# templates/NOTES.txt
Thank you for installing {{ .Chart.Name }}.

Your release is named {{ .Release.Name }}.

To learn more about the release, try:

  $ helm get all {{ .Release.Name }}

To get the application URL:

{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
  http://{{ .host }}
{{- end }}
{{- else }}
  # Port forward for local testing
  $ kubectl port-forward svc/{{ include "mychart.fullname" . }} {{ .Values.service.port }}:80
{{- end }}

To upgrade the release:
  $ helm upgrade {{ .Release.Name }} .

To uninstall the release:
  $ helm uninstall {{ .Release.Name }}

条件与循环

条件渲染

# 启用/禁用配置
{{- if .Values.feature.enabled }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: feature-config
data:
  feature-flag: "true"
{{- end }}

# 条件赋值
{{ $image := .Values.image.repository }}
{{- if .Values.global.imageRegistry }}
{{ $image = printf "%s/%s" .Values.global.imageRegistry .Values.image.repository }}
{{- end }}

# 多条件
{{- if and .Values.ingress.enabled .Values.ingress.tls.enabled }}
tls:
{{- range .Values.ingress.tls }}
  - secretName: {{ .secretName }}
    hosts:
{{- range .hosts }}
      - {{ . }}
{{- end }}
{{- end }}
{{- end }}

循环遍历

# 遍历列表
{{- range .Values.env }}
- name: {{ .name }}
  value: {{ .value | quote }}
{{- end }}

# 遍历 Map
{{- range $key, $value := .Values.config }}
- name: {{ $key }}
  value: {{ $value | quote }}
{{- end }}

# 遍历带索引
{{- range $index, $service := .Values.services }}
# Service {{ $index }}: {{ $service.name }}
{{- end }}

with 作用域

# 改变当前作用域
{{- with .Values.service }}
type: {{ .type }}
port: {{ .port }}
{{- end }}

# with 内的 else
{{- with .Values.persistence }}
enabled: true
size: {{ .size }}
{{- else }}
enabled: false
{{- end }}

依赖管理

Chart 依赖

# Chart.yaml
apiVersion: v2
name: myapp
version: 1.0.0
dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com"
    condition: postgresql.enabled
    tags:
      - database

  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com"
    condition: redis.enabled
    tags:
      - cache

  - name: common
    version: "1.x.x"
    repository: "https://charts.bitnami.com"

依赖下载

# 初始化依赖
helm dependency update ./mychart

# 查看依赖
helm dependency list ./mychart

# 输出:
# NAME          VERSION    REPOSITORY                    STATUS
# postgresql    12.x.x     https://charts.bitnami.com    fetched
# redis         17.x.x     https://charts.bitnami.com    fetched
# common        1.x.x      https://charts.bitnami.com    fetched

# 更新依赖
helm dependency update ./mychart

# 构建 Chart(打包时)
helm package ./mychart

子 Chart 访问

┌─────────────────────────────────────────────────────────────────┐
│                    Chart 依赖作用域                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  父 Chart                                                       │
│                                                                 │
│  values.yaml                                                   │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ global:                                                 │   │
│  │   color: blue                                          │   │
│  │                                                         │   │
│  │ postgresql:                                            │   │
│  │   enabled: true                                        │   │
│  │   password: secret123                                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│       │                                                      │
│       │  子 Chart 可以访问父 Chart 的 global                  │
│       ▼                                                      │
│  子 Chart (postgresql)                                        │
│                                                                 │
│  templates/deployment.yaml                                     │
│  env:                                                          │
│  - name: PASSWORD                                              │
│    value: {{ .Values.password | default "default" }}           │
│  - name: GLOBAL_COLOR                                          │
│    value: {{ .Values.global.color }}                          │
│                                                                 │
│  注意:子 Chart 不能访问父 Chart 的非 global 配置              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

共享模板(Library Chart)

# library chart 结构
common/
├── Chart.yaml
├── templates/
│   ├── _deployment.tpl
│   ├── _service.tpl
│   └── _helpers.tpl
└── values.yaml

---
# Chart.yaml
apiVersion: v2
type: library          # 关键:类型为 library
name: common
version: 1.0.0

---
# 父 Chart 引用
dependencies:
  - name: common
    version: "1.x.x"
    repository: "file://../common"

多环境配置

环境目录结构

myapp/
├── Chart.yaml
├── values.yaml           # 默认值
├── values-staging.yaml   # 预发环境
├── values-prod.yaml      # 生产环境
└── templates/
    └── deployment.yaml

values 文件继承

# values.yaml(默认)
image:
  repository: myapp
  tag: "1.0.0"
replicaCount: 1
resources:
  limits:
    cpu: 500m
    memory: 512Mi

---
# values-staging.yaml(预发)
image:
  tag: "1.1.0-staging"
replicaCount: 2
resources:
  limits:
    cpu: 1000m
    memory: 1Gi

---
# values-prod.yaml(生产)
image:
  tag: "1.1.0"
replicaCount: 5
resources:
  limits:
    cpu: 2000m
    memory: 4Gi

部署命令

# 安装到不同环境
helm install myapp ./mychart -f values.yaml

helm install myapp-staging ./mychart \
  -f values.yaml \
  -f values-staging.yaml

helm install myapp-prod ./mychart \
  -f values.yaml \
  -f values-prod.yaml

# 升级
helm upgrade myapp-prod ./mychart \
  -f values.yaml \
  -f values-prod.yaml

# 查看差异
helm diff upgrade myapp-prod ./mychart \
  -f values.yaml \
  -f values-prod.yaml

命名空间覆盖

# values-prod.yaml
namespace: production

# 安装时指定命名空间
helm install myapp ./mychart \
  -n production \
  --create-namespace \
  -f values-prod.yaml

# 或在 Chart.yaml 中使用
namespace: {{ .Release.Namespace }}

Helm 仓库

添加仓库

# 添加官方仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts

# 更新仓库索引
helm repo update

# 搜索 Chart
helm search repo nginx
helm search repo prometheus

# 查看仓库列表
helm repo list

# 移除仓库
helm repo remove bitnami

创建私有仓库

# 方式1:ChartMuseum(私有仓库服务)
helm repo add chartmuseum https://chartmuseum.example.com \
  --username=admin \
  --password=secret

# 方式2:对象存储
# 将 Chart 包上传到 S3/MinIO 等

# 方式3:GitHub Pages
# 1. 创建 GitHub 仓库
# 2. 发布 Chart 包到 gh-pages 分支
# 3. 添加仓库
helm repo add myrepo https://myorg.github.io/charts

# 方式4:Harbor
# Harbor 内置 Chart 仓库支持

打包与发布

# 打包 Chart
helm package ./mychart

# 输出:mychart-1.0.0.tgz

# 签名(可选)
helm package --sign --key mykey --keyring ~/.gnupg/secring.gpg ./mychart

# 上传到私有仓库
curl -u admin:secret -X POST https://chartmuseum.example.com/api/charts \
  -F "chart=@mychart-1.0.0.tgz"

# 签名验证
helm verify mychart-1.0.0.tgz

CI/CD 集成

GitHub Actions 示例

# .github/workflows/helm.yml
name: Helm Charts CI/CD

on:
  push:
    branches: [main]
    paths:
      - 'charts/**'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Set up Helm
      uses: azure/setup-helm@v3
      with:
        version: v3.14.0

    - name: Helm lint
      run: |
        helm lint ./charts/myapp

    - name: Helm template
      run: |
        helm template myapp ./charts/myapp \
          -f ./charts/myapp/values.yaml > /tmp/rendered.yaml

  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Set up Kubernetes
      uses: azure/setup-kubectl@v4

    - name: Helm test
      run: |
        helm template test-app ./charts/myapp \
          --dry-run --debug

  deploy-staging:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: staging
    steps:
    - uses: actions/checkout@v4

    - name: Set up Helm
      uses: azure/setup-helm@v3

    - name: Configure kubectl
      run: |
        echo "${{ secrets.KUBE_CONFIG_STAGING }}" | base64 -d > kubeconfig
        export KUBECONFIG=kubeconfig

    - name: Deploy to Staging
      run: |
        helm upgrade --install myapp ./charts/myapp \
          --namespace staging \
          --create-namespace \
          --wait --timeout 5m \
          --debug

  deploy-prod:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
    - uses: actions/checkout@v4

    - name: Set up Helm
      uses: azure/setup-helm@v3

    - name: Configure kubectl
      run: |
        echo "${{ secrets.KUBE_CONFIG_PROD }}" | base64 -d > kubeconfig
        export KUBECONFIG=kubeconfig

    - name: Diff before deploy
      run: |
        helm diff upgrade myapp ./charts/myapp \
          -f ./charts/myapp/values-prod.yaml \
          --namespace production

    - name: Deploy to Production
      run: |
        helm upgrade --install myapp ./charts/myapp \
          --namespace production \
          -f ./charts/myapp/values.yaml \
          -f ./charts/myapp/values-prod.yaml \
          --wait --timeout 10m \
          --atomic \
          --cleanup-on-fail

Helmfile 编排

# helmfile.yaml
repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: myapp
    chart: ./charts/myapp
    values:
      - values.yaml.gotmpl
      - environment: {{ .Environment.Name }}
        values:
          - environments/{{ .Environment.Name }}/values.yaml
    namespaces:
      - {{ .Environment.Name }}
    secrets:
      - environments/{{ .Environment.Name }}/secrets.yaml.gotmpl

  - name: redis
    chart: bitnami/redis
    version: "17.x.x"
    condition: myapp.redis.enabled
    values:
      - environments/{{ .Environment.Name }}/redis.yaml

高级技巧

测试 Chart

# Chart.yaml 添加测试
apiVersion: v2
name: myapp
version: 1.0.0

# templates/ 目录添加测试
# tests/ 目录下的资源不会在 install 时部署
templates/
├── deployment.yaml
├── service.yaml
└── tests/
    └── test-connection.yaml
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "mychart.fullname" . }}-test"
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
spec:
  containers:
  - name: wget
    image: busybox:1.36
    command: ['wget']
    args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
  restartPolicy: Never
# 运行测试
helm test myapp

# 测试钩子类型:
# - test:运行测试
# - pre-install:在安装前执行
# - post-install:在安装后执行
# - pre-upgrade:在升级前执行
# - post-upgrade:在升级后执行
# - pre-rollback:在回滚前执行
# - post-rollback:在回滚后执行

Helm Hooks

# 使用 Hook 执行任务
apiVersion: batch/v1
kind: Job
metadata:
  name: database-migration
  annotations:
    "helm.sh/hook": pre-upgrade,pre-rollback
    "helm.sh/hook-weight": "-1"           # 权重,越小越先执行
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
      - name: migration
        image: myapp-migration:latest
        command: ["./migrate.sh"]
      restartPolicy: Never

模板调试

# 本地渲染模板
helm template myapp ./mychart

# 调试模式
helm template myapp ./mychart --debug

# 只渲染指定模板
helm template myapp ./mychart --show-only templates/deployment.yaml

# 指定 values
helm template myapp ./mychart -f values-prod.yaml

# 渲染并验证(不安装)
helm install myapp ./mychart --dry-run --debug

# 在集群上模拟安装
helm upgrade --install myapp ./mychart --dry-run --debug --namespace test

常见问题与避坑指南

Q1:如何回滚 Release?

# 查看历史
helm history myapp

# 输出:
# REVISION  UPDATED                  STATUS     CHART      DESCRIPTION
# 1         2024-05-19 10:00:00      superseded myapp-1.0.0  Install complete
# 2         2024-05-19 11:00:00      deployed   myapp-1.1.0  Upgrade complete
# 3         2024-05-19 12:00:00      superseded myapp-1.2.0  Upgrade failed

# 回滚到版本 1
helm rollback myapp 1

# 指定命名空间
helm rollback myapp 1 -n production

Q2:如何处理敏感数据?

# 方式1:外部密钥管理
# values.yaml
image:
  password: ""

# 部署时从 Vault 注入
helm upgrade myapp ./mychart \
  --set image.password=$(vault kv get -field=password secret/myapp)

# 方式2:SOPS 加密
# .sops.yaml
creation_rules:
  - age: >1

# 加密文件
sops --encrypt values.enc.yaml > values.enc.yaml

# 使用加密文件
helm upgrade myapp ./mychart --secrets-file values.enc.yaml

Q3:如何管理多个集群?

# 使用 kubeconfig 或指定 kubeconfig
export KUBECONFIG=/path/to/config
helm list --all-namespaces

# 或在 CI/CD 中使用不同 kubeconfig

Q4:Chart 版本冲突?

# 依赖版本范围
dependencies:
  - name: common
    version: ">=1.0.0,<2.0.0"    # 范围语法
    # >= 1.0.0, < 2.0.0

  - name: postgresql
    version: "~12"               # 修定版本
    # >=12.0.0, <13.0.0

  - name: redis
    version: "^17.0.0"           # 兼容版本
    # >=17.0.0, <18.0.0

总结

┌─────────────────────────────────────────────────────────────────┐
│                    核心要点回顾                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Helm 核心概念                                                  │
│  ├── Chart:打包格式,包含模板和配置                            │
│  ├── Release:Chart 在集群中的实例                             │
│  └── Repository:Chart 存储仓库                                 │
│                                                                 │
│  Chart 结构                                                    │
│  ├── Chart.yaml:元数据和依赖                                   │
│  ├── values.yaml:默认配置                                     │
│  └── templates/:K8s 资源模板                                   │
│                                                                 │
│  模板语法                                                       │
│  ├── 内置对象:.Release, .Chart, .Values                       │
│  ├── 模板函数:default, upper, lower, replace...                │
│  └── 流程控制:if, with, range, ...                            │
│                                                                 │
│  依赖管理                                                       │
│  ├── dependencies 定义依赖                                     │
│  ├── helm dependency update 下载                               │
│  └── library chart 共享模板                                     │
│                                                                 │
│  最佳实践                                                       │
│  ├── 合理使用 _helpers.tpl                                     │
│  ├── values 分层管理环境                                        │
│  ├── CI/CD 集成自动化                                          │
│  └── Helm Hooks 处理迁移和清理                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

思考题

  1. Helm 和 Kustomize 各有什么优缺点?什么场景下选择哪个?
  2. 如何设计一个可复用的 Chart 模板库?
  3. 如何在 Helm 中实现蓝绿部署或金丝雀发布?

引用与参考

  1. Helm Documentation
  2. Helm Chart Best Practices
  3. Artifact Hub - Chart 仓库

下篇预告

下一篇文章我们将探讨 Operator 与 CRD,包括:

  • 自定义资源定义
  • Operator 模式与控制器
  • KubeBuilder 开发框架
  • 实战:一个数据库 Operator

敬请期待!