Helm 图表:Kubernetes 包管理

使用 Helm 包管理的 Kubernetes 部署

Helm 已彻底改变 Kubernetes 应用程序部署,通过引入传统操作系统中熟悉的软件包管理概念。

随着 Kubernetes 的采用率增长,管理包含数十个 YAML 文件的复杂应用程序变得具有挑战性。Helm Charts 通过将所有资源打包成版本化、可配置的软件包来解决这个问题。

software-developer in the nature 这张漂亮的图片是由 AI 模型 Flux 1 dev 生成的。

了解 Helm:Kubernetes 的软件包管理器

Helm 对 Kubernetes 的作用,就像 apt 对 Debian、yum 对 RedHat 或 Homebrew 对 macOS 的作用一样。它将 Kubernetes 应用程序打包成 Charts – 一组描述相关 Kubernetes 资源的文件集合。一个 Chart 可能部署一个完整的应用程序堆栈:Web 服务器、数据库、缓存层、入口规则和监控组件。对于 Kubernetes 新手,Kubernetes 快速参考 提供了启动所需的必备命令和概念。

为什么 Helm 在现代 DevOps 中很重要

复杂性降低:您不再需要管理 20 多个 YAML 文件,而是通过可自定义的值管理一个 Chart。

可重复性:通过环境特定的值覆盖,可以在开发、测试和生产环境中部署相同的配置。这对于部署需要一致性的复杂微服务架构尤其有价值。

版本控制:Charts 是版本化的,便于轻松回滚和升级跟踪。

社区生态系统:通过 Artifact Hub(以前称为 Helm Hub)可以获取数千个预构建的 Charts,用于流行的 PostgreSQL、Redis、NGINX、Prometheus 等应用程序。

模板功能:Go 模板可以根据输入值动态生成资源,减少重复。

Helm 架构和核心概念

Helm 3 架构

Helm 3 通过移除 Helm 2 中有问题的服务器端组件 Tiller 简化了架构:

  • Helm 客户端:与 Kubernetes API 直接交互的 CLI 工具
  • Chart:包含模板和元数据的软件包格式
  • Release:在 Kubernetes 集群中运行的 Chart 实例
  • Repository:Charts 的存储位置(HTTP 服务器或 OCI 注册表)

Helm Chart 的关键组件

my-app-chart/
├── Chart.yaml          # Chart 元数据和版本
├── values.yaml         # 默认配置值
├── charts/             # 依赖的 Charts
├── templates/          # Kubernetes 资源模板
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # 模板助手
│   └── NOTES.txt       # 安装后说明
├── .helmignore         # 打包时忽略的文件
├── README.md
└── LICENSE

创建您的第一个 Helm Chart

初始化新 Chart

helm create my-application
cd my-application

这将生成一个包含部署、服务和入口示例模板的初始 Chart。

Chart.yaml:定义元数据

apiVersion: v2
name: my-application
description: 一个生产就绪的应用程序 Chart
type: application
version: 1.0.0        # Chart 版本
appVersion: "2.4.1"   # 应用程序版本

maintainers:
  - name: 您的团队
    email: team@company.com

dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

values.yaml:配置管理

values.yaml 文件定义了用户可以覆盖的默认配置。这种方法将配置与模板分离,使管理不同环境(开发、测试、生产)和自定义部署变得容易,而无需修改模板文件。

replicaCount: 3

image:
  repository: myapp/backend
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

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

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

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

模板:动态 Kubernetes 清单

