Kubernetes Helm 包管理详解

1. 简介与核心作用

Helm 是 K8S 的包管理器,用于定义、安装、升级复杂的 K8S 应用。

plaintext

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        Helm 核心概念                              │
├─────────────────────────────────────────────────────────────────┤
│   Chart        ──▶ 应用模板包(类比 RPM/Deb)                     │
│   Repository   ──▶ Chart 仓库(类比 yum/apt 源)                  │
│   Release      ──▶ Chart 的运行时实例(可多实例)                │
│   values.yaml  ──▶ 配置参数(模板变量)                          │
└─────────────────────────────────────────────────────────────────┘

表格

问题 kubectl Helm
多文件管理 散落 YAML Chart 统一打包
多环境部署 手动修改 values 覆盖
版本回滚 手动操作 helm rollback

2. Chart 结构详解

plaintext

复制代码
mychart/
├── Chart.yaml          # 元信息
├── values.yaml         # 默认配置
├── values.schema.json  # 校验(可选)
├── templates/          # K8S 资源模板
│   ├── _helpers.tpl   # 命名模板
│   ├── deployment.yaml
│   ├── service.yaml
│   └── NOTES.txt
└── .helmignore

Chart.yaml

yaml

复制代码
apiVersion: v2
name: mywebapp
version: 1.0.0
kubeVersion: ">=1.24.0"
dependencies:
  - name: redis
    version: "18.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

values.yaml

yaml

复制代码
replicaCount: 2
image:
  repository: myregistry/mywebapp
  tag: "v1.0.0"
  pullPolicy: IfNotPresent
service:
  type: ClusterIP
  port: 8080
ingress:
  enabled: true
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 256Mi
redis:
  enabled: true

2.1 values 覆盖机制

plaintext

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    values 优先级(从高到低)                       │
├─────────────────────────────────────────────────────────────────┤
│  1. --set / --set-file 命令行参数                               │
│  2. --values 指定文件                                            │
│  3. Release revision(回滚时使用)                               │
│  4. Chart values.yaml(最低)                                   │
└─────────────────────────────────────────────────────────────────┘

bash

复制代码
# 多层覆盖
helm install myapp ./mychart \
  -f values.yaml \
  -f values-prod.yaml \
  --set replicaCount=5

# 嵌套覆盖
helm upgrade myapp ./mychart \
  --set service.type=LoadBalancer \
  --set 'ingress.hosts[0].host=prod.example.com'

2.2 依赖管理

bash

复制代码
# 下载依赖
helm dependency build ./mychart

# 查看依赖
helm dependency list ./mychart
# NAME       VERSION  REPOSITORY                         STATUS
# redis      18.x.x   https://charts.bitnami.com/bitnami  installed

2.3 Helm Hooks

plaintext

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        Hook 生命周期                             │
├─────────────────────────────────────────────────────────────────┤
│   install/upgrade:                                                │
│     pre-install → post-install                                   │
│     pre-upgrade → post-upgrade                                   │
│   uninstall:                                                     │
│     pre-delete → post-delete                                     │
└─────────────────────────────────────────────────────────────────┘

Hook 示例:数据库迁移 Job

yaml

复制代码
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    helm.sh/hook: pre-install,pre-upgrade
    helm.sh/hook-weight: "-1"
    helm.sh/hook-delete-policy: before-hook-creation,hook-failed
spec:
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: myapp/migrate:latest
          command: ["/migrate.sh"]

3. Helm 3 架构

plaintext

复制代码
┌───────────────────────────────────────────────────────────────────┐
│                     Helm 3 架构(无 Tiller)                        │
├───────────────────────────────────────────────────────────────────┤
│                                                                   │
│   helm CLI ───────────┐                                           │
│   (本地客户端)        │                                           │
│                       ▼                                           │
│              ┌─────────────────┐                                  │
│              │  K8S API Server │                                  │
│              │                 │                                  │
│              │  Secrets        │  Release 存储在 kube-system     │
│              │  (Release 信息)  │                                  │
│              └─────────────────┘                                  │
│                                                                   │
│   helm template ──▶ 本地渲染(无需 K8S)                          │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

Helm 3 vs Helm 2

表格

特性 Helm 2 Helm 3
服务端 Tiller
Release 存储 Tiller 内存 K8S Secrets
依赖声明 chart: dependencies:

Chart 渲染流程

plaintext

