Helm 详细介绍

182 阅读2分钟

1. Helm 简介

Helm 是 Kubernetes 的包管理器,被称为"Kubernetes 的 apt/yum"。它简化了 Kubernetes 应用的部署、管理和版本控制,通过模板化的方式管理复杂的 Kubernetes 配置。

1.1 核心概念

  • Chart: Helm 包,包含运行应用所需的所有 Kubernetes 资源定义
  • Release: Chart 在 Kubernetes 集群中的一个实例
  • Repository: 存储和分享 Charts 的地方
  • Values: 用于自定义 Chart 配置的参数

1.2 Helm 架构演进

Helm v2 (已弃用)           Helm v3 (当前版本)
┌─────────────┐           ┌─────────────┐
│    Helm     │           │    Helm     │
│   Client    │    -->    │   Client    │
└─────────────┘           └─────────────┘
       │                         │
       v                         v
┌─────────────┐           ┌─────────────┐
│   Tiller    │           │ Kubernetes  │
│  (Server)   │           │    API      │
└─────────────┘           └─────────────┘

2. Helm 安装和配置

2.1 安装 Helm

# 方式一:使用脚本安装
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 方式二:使用包管理器
# macOS
brew install helm

# Ubuntu/Debian
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

# Windows (使用 Chocolatey)
choco install kubernetes-helm

# 方式三:直接下载二进制文件
wget https://get.helm.sh/helm-v3.13.0-linux-amd64.tar.gz
tar -zxvf helm-v3.13.0-linux-amd64.tar.gz
sudo mv linux-amd64/helm /usr/local/bin/helm

2.2 验证安装

# 查看版本
helm version

# 查看帮助
helm help

# 初始化(可选,添加官方仓库)
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

3. Chart 结构详解

3.1 标准 Chart 目录结构

mychart/
├── Chart.yaml          # Chart 元数据
├── values.yaml         # 默认配置值
├── charts/             # 依赖的子 Charts
├── templates/          # 模板文件
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   ├── _helpers.tpl    # 模板辅助函数
│   └── NOTES.txt       # 安装后显示的说明
├── .helmignore         # 忽略文件列表
└── README.md           # 文档

3.2 Chart.yaml 文件

# Chart.yaml
apiVersion: v2
name: mychart
description: A Helm chart for my application
type: application
version: 0.1.0          # Chart 版本
appVersion: "1.0.0"     # 应用版本
maintainers:
  - name: Your Name
    email: your.email@example.com
home: https://github.com/yourname/mychart
sources:
  - https://github.com/yourname/myapp
dependencies:
  - name: redis
    version: "^17.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
keywords:
  - web
  - application
  - microservice

3.3 values.yaml 文件

# values.yaml - 默认配置
replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: "1.21.0"

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: false
  className: ""
  annotations: {}
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: Prefix
  tls: []

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

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80

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

# 应用特定配置
config:
  database:
    host: "localhost"
    port: 5432
    name: "myapp"
  redis:
    host: "redis-service"
    port: 6379
  logLevel: "info"

4. 模板语法详解

4.1 基本模板语法

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort }}
              protocol: TCP
          env:
            - name: DATABASE_URL
              value: "postgresql://{{ .Values.config.database.host }}:{{ .Values.config.database.port }}/{{ .Values.config.database.name }}"
            - name: REDIS_URL
              value: "redis://{{ .Values.config.redis.host }}:{{ .Values.config.redis.port }}"
            - name: LOG_LEVEL
              value: {{ .Values.config.logLevel | quote }}
          livenessProbe:
            httpGet:
              path: /health
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

4.2 模板辅助函数

# templates/_helpers.tpl
{{/*
展开 chart 的名称
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
创建完整的名称
*/}}
{{- 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 }}

{{/*
Chart 标签
*/}}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
{{ include "mychart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
选择器标签
*/}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Chart 版本
*/}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

4.3 条件和循环

