【架构实战】Helm Chart 进阶:依赖管理、测试与 CI/CD 集成

一、开篇:从"单 Chart"到"应用栈"

上篇我们聊了 Helm 的基础概念、values.yaml 配置管理和 Release 回滚机制。但真实世界远比单个 Chart 复杂:

一个典型的微服务应用栈,可能包含:

  • 前端
  • 后端 API(Spring Boot)
  • 数据库
  • 缓存(Redis Cluster)
  • 消息队列
  • 监控

如果用 6 个独立 Chart 分别管理,启动顺序、配置依赖、环境隔离都是噩梦。这就是 Helm Chart 依赖管理要解决的核心问题。

今天这篇,我们深入 Helm 的进阶能力:依赖管理、子 Chart、测试框架、CI/CD 集成,以及我们团队在落地过程中总结的最佳实践。

二、Chart 依赖管理:从"手动编排"到"声明式依赖"

Helm 3 支持 Chart 依赖声明,类似 npm 的 package.json 或 Maven 的 pom.xml。

Chart.yaml 中声明依赖:

yaml 复制代码
apiVersion: v2
name: myapp-stack
version: 1.0.0
dependencies:
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
    tags:
      - cache

  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
    alias: db   # 在模板中用 .Values.db 访问

  - name: myapp-backend
    version: "1.2.0"
    repository: "file://charts/backend"   # 本地 Chart

关键参数解释:

参数 作用 示例
condition 条件启用,可通过 values 控制是否安装 redis.enabled: false 跳过 Redis
tags 批量控制一组依赖 tags: [cache] 可统一开关
alias 重命名依赖,避免冲突 两个不同版本的 Redis 用不同别名

下载依赖并构建 charts/ 目录:

bash 复制代码
helm dependency update myapp-stack/
# 或简写
helm dep up myapp-stack/

执行后会在 charts/ 目录生成 .tgz 压缩包:

复制代码
myapp-stack/
├── Chart.yaml
├── charts/
│   ├── redis-17.3.0.tgz
│   ├── postgresql-12.1.0.tgz
│   └── backend/
└── values.yaml

通过 values.yaml 控制依赖启用:

yaml 复制代码
# 不安装 Redis,用外部托管服务
redis:
  enabled: false

# 启用 PostgreSQL,覆盖默认配置
postgresql:
  enabled: true
  auth:
    password: "prod-db-password-2026"
  primary:
    persistence:
      size: 100Gi
      storageClass: ssd-gold

# 后端服务配置
backend:
  image:
    repository: myregistry/myapp-backend
    tag: "2.1.4"
  env:
    DATABASE_URL: "postgresql://db:5432/myapp"

三、子 Chart 的值传递:父 Chart 如何控制子 Chart

这是 Helm 依赖管理中最容易踩坑的地方:values 是如何从父 Chart 传递到子 Chart 的?

规则很简单但容易被忽略:

  • 父 Chart 的 values.yaml 中,以子 Chart 名称为 key 的部分,会传递给子 Chart
  • 子 Chart 只能看到自己命名空间下的值

示例:父 values.yaml

yaml 复制代码
# 全局配置(所有子 Chart 可见)
global:
  imageRegistry: myregistry.io
  imagePullSecrets:
    - name: registry-secret

# 传递给 redis 子 Chart
redis:
  auth:
    password: "redis-prod-pass"
  replica:
    replicaCount: 3

# 传递给 postgresql 子 Chart(注意 alias)
db:   # 用 alias 名,不是原名
  auth:
    password: "pg-prod-pass"

# 后端应用配置(非子 Chart,直接在本 Chart 模板使用)
backend:
  image:
    tag: "2.1.4"

子 Chart(redis)收到的 values:

yaml 复制代码
# redis 子 Chart 的 values.yaml + 父 Chart 传入的覆盖
auth:
  password: "redis-prod-pass"
replica:
  replicaCount: 3

# global 是特殊字段,所有子 Chart 自动继承
global:
  imageRegistry: myregistry.io
  imagePullSecrets:
    - name: registry-secret

踩坑记录: 我们第一次用依赖管理时,把 redis.auth.password 写在 values.yaml 根级别,结果 Redis 一直用默认密码启动。排查半天才发现:必须放在 redis: 下面,Helm 才会把值传给子 Chart。

四、Chart 测试框架:安装后自动验证

Helm 内置了测试框架,可以在安装/升级后自动运行验证脚本,确保应用真正可用。

定义测试 Pod(templates/tests/):

yaml 复制代码
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ .Release.Name }}-test-connection"
  annotations:
    "helm.sh/hook": test
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['{{ .Release.Name }}:{{ .Values.service.port }}']
  restartPolicy: Never

运行测试:

bash 复制代码
helm test my-release
# NAME: my-release
# LAST DEPLOYED: Mon Jul 27 15:00:00 2026
# NAMESPACE: default
# STATUS: deployed
# TEST SUITE:     my-release-test-connection
# Last Started:   Mon Jul 27 15:01:00 2026
# Last Completed: Mon Jul 27 15:01:05 2026
# Phase:          Succeeded