复制代码
┌───────────────────────────────────────────────────────────────────┐
│                     Chart 渲染流程                                  │
├───────────────────────────────────────────────────────────────────┤
│                                                                   │
│   1. 合并 values (values.yaml + 覆盖文件 + --set)                   │
│                         │                                          │
│                         ▼                                          │
│   2. Go Template 渲染 templates/*.yaml                            │
│      - _helpers.tpl 命名模板                                       │
│      - range/if/include 等函数                                     │
│                         │                                          │
│                         ▼                                          │
│   3. K8S API 创建资源                                              │
│                         │                                          │
│                         ▼                                          │
│   4. Secret 存储 Release 信息,revision++                          │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

4. 实战配置

场景 1:完整 Chart 开发

templates/deployment.yaml

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mywebapp.fullname" . }}
  labels:
    {{- include "mywebapp.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "mywebapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mywebapp.selectorLabels" . | nindent 8 }}
    spec:
      serviceAccountName: {{ include "mywebapp.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.port }}
          livenessProbe:
            httpGet:
              path: /healthz
              port: http
          readinessProbe:
            httpGet:
              path: /ready
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

templates/service.yaml

yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: {{ include "mywebapp.fullname" . }}
  labels:
    {{- include "mywebapp.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "mywebapp.selectorLabels" . | nindent 4 }}

templates/ingress.yaml

yaml

复制代码
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "mywebapp.fullname" . }}
  annotations:
    {{- with .Values.ingress.annotations }}
    {{- toYaml . | nindent 4 }}
    {{- end }}
spec:
  {{- if .Values.ingress.className }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  {{- 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 | default "Prefix" }}
            backend:
              service:
                name: {{ include "mywebapp.fullname" $ }}
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
{{- end }}

templates/_helpers.tpl

yaml

复制代码
{{- define "mywebapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{- define "mywebapp.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 "mywebapp.labels" -}}
helm.sh/chart: {{ include "mywebapp.chart" . }}
{{ include "mywebapp.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{- define "mywebapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mywebapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{- define "mywebapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

场景 2:多环境部署

plaintext

复制代码
mywebapp/
├── values.yaml       # 默认值
├── values.dev.yaml   # 开发环境
├── values.staging.yaml
└── values.prod.yaml  # 生产环境

values.prod.yaml

yaml

复制代码
replicaCount: 3
image:
  pullPolicy: Always
service:
  type: LoadBalancer
ingress:
  enabled: true
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  tls:
    - secretName: myapp-tls-prod
      hosts:
        - myapp.example.com
resources:
  limits:
    cpu: 2000m
    memory: 2Gi
autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20

部署命令

bash

复制代码
# 开发环境
helm install myapp-dev ./mywebapp -f values.yaml -f values.dev.yaml

# 生产环境
helm install myapp-prod ./mywebapp -f values.yaml -f values.prod.yaml

场景 3:Subchart 依赖

Chart.yaml

yaml

复制代码
apiVersion: v2
name: mywebapp
version: 1.0.0
dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
  - name: redis
    version: "18.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

values.yaml(传递子 Chart 配置)

yaml

复制代码
replicaCount: 2
image:
  repository: myregistry/mywebapp
postgresql:
  enabled: true
  auth:
    username: myapp
    database: myappdb
  primary:
    persistence:
      size: 10Gi
redis:
  enabled: true
  architecture: replication
  auth:
    enabled: true

场景 4:Hooks 实战

pre-install + post-install 组合

yaml

复制代码
# templates/hooks/db-init.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mywebapp.fullname" . }}-db-init
  annotations:
    helm.sh/hook: pre-install
    helm.sh/hook-weight: "-1"
    helm.sh/hook-delete-policy: before-hook-creation,hook-failed
spec:
  backoffLimit: 3
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}/migrate"
          command: ["/migrate.sh", "up"]

---
# templates/hooks/verify.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mywebapp.fullname" . }}-verify
  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: verify
          image: curlimages/curl:latest
          command:
            - /bin/sh
            - -c
            - |
              sleep 10
              curl -f http://{{ include "mywebapp.fullname" . }}:{{ .Values.service.port }}/healthz

场景 5:模板函数与管道

templates/configmap.yaml

yaml

复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "mywebapp.fullname" . }}-config
data:
  # 基础变量
  APP_NAME: {{ .Chart.Name | quote }}
  APP_VERSION: {{ .Chart.Version | quote }}
  RELEASE_NAME: {{ .Release.Name | quote }}
  RELEASE_TIME: {{ .Release.Time | date "2006-01-02" | quote }}
  
  # 字符串处理
  APP_NAME_UPPER: {{ .Chart.Name | upper | quote }}
  
  # 默认值
  REPLICA_COUNT: {{ .Values.replicaCount | default 1 | toString | quote }}
  LOG_LEVEL: {{ .Values.config.logLevel | default "info" | quote }}
  
  # 条件渲染
  {{- if .Values.features.debug }}
  DEBUG_MODE: "true"
  {{- end }}
  
  # 多行配置
  CUSTOM_CONFIG: |
    server:
      port: {{ .Values.service.port }}
    logging:
      level: {{ .Values.config.logLevel | default "info" }}

场景 6:生产级 values

yaml

复制代码
# values.yaml
replicaCount: 2
environment: production

image:
  repository: myregistry/mywebapp
  tag: "v1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 8080

ingress:
  enabled: true
  className: nginx
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
  tls:
    - secretName: myapp-tls
      hosts:
        - myapp.example.com

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 100m
    memory: 256Mi

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

podSecurityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000

securityContext:
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL

5. 常用命令

Chart 开发

bash

复制代码
# 创建 Chart
helm create mychart

# 验证 Chart
helm lint ./mychart

# 本地渲染
helm template myrelease ./mychart
helm template myrelease ./mychart --debug

# 渲染 + values 覆盖
helm template myrelease ./mychart -f values.yaml -f values.prod.yaml

# 打包
helm package ./mychart

Release 管理

bash

复制代码
# 安装
helm install myapp ./mychart
helm install myapp ./mychart -f values.prod.yaml --namespace myns

# 升级
helm upgrade myapp ./mychart
helm upgrade myapp ./mychart --atomic   # 失败自动回滚
helm upgrade --install myapp ./mychart  # 不存在则安装

# 回滚
helm rollback myapp 1

# 卸载
helm uninstall myapp

# 查看列表
helm list --all-namespaces
helm history myapp
# REVISION  STATUS      CHART       DESCRIPTION
# 1         superseded  myapp-1.0.0 Install complete
# 2         deployed    myapp-1.1.0 Upgrade complete

依赖管理

bash

复制代码
helm dependency build ./mychart
helm dependency update ./mychart
helm dependency list ./mychart

Repository

bash

复制代码
# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami

# 更新
helm repo update

# 搜索
helm search repo nginx
helm search hub postgresql

# 索引
helm repo index ./myrepo --url https://charts.example.com

6. 常见问题与排查

问题 1:upgrade 失败与回滚

bash

复制代码
# 查看历史
helm history myapp

# 查看失败原因
helm get values myapp --revision 2

# 回滚
helm rollback myapp 1

# --atomic 自动回滚
helm upgrade myapp ./mychart --atomic

问题 2:模板渲染错误

bash

复制代码
# 本地调试
helm template myapp ./mychart --debug

# Dry-run
helm install myapp ./mychart --dry-run

# 常见错误修复
# 未定义变量:
{{ .Values.missing | default "default" }}

# 类型转换:
replicas: {{ .Values.replicaCount | int }}

# range 空列表:
{{- range .Values.items }}
- {{ .name }}
{{- else }}
- []
{{- end }}

问题 3:依赖冲突

bash

复制代码
# 查看依赖状态
helm dependency list ./mychart

# 重新下载
helm dependency build ./mychart --verify

# 本地依赖(离线)
ls ./mychart/charts/

7. 最佳实践

Chart 设计

表格

规范 说明
版本命名 SemVer 2
Chart 名称 lowercase + hyphen
敏感信息 使用 Secret,不在 values 明文
默认值 生产级,非最小配置

生产配置

yaml

复制代码
# Pod 安全上下文
podSecurityContext:
  runAsNonRoot: true
  runAsUser: 1000
  seccompProfile:
    type: RuntimeDefault

securityContext:
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop: [ALL]

# NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ include "mywebapp.fullname" . }}
spec:
  podSelector:
    matchLabels:
      {{- include "mywebapp.selectorLabels" . | nindent 6 }}
  policyTypes:
    - Ingress
    - Egress

CI/CD 集成

bash

复制代码
# 1. 打包验证
helm lint ./mychart && helm package ./mychart

# 2. 测试部署
helm upgrade --install myapp ./mychart --namespace test --dry-run

# 3. 测试
helm test myapp --namespace test

# 4. 清理
helm uninstall myapp --namespace test

OCI 仓库(Helm 3.8+)

bash

复制代码
helm registry login registry.example.com
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
helm pull oci://registry.example.com/charts/mychart --version 1.0.0

总结

  • Chart = 应用模板包,Release = 运行实例
  • Helm 3 无 Tiller,直接 K8S API
  • values 优先级:命令行 > 文件 > 默认值
  • Hooks 实现生命周期管理
  • 调试 :优先 helm template --debug
相关推荐
吴声子夜歌2 小时前
Java——通用容器类
java·容器
叶~小兮2 小时前
K8S优先级、Pod驱逐、HPA扩缩容 学习笔记
笔记·学习·kubernetes
xingfujie2 小时前
第2章:服务器规划与基础环境配置
linux·运维·微服务·云原生·容器·kubernetes·负载均衡
Raink老师2 小时前
【AI面试临阵磨枪-56】大模型服务部署:Docker、K8s、GPU 调度、推理加速
人工智能·面试·kubernetes·ai 面试
ℳ₯㎕ddzོꦿ࿐2 小时前
实战指南:使用 Docker Compose 优雅部署 MongoDB 并自动初始化用户
mongodb·docker·容器
yyyyy_abc2 小时前
docker学习笔记
运维·docker·容器
一起逃去看海吧3 小时前
Dify-01-docker安装 和 dify部署
运维·docker·容器
叶~小兮3 小时前
K8S-Helm与灰度发布学习笔记
笔记·学习·kubernetes
月光技术杂谈3 小时前
深度解析:基于Docker跨架构构建RK3588嵌入式rootfs的原理、边界与最佳实践
docker·容器·rootfs·rk3588·openeuler·欧拉·文件系统构建