模板使用 Go 模板语法动态生成 Kubernetes 资源。这些模板可以生成任何 Kubernetes 资源类型,从简单的 Deployment 到需要持久存储的复杂 StatefulSet。对于需要稳定身份和持久卷的应用程序,您应该使用 StatefulSet 而不是 Deployment,如我们在 Kubernetes 中的 StatefulSets 和持久存储 指南中所述。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-application.fullname" . }}
  labels:
    {{- include "my-application.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "my-application.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-application.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          {{- toYaml .Values.resources | nindent 10 }}

模板助手 (_helpers.tpl)

创建可重用的模板函数以避免重复:

{{/*
扩展 Chart 的名称。
*/}}
{{- define "my-application.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
创建默认的完全限定应用程序名称。
*/}}
{{- define "my-application.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

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

管理 Helm Charts:安装和操作

安装 Chart

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

# 从本地目录安装
helm install my-app ./my-application

# 使用自定义值安装
helm install my-app ./my-application -f values-production.yaml

# 使用内联值覆盖安装
helm install my-app ./my-application \
  --set replicaCount=5 \
  --set image.tag=2.0.0

升级发布

# 使用新值升级
helm upgrade my-app ./my-application -f values-production.yaml

# 失败时自动回滚
helm upgrade my-app ./my-application --atomic --timeout 5m

# 强制资源更新
helm upgrade my-app ./my-application --force

回滚和历史记录

# 查看发布历史
helm history my-app

# 回滚到上一版本
helm rollback my-app

# 回滚到特定版本
helm rollback my-app 3

测试和调试

# 干运行查看生成的清单
helm install my-app ./my-application --dry-run --debug

# 渲染模板而不安装
helm template my-app ./my-application

# 检查 Chart 的问题
helm lint ./my-application

# 使用测试钩子测试发布
helm test my-app

高级 Helm 功能

Chart 依赖

Helm Charts 可以依赖其他 Charts,允许您通过可重用组件构建复杂的应用程序。这对于需要数据库、消息队列或其他支持服务的微服务部署特别有用。在 Chart.yaml 中定义依赖项:

dependencies:
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

更新依赖项:

helm dependency update ./my-application

Helm 钩子用于生命周期管理

钩子在发布生命周期的特定点执行:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-application.fullname" . }}-db-migration
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
      - name: db-migrate
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        command: ["python", "manage.py", "migrate"]
      restartPolicy: Never

钩子类型包括:

  • pre-install:在资源安装之前
  • post-install:在所有资源安装之后
  • pre-upgrade:在升级之前
  • post-upgrade:在升级之后
  • pre-delete:在删除之前
  • post-delete:在删除之后
  • pre-rollback:在回滚之前
  • post-rollback:在回滚之后

条件逻辑和流程控制

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "my-application.fullname" . }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if .Values.ingress.className }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  rules:
  {{- range .Values.ingress.hosts }}
  - host: {{ .host | quote }}
    http:
      paths:
      {{- range .paths }}
      - path: {{ .path }}
        pathType: {{ .pathType }}
        backend:
          service:
            name: {{ include "my-application.fullname" $ }}
            port:
              number: {{ $.Values.service.port }}
      {{- end }}
  {{- end }}
{{- end }}

OCI 注册表支持:现代 Chart 分发

自 Helm 3.8 起,OCI(开放容器倡议)注册表支持已稳定,允许将 Charts 与容器镜像一起存储。

发布到 OCI 注册表

# 登录到注册表
helm registry login registry.example.com

# 打包 Chart
helm package ./my-application

# 推送到 OCI 注册表
helm push my-application-1.0.0.tgz oci://registry.example.com/charts

# 从 OCI 注册表安装
helm install my-app oci://registry.example.com/charts/my-application --version 1.0.0

OCI 注册表的优势

  • 统一存储:Charts 和镜像在一个地方
  • 标准工具:使用现有的注册表基础设施
  • 更好的安全性:利用注册表认证和扫描
  • 更简单的管理:不需要单独的 Chart 仓库服务器

生产 Helm Charts 的最佳实践

1. 值结构和文档

用注释文档所有值:

# -- 应用程序的副本数
replicaCount: 3

# -- 图像配置
image:
  # -- 图像仓库
  repository: myapp/backend
  # -- 图像拉取策略
  pullPolicy: IfNotPresent
  # -- 图像标签(默认为 chart appVersion)
  tag: ""

2. 资源管理

始终设置资源请求和限制:

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 500m
    memory: 512Mi

3. 安全上下文

为容器定义安全上下文:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000
  capabilities:
    drop:
    - ALL
  readOnlyRootFilesystem: true

4. 健康检查

包括存活和就绪探针:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

5. ConfigMap 和 Secret 管理

对于生产部署,正确管理 Secret 至关重要。使用外部 Secret 管理器(如 Sealed Secrets、External Secrets Operator 或 Vault)而不是在 values 文件中存储 Secret。这确保了数据库密码、API 密钥和证书等敏感数据的安全处理:

envFrom:
- secretRef:
    name: {{ include "my-application.fullname" . }}-secrets
- configMapRef:
    name: {{ include "my-application.fullname" . }}-config

6. Schema 验证

创建 values.schema.json 以验证用户输入:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1
    },
    "image": {
      "type": "object",
      "properties": {
        "repository": {
          "type": "string"
        },
        "tag": {
          "type": "string"
        }
      },
      "required": ["repository"]
    }
  },
  "required": ["image"]
}

7. NOTES.txt 用于用户指导

提供安装后说明:

1. 通过运行以下命令获取应用程序 URL:
{{- if .Values.ingress.enabled }}
  https://{{ (index .Values.ingress.hosts 0).host }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-application.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- end }}

2. 监控部署:
  kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ include "my-application.name" . }}"