我们团队的实践: 所有 Chart 必须包含至少一个测试用例,验证核心服务可访问性。CI/CD 流程中 helm upgrade 后自动跑 helm test,测试失败则自动回滚。

进阶测试:数据库连通性验证

yaml 复制代码
# templates/tests/test-db-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ .Release.Name }}-test-db"
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: pg-test
      image: postgres:15
      command: ['psql']
      args:
        - '-c'
        - 'SELECT 1'
      env:
        - name: PGHOST
          value: "{{ .Release.Name }}-db"
        - name: PGUSER
          value: "postgres"
        - name: PGPASSWORD
          valueFrom:
            secretKeyRef:
              name: "{{ .Release.Name }}-db-postgresql"
              key: password
  restartPolicy: Never

五、CI/CD 集成:从手动部署到自动化流水线

Helm 天然适合 CI/CD 集成。我们团队用 GitLab CI + Helm 实现了一套完整流程:

.gitlab-ci.yml 核心片段:

yaml 复制代码
stages:
  - lint
  - test
  - package
  - deploy

# 阶段 1:语法检查
helm-lint:
  stage: lint
  image: alpine/helm:3.12.0
  script:
    - helm lint charts/myapp/
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# 阶段 2:模板渲染验证(dry-run)
helm-template:
  stage: test
  image: alpine/helm:3.12.0
  script:
    - helm template myapp charts/myapp/ -f values/dev.yaml > /dev/null
    - helm template myapp charts/myapp/ -f values/prod.yaml > /dev/null
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# 阶段 3:打包并推送到 Chart 仓库
helm-package:
  stage: package
  image: alpine/helm:3.12.0
  script:
    - helm dependency update charts/myapp/
    - helm package charts/myapp/ --version $CI_COMMIT_TAG
    - helm push myapp-$CI_COMMIT_TAG.tgz oci://$OCI_REGISTRY/charts
  rules:
    - if: $CI_COMMIT_TAG

# 阶段 4:部署到 staging
deploy-staging:
  stage: deploy
  image: alpine/helm:3.12.0
  environment: staging
  script:
    - helm upgrade --install myapp oci://$OCI_REGISTRY/charts/myapp
      --version $CI_COMMIT_TAG
      -f values/staging.yaml
      -n myapp-staging
      --create-namespace
    - helm test myapp -n myapp-staging
  rules:
    - if: $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$/
  after_script:
    - |
      if [ $CI_JOB_STATUS == "failed" ]; then
        helm rollback myapp -n myapp-staging
      fi

# 阶段 5:部署到 production(需要手动审批)
deploy-production:
  stage: deploy
  image: alpine/helm:3.12.0
  environment: production
  script:
    - helm upgrade --install myapp oci://$OCI_REGISTRY/charts/myapp
      --version $CI_COMMIT_TAG
      -f values/production.yaml
      -n myapp-prod
      --create-namespace
    - helm test myapp -n myapp-prod
  rules:
    - if: $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/
  when: manual
  allow_failure: false

关键设计点:

  1. lint + template 双重验证:语法正确不代表渲染正确,必须两步都过
  2. OCI Registry:Helm 3 支持 OCI 协议,直接推送到 Docker Registry,不需要单独的 ChartMuseum
  3. 语义化版本规则v1.2.3-rc.1 自动部署 staging,v1.2.3 需手动审批上生产
  4. 测试失败自动回滚after_script 中判断任务状态,失败则回滚

六、Chart 仓库:从本地文件到企业级托管

Helm 支持多种 Chart 仓库方案:

方案 适用场景 优缺点
本地文件系统 开发测试 简单但无法团队共享
HTTP 服务器 中小团队 需自建,维护成本中等
ChartMuseum 企业私有仓库 功能完善,支持多存储后端
OCI Registry 现代方案 复用 Docker Registry,无需额外服务
Artifact Hub 开源 Chart 发现 公开仓库,类似 npm registry

OCI Registry 方式(推荐):

bash 复制代码
# 登录 Registry
helm registry login registry.example.com -u myuser -p mytoken

# 推送 Chart
helm push myapp-1.2.0.tgz oci://registry.example.com/charts

# 安装
helm install myapp oci://registry.example.com/charts/myapp --version 1.2.0

我们团队的选择: 用 Harbor 作为私有 Registry,同时托管 Docker 镜像和 Helm Chart,一套权限体系管理所有制品。

七、Helm Diff 插件:升级前预览变更

helm upgrade 最大的痛点是不知道具体改了什么。Helm Diff 插件解决了这个问题:

bash 复制代码
# 安装插件
helm plugin install https://github.com/databus23/helm-diff