# 条件渲染
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "mychart.fullname" . }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if .Values.ingress.tls }}
  tls:
    {{- range .Values.ingress.tls }}
    - hosts:
        {{- range .hosts }}
        - {{ . | quote }}
        {{- end }}
      secretName: {{ .secretName }}
    {{- end }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: {{ .pathType }}
            backend:
              service:
                name: {{ include "mychart.fullname" $ }}
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
{{- end }}

# 循环示例
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
  value: {{ $value | quote }}
{{- end }}

5. Helm 命令详解

5.1 Chart 管理命令

# 创建新 Chart
helm create mychart

# 验证 Chart 语法
helm lint mychart/

# 渲染模板(不安装)
helm template my-release mychart/

# 打包 Chart
helm package mychart/

# 查看 Chart 信息
helm show chart mychart/
helm show values mychart/
helm show readme mychart/

# 检查 Chart 依赖
helm dependency list mychart/
helm dependency update mychart/

5.2 Release 管理命令

# 安装 Chart
helm install my-release mychart/

# 使用自定义值文件安装
helm install my-release mychart/ -f custom-values.yaml

# 设置特定值
helm install my-release mychart/ --set image.tag=v2.0.0

# 从仓库安装
helm install my-nginx bitnami/nginx

# 升级 Release
helm upgrade my-release mychart/ -f new-values.yaml

# 回滚 Release
helm rollback my-release 1

# 卸载 Release
helm uninstall my-release

# 保留历史记录卸载
helm uninstall my-release --keep-history

5.3 查看和调试命令

# 列出所有 Release
helm list
helm list --all-namespaces

# 查看 Release 状态
helm status my-release

# 查看 Release 历史
helm history my-release

# 获取 Release 的 values
helm get values my-release

# 获取 Release 的 manifest
helm get manifest my-release

# 获取完整信息
helm get all my-release

# 调试安装(不实际安装)
helm install my-release mychart/ --debug --dry-run

5.4 仓库管理命令

# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add elastic https://helm.elastic.co

# 列出仓库
helm repo list

# 更新仓库索引
helm repo update

# 搜索 Chart
helm search repo nginx
helm search hub wordpress

# 移除仓库
helm repo remove bitnami

6. 高级功能和最佳实践

6.1 依赖管理

# Chart.yaml 中定义依赖
dependencies:
  - name: postgresql
    version: "^12.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
    tags:
      - cache
  - name: common
    version: "^2.0.0"
    repository: "https://charts.bitnami.com/bitnami"
# 更新依赖
helm dependency update mychart/

# 构建依赖
helm dependency build mychart/

6.2 Hooks 和测试

# templates/post-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ include "mychart.fullname" . }}-post-install"
  annotations:
    "helm.sh/hook": post-install
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: post-install
        image: busybox
        command: ['sh', '-c', 'echo "Post-install hook executed"']

# templates/tests/connection-test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "mychart.fullname" . }}-test-connection"
  annotations:
    "helm.sh/hook": test
spec:
  restartPolicy: Never
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
# 运行测试
helm test my-release

6.3 子 Chart 和全局值

# 父 Chart 的 values.yaml
global:
  imageRegistry: "my-registry.com"
  storageClass: "fast-ssd"

postgresql:
  enabled: true
  auth:
    postgresPassword: "mypassword"

redis:
  enabled: true
  auth:
    enabled: false

# 在模板中使用全局值
image: "{{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}"

6.4 命名模板和库 Chart

# library-chart/templates/_common.tpl
{{- define "common.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "common.fullname" . }}
  labels:
    {{- include "common.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "common.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "common.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
{{- end }}

# 在应用 Chart 中使用
{{- template "common.deployment" . }}

7. 安全和最佳实践

7.1 安全配置

# 使用 Secret 管理敏感信息
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "mychart.fullname" . }}-secret
type: Opaque
data:
  database-password: {{ .Values.database.password | b64enc }}
  api-key: {{ .Values.apiKey | b64enc }}

# RBAC 配置
{{- if .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: {{ include "mychart.fullname" . }}
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
{{- end }}

7.2 最佳实践

# 1. 使用语义版本控制
version: 1.2.3

# 2. 验证和测试
helm lint mychart/
helm template my-release mychart/ | kubectl apply --dry-run=client -f -

# 3. 使用 .helmignore
echo "*.tmp" >> .helmignore
echo ".git/" >> .helmignore

# 4. 文档化
# 在 README.md 中记录配置选项
# 在 NOTES.txt 中提供使用说明

7.3 多环境配置

# values-dev.yaml
replicaCount: 1
resources:
  requests:
    memory: "64Mi"
    cpu: "250m"

# values-prod.yaml
replicaCount: 3
resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
# 部署到不同环境
helm install my-app-dev mychart/ -f values-dev.yaml
helm install my-app-prod mychart/ -f values-prod.yaml

8. 插件和生态系统

8.1 常用插件

# 安装插件管理器
helm plugin install https://github.com/databus23/helm-diff

# Helm Diff - 比较变更
helm diff upgrade my-release mychart/

# Helm Secrets - 管理加密配置
helm plugin install https://github.com/jkroepke/helm-secrets
helm secrets install my-release mychart/ -f secrets.yaml

# Helm Push - 推送到仓库
helm plugin install https://github.com/chartmuseum/helm-push
helm push mychart/ my-repo

# 查看已安装插件
helm plugin list

8.2 Chart 仓库

# 官方仓库
helm repo add stable https://charts.helm.sh/stable
helm repo add incubator https://charts.helm.sh/incubator

# 第三方仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add elastic https://helm.elastic.co
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo add jetstack https://charts.jetstack.io

# 私有仓库
helm repo add myrepo https://charts.example.com --username user --password pass

9. 故障排除和调试

9.1 常见问题诊断

# 检查 Chart 语法
helm lint mychart/

# 渲染模板查看输出
helm template my-release mychart/ --debug

# 检查 Release 状态
helm status my-release

# 查看详细错误信息
kubectl describe pod <pod-name>
kubectl logs <pod-name>

# 检查 Kubernetes 事件
kubectl get events --sort-by=.metadata.creationTimestamp

# 验证资源创建
kubectl get all -l app.kubernetes.io/instance=my-release

9.2 调试技巧

# 在模板中添加调试信息
{{- if .Values.debug }}
# DEBUG: Values dump
{{ toYaml .Values | indent 2 }}
{{- end }}

# 使用 fail 函数进行验证
{{- if not .Values.database.host }}
{{- fail "Database host is required" }}
{{- end }}

# 条件调试
{{- $debug := .Values.debug | default false }}
{{- if $debug }}
annotations:
  debug/values: {{ toJson .Values | quote }}
{{- end }}

10. 实际应用示例

10.1 完整的 Web 应用部署

# Chart.yaml
apiVersion: v2
name: webapp
description: A complete web application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
  - name: postgresql
    version: "^12.0.0"
    repository: "https://charts.bitnami.com/bitnami"
  - name: redis
    version: "^17.0.0"
    repository: "https://charts.bitnami.com/bitnami"

# values.yaml
image:
  repository: myapp/webapp
  tag: "v1.0.0"

service:
  type: LoadBalancer
  port: 80

ingress:
  enabled: true
  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

postgresql:
  enabled: true
  auth:
    postgresPassword: "secretpassword"
    database: "webapp"

redis:
  enabled: true
  auth:
    enabled: false

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

10.2 微服务架构部署

# umbrella-chart/Chart.yaml
apiVersion: v2
name: microservices-app
description: Microservices application umbrella chart
version: 0.1.0
dependencies:
  - name: user-service
    version: "^0.1.0"
    repository: "file://../user-service"
  - name: order-service
    version: "^0.1.0"
    repository: "file://../order-service"
  - name: api-gateway
    version: "^0.1.0"
    repository: "file://../api-gateway"
  - name: postgresql
    version: "^12.0.0"
    repository: "https://charts.bitnami.com/bitnami"

# values.yaml
global:
  imageRegistry: "myregistry.com"
  database:
    host: "postgres-service"
    port: 5432

user-service:
  enabled: true
  replicaCount: 2

order-service:
  enabled: true
  replicaCount: 3

api-gateway:
  enabled: true
  replicaCount: 2
  service:
    type: LoadBalancer

10.3 部署脚本示例

#!/bin/bash
# deploy.sh

set -e

NAMESPACE=${1:-default}
ENVIRONMENT=${2:-development}
RELEASE_NAME="myapp-${ENVIRONMENT}"

echo "Deploying to ${NAMESPACE} namespace in ${ENVIRONMENT} environment"

# 创建 namespace(如果不存在)
kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -

# 添加必要的仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 安装或升级应用
if helm list -n ${NAMESPACE} | grep -q ${RELEASE_NAME}; then
    echo "Upgrading ${RELEASE_NAME}..."
    helm upgrade ${RELEASE_NAME} ./mychart \
        -n ${NAMESPACE} \
        -f values-${ENVIRONMENT}.yaml \
        --wait \
        --timeout=300s
else
    echo "Installing ${RELEASE_NAME}..."
    helm install ${RELEASE_NAME} ./mychart \
        -n ${NAMESPACE} \
        -f values-${ENVIRONMENT}.yaml \
        --wait \
        --timeout=300s
fi

# 运行测试
echo "Running tests..."
helm test ${RELEASE_NAME} -n ${NAMESPACE}

echo "Deployment completed successfully!"

11. 总结

Helm 是 Kubernetes 生态系统中不可或缺的工具,它通过以下方式简化了应用部署:

核心优势

  1. 模板化: 通过 Go 模板实现配置的参数化
  2. 版本管理: 支持应用的版本控制和回滚
  3. 依赖管理: 自动处理复杂的依赖关系
  4. 生态丰富: 庞大的 Chart 仓库和社区支持

适用场景

  • 复杂应用部署: 多组件应用的统一管理
  • 多环境部署: 通过不同的 values 文件管理多环境
  • CI/CD 集成: 自动化部署流水线
  • 应用分发: Chart 仓库实现应用的打包和分发

学习建议

  1. 从简单的 Chart 开始,逐步掌握模板语法
  2. 理解 Kubernetes 资源模型是使用 Helm 的基础
  3. 多实践,通过部署实际应用加深理解
  4. 关注 Helm 社区和最佳实践的发展

Helm 作为 Kubernetes 的包管理器,极大地简化了在 Kubernetes 上部署和管理应用的复杂性,是每个 Kubernetes 开发者和运维人员必须掌握的重要工具。