Helm Chart 测试和 CI/CD 集成

使用 chart-testing (ct) 进行 Chart 测试

# 安装 chart-testing
brew install chart-testing

# 验证 Charts
ct lint --config ct.yaml

# 安装并测试 Charts
ct install --config ct.yaml

GitHub Actions 示例

name: Helm Chart CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: 设置 Helm
        uses: azure/setup-helm@v3
        with:
          version: 3.12.0

      - name: 设置 chart-testing
        uses: helm/chart-testing-action@v2

      - name: 验证 Charts
        run: ct lint --config ct.yaml

      - name: 创建 kind 集群
        uses: helm/kind-action@v1

      - name: 安装 Charts
        run: ct install --config ct.yaml

使用 Helm 的 GitOps:ArgoCD 和 Flux

像 ArgoCD 和 Flux 这样的 GitOps 工具可以与 Helm Charts 无缝集成,实现声明式、自动化的部署。这些工具监视您的 Git 仓库中的更改,并自动同步 Helm 发布,使持续部署变得简单。对于复杂的微服务架构,考虑如何使用分布式事务模式,如 Saga 模式 来管理通过 Helm 部署的服务之间的一致性。

ArgoCD 应用程序

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-application
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-app-chart
    targetRevision: main
    path: charts/my-application
    helm:
      valueFiles:
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Flux HelmRelease

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: my-application
  namespace: flux-system
spec:
  interval: 5m
  chart:
    spec:
      chart: my-application
      version: '1.x.x'
      sourceRef:
        kind: HelmRepository
        name: my-charts
  values:
    replicaCount: 5
    image:
      tag: "2.0.0"

解决常见的 Helm 问题

问题:升级失败并处于挂起状态

# 检查发布状态
helm list --all-namespaces

# 获取发布详细信息
helm status my-app -n namespace

# 如果需要,强制删除(谨慎使用)
kubectl delete secret -n namespace -l owner=helm,name=my-app

问题:模板渲染错误

# 调试模板渲染
helm template my-app ./my-application --debug

# 验证 Kubernetes
helm template my-app ./my-application | kubectl apply --dry-run=client -f -

问题:值未应用

# 检查合并后的值
helm get values my-app

# 显示所有计算后的值
helm get values my-app --all

Helm 生态系统和工具

Helm 生态系统包括许多扩展其功能并与其他 Kubernetes 技术集成的工具。当部署需要高级网络功能(如服务到服务通信、流量管理和安全策略)的应用程序时,考虑集成 使用 Istio 和 Linkerd 的服务网格,这些可以通过 Helm Charts 部署和管理。

必要工具

  • Helmfile:用于部署 Helm Charts 的声明式规范
  • Helm Diff:升级前预览更改
  • Helm Secrets:使用 SOPS 管理 Secret
  • Nova:查找过时的 Helm Charts
  • Pluto:检测过时的 Kubernetes API

示例 Helmfile

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: postgresql
    namespace: database
    chart: bitnami/postgresql
    version: 12.x.x
    values:
      - postgresql:
          auth:
            database: myapp
            username: appuser

  - name: my-app
    namespace: production
    chart: ./charts/my-application
    values:
      - values-production.yaml
    needs:
      - database/postgresql

Helm 的未来

Helm 生态系统持续发展:

  • OCI 原生:完全过渡到 OCI 注册表作为标准
  • 改进的安全性:更好的 Secret 管理和签名功能
  • 性能:大型 Charts 的更快渲染和安装
  • WASM 支持:用于 Chart 插件和扩展的 WebAssembly
  • 更好的验证:增强的 Schema 验证和策略执行

结论

Helm 已成为 Kubernetes 软件包管理的事实标准,掌握它是现代 DevOps 实践的关键。通过理解 Chart 结构、模板、值管理以及最佳实践,您可以创建可维护、安全且可扩展的 Kubernetes 部署。

从简单的 Chart 开始,逐步引入钩子和依赖项等高级功能,并与 GitOps 工具集成以实现生产级基础设施。Helm 社区提供了数千个预构建的 Charts,但真正的力量来自于创建定制的 Charts,以满足您组织的需求。无论您是部署无状态应用程序还是需要持久存储的有状态工作负载,Helm 都简化了 Kubernetes 资源管理的复杂性。对于正在设置新 Kubernetes 集群的团队,考虑 使用 Kubespray 安装 Kubernetes 或探索 Kubernetes 发行版,如 k3s 或 MicroK8s 用于开发环境。如果您正在评估哪个发行版适合您的家庭实验室或小型集群需求,请参阅我们的 Kubernetes 发行版全面比较 以获得详细分析。

有用的链接