2. Helm Chart 基础回顾:Chart 结构、Values、Templates 与依赖管理

在深入镜像拉取依赖图与集群升级适配测试之前,必须牢固掌握 Helm Chart 的核心构造。Chart 并非简单的 YAML 文件集合,而是一个具备模板引擎、参数注入与依赖解析能力的应用打包格式。本节将拆解其四大支柱:目录结构、Values 参数体系、Template 渲染机制以及依赖管理。

2.1 Chart 标准目录结构

一个标准的 Helm Chart 遵循固定布局,Helm CLI 通过识别这些约定文件来执行打包、验证与安装。下表列出了核心文件与目录的作用:

路径 / 文件 是否必需 作用说明
Chart.yaml 是 Chart 元数据:名称、版本、API 版本、描述、关键字等
values.yaml 是 默认配置值,供模板引用;用户可通过 --set 或自定义 values 文件覆盖
templates/ 是 Go 模板文件目录,渲染后生成 Kubernetes 资源清单
charts/ 否 子 Chart 或依赖 Chart 的本地存储目录(helm dependency update 后生成)
crds/ 否 自定义资源定义(CRD),Helm 会在安装时优先应用此目录下的 YAML
templates/NOTES.txt 否 安装成功后向用户展示的说明文本(如访问地址、默认密码等)
templates/tests/ 否 测试 Pod 模板,用于 helm test 验证部署是否正常
实践要点:在集群升级适配测试中,Chart.yaml 中的 apiVersion 字段决定了 Helm 的行为模式(v1 为旧版,v2 为当前主流)。务必确认 Chart 的 apiVersion 与 Helm 客户端版本兼容,否则可能导致依赖解析失败。

2.2 Values 参数注入与作用域

Values 是 Chart 的配置接口。Helm 通过三层合并策略将用户提供的值注入模板:

  1. 内置对象:.Release、.Chart、.Files、.Capabilities 等由 Helm 自动注入。
  2. 用户 Values:--values myvals.yaml 或 --set key=value 传入。
  3. 父 Chart 传递:子 Chart 通过 requirements.yaml 或 Chart.yaml 的 dependencies 字段接收父 Chart 的 values。

作用域规则遵循“全局优先,局部覆盖”原则。以下是一个典型的 values 层级示例:

# values.yaml
replicaCount: 3

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

service:
  type: ClusterIP
  port: 80

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

在模板中通过 .Values.image.repository 引用。当用户执行 helm install --set image.tag=1.26 时,tag 被覆盖为 1.26,其余字段保持默认。

升级适配关键点:镜像拉取依赖图的核心数据源正是 .Values.image 字段。在集群升级场景中,需要确保 image.repository 指向的镜像仓库在目标集群中可访问,且 image.tag 对应的镜像架构与目标节点 CPU 架构(amd64 / arm64)匹配。

2.3 Template 渲染引擎与内置函数

Helm 使用 Go 模板引擎(text/template)扩展了 60+ 个模板函数,包括 Sprig 库、流控制、管道操作等。模板文件通常以 .yaml 为后缀,但实际是 Go 模板语法与 YAML 的混合体。

一个典型的 Deployment 模板片段:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}

模板中使用了 include 函数引用命名模板(_helpers.tpl 中定义),nindent 控制缩进,. 表示当前作用域上下文。在升级适配测试中,需要特别关注 imagePullPolicy 的渲染结果——如果 tag 为 latest 或未指定,Helm 默认会将其设为 Always,这会影响集群升级时的镜像拉取行为。

常见陷阱:模板中的 imagePullPolicy 如果被硬编码为 IfNotPresent,在集群升级后节点缓存了旧版本镜像时,Kubelet 不会主动拉取新版本,导致 Pod 仍运行旧镜像。建议在 values 中暴露 pullPolicy 字段,并在升级前通过 --set 临时覆盖为 Always。

2.4 依赖管理:从 requirements.yaml 到 Chart.yaml

Helm 2 使用独立的 requirements.yaml 管理依赖,Helm 3 将其合并到 Chart.yaml 的 dependencies 字段。依赖管理决定了子 Chart 的下载、版本锁定与 values 传递方式。

以下是一个 Helm 3 风格的依赖声明:

# Chart.yaml
apiVersion: v2
name: myapp
version: 1.0.0
dependencies:
  - name: redis
    version: ">=16.0.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
    tags:
      - cache
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    alias: db
    import-values:
      - child: defaults
        parent: postgres

关键字段说明:

字段 作用 升级适配影响
condition 条件控制:当 redis.enabled=false 时跳过安装 升级时需确认条件值是否变化,避免子 Chart 被意外启用或禁用
tags 标签分组:可通过 --set tags.cache=false 批量禁用 标签覆盖优先级高于 condition,升级时需检查标签冲突
alias 别名:避免同名 Chart 冲突,子 Chart values 通过别名访问 别名变更会导致 values 路径失效,升级前必须对齐
import-values 值导入:将子 Chart 的 defaults 映射到父 Chart 的 postgres 路径 映射关系错误会导致子 Chart 使用空值或默认值,引发配置漂移
repository Chart 仓库 URL 或 OCI 地址 集群升级后若仓库迁移或鉴权变更,helm dependency update 会失败

执行 helm dependency update 后,Helm 会生成 Chart.lock 文件锁定精确版本,并将 Chart 包下载到 charts/ 目录。在集群升级适配测试中,必须重新执行 helm dependency update 以确保子 Chart 版本与目标集群的 Kubernetes API 版本兼容。

2.5 依赖解析与镜像拉取的关联

依赖管理直接影响镜像拉取依赖图的构建。每个子 Chart 可能定义自己的镜像仓库与标签,父 Chart 无法直接控制子 Chart 的 image 字段,除非通过 import-values 或全局 values 传递。

下图展示了 Helm Chart 依赖树与镜像拉取路径的关系:

Helm Chart 依赖树与镜像拉取路径 父 Chart (myapp) 子 Chart: redis 子 Chart: postgresql 子 Chart: nginx-ingress redis:7.2-alpine postgres:16.1 nginx-ingress:1.9.5 docker.io/bitnami/redis docker.io/bitnami/postgresql 说明:实线为依赖关系,虚线为镜像引用关系。每个子 Chart 独立声明镜像,父 Chart 无法直接覆盖子 Chart 的 image 字段。

从图中可以看出:

  • 父 Chart 通过 dependencies 声明子 Chart,但不直接控制子 Chart 的镜像。
  • 每个子 Chart 的 values.yaml 中定义了各自的 image.repository 和 image.tag。
  • 镜像拉取依赖图需要递归遍历所有子 Chart 的 values,才能构建完整的镜像列表。
  • 在集群升级场景中,如果子 Chart 版本升级导致镜像仓库或标签变更,父 Chart 的升级测试必须覆盖这些变更。
适配测试策略:在集群升级前,使用 helm template --generate-name 渲染所有模板,然后通过 grep -E 'image:' 提取所有镜像地址,构建完整的镜像拉取清单。对比升级前后的清单,识别新增、删除或变更的镜像,提前在目标集群中验证镜像拉取权限与架构兼容性。

2.6 依赖版本约束与升级兼容性

Helm 支持语义化版本范围约束,常用的操作符包括:

操作符 示例 匹配版本 升级风险
>= >= 16.0.0 16.0.0 及以上所有版本 可能引入不兼容的 API 变更
~(波浪号) ~16.0 16.0.x 范围内的最新补丁版本 补丁升级通常安全,但需验证
^(脱字符) ^16.0.0 16.0.0 到 17.0.0 以下(不含主版本变更) 次版本升级可能引入新特性或弃用
= =16.0.1 精确匹配 16.0.1 无版本漂移风险,但需手动更新

在集群升级适配测试中,建议将依赖版本约束从 >= 或 ^ 收紧为 ~ 或精确版本,避免 helm dependency update 自动拉取不兼容的子 Chart 版本。同时,Chart.lock 文件应纳入版本控制,确保所有环境使用相同的依赖版本。

升级阻断场景:当子 Chart 从 Helm 2 迁移到 Helm 3 时,其 apiVersion 可能从 v1 变为 v2。如果父 Chart 的 Chart.yaml 中 apiVersion: v1,则无法引用 apiVersion: v2 的子 Chart。此时必须先将父 Chart 升级为 apiVersion: v2,否则 helm dependency build 会报错。