# 升级前预览变更
helm diff upgrade myapp bitnami/nginx -f values/prod.yaml
# default, myapp-nginx (Deployment)       spec.template.spec.containers[0].image
# - image: nginx:1.20
# + image: nginx:1.21

CI/CD 中集成 Diff:

yaml 复制代码
helm-diff:
  stage: test
  script:
    - helm diff upgrade myapp charts/myapp/ -f values/prod.yaml || true
  artifacts:
    paths:
      - diff-output.txt

我们团队规定:生产环境任何 helm upgrade,必须先跑 diff 并在 MR 中展示变更内容。

八、敏感信息管理:从明文到加密

Chart 中经常包含数据库密码、API Key 等敏感信息。直接写 values.yaml 会进入 Git 仓库,存在安全风险。

方案 1:Sealed Secrets(推荐)

yaml 复制代码
# values.yaml 中引用加密 Secret
postgresql:
  auth:
    existingSecret: "myapp-db-secret"

用 kubeseal 加密 Secret:

bash 复制代码
kubectl create secret generic myapp-db-secret --from-literal=password=prod-pass-2026 --dry-run=client -o yaml | kubeseal -o yaml > sealed-secret.yaml

方案 2:Helm Secrets 插件

bash 复制代码
# 安装插件
helm plugin install https://github.com/jkroepke/helm-secrets

# 加密 values 文件
helm secrets encrypt values/prod-secrets.yaml

# 部署时自动解密
helm secrets upgrade myapp charts/myapp/ -f values/prod.yaml -f values/prod-secrets.yaml

我们团队的选择: CI/CD 中用 Helm Secrets,本地开发用 Sealed Secrets。两种方案各有优劣,但核心原则一致:敏感信息不入 Git。

九、我们落地 Helm 依赖管理的几个关键坑

坑 1:循环依赖导致无限递归。

A 依赖 B,B 又依赖 A。Helm 会直接报错,但排查时容易忽略。规范:依赖图必须是无环有向图(DAG)。

坑 2:子 Chart 版本不兼容。

父 Chart 声明 redis: "17.x.x",但某个子 Chart 又依赖 redis: "16.x.x",版本冲突。解决方案:用 helm dependency list 检查版本树,确保兼容。

坑 3:charts/ 目录忘记提交。

helm dep up 生成的 .tgz 文件应该提交到 Git(除非用 CI/CD 自动构建)。我们团队规定:charts/ 目录必须纳入版本控制,保证任何时间点都能完整复现发布。

坑 4:global 值污染。

global 字段会传递给所有子 Chart,但很多新人不知道。我们在规范里明确:global 只放真正全局的值(如 imageRegistry),环境特定配置必须放在对应子 Chart 命名空间下。

十、写在最后

Helm 的依赖管理和 CI/CD 集成,本质上是在解决应用栈的标准化交付问题

从单 Chart 到多 Chart 依赖,从手动 helm install 到 GitLab CI 自动化流水线,从明文密码到 Helm Secrets 加密------这套工程化能力的演进,映射着我们从"能跑就行"到"可复现、可追溯、可回滚"的架构成熟度提升。

我们团队现在的标准:任何应用栈上线,必须提供 Helm Chart + CI/CD 流水线 + 测试用例。 Chart 即文档,流水线即流程,测试即保障。

下一站,我们可以聊聊 ArgoCD + Helm,看看在 GitOps 体系下,Helm Chart 如何成为声明式部署的核心载体。关注我,架构路上不迷路。

------ 本文是《100 篇架构实战》系列第 90 篇,承接上篇 Helm 基础,进阶探讨依赖管理与自动化集成。

相关推荐
四眼肥鱼2 小时前
【Nextjs】macos 系统运行报错:Error: Cannot find module '../lightningcss.darwin-x64.node'
前端·架构·前端框架
GitLqr3 小时前
别被“Flutter 传感器延迟 150ms”带偏了:这可能只是你的实现方式错了
flutter·架构·kotlin
SamDeepThinking3 小时前
微信支付对接实战:从下单到回调的完整落地过程
后端·程序员·架构
Slice_cy4 小时前
Mint 自研框架设计与实现:从重复开发走向配置驱动(三)
前端·后端·架构
写代码的强哥4 小时前
TiDB 和 OceanBase 对比:架构师视角下的企业选型实战指南
数据库·云原生·架构
张忠琳5 小时前
【NVIDIA】NVIDIA k8s-device-plugin v0.19.3 配置API模块深度分析之二
云原生·容器·架构·kubernetes·nvidia
心念枕惊5 小时前
【Agent Harness】Gliding Horse 整体架构拼图:当 AI Agent 有了自己的操作系统
人工智能·架构
2601_960567966 小时前
电商套图批量生成的效率瓶颈量化——逐图架构与流水线架构的性能对比
架构
LONGZETECH6 小时前
新能源汽车动力系统仿真硬核拆解:五系统分层架构的设计逻辑与技术权衡
c语言·开发语言·架构·汽车·汽车仿真教学软件·汽车教学软件