Helm通过Chart模板将Kubernetes应用配置参数化,实现一键部署与版本化管理的包管理体验。Chart支持依赖管理、值覆盖与模板函数,适合管理复杂的AI推理服务栈。文章从Chart结构设计、values分层配置到私有仓库搭建,给出Helm在AI工程中的落地实践。
需要学习Helm包管理,数据管道与模型训练的衔接需要MLOps能力。本篇涵盖MLOps/LLMOps、模型部署、CI/CD、版本管理,帮助你实现AI工程化交付。
目录
可视化演示
展示Helm Chart目录结构到YAML渲染流程、values分层配置覆盖合并机制、以及Release版本管理与回滚操作。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Chart结构渲染 Chart.yaml声明元信息,values.yaml定义默认配置参数 templates目录存放Go template渲染的K8s资源模板 helm install → 模板渲染 → YAML输出 Chart结构将K8s应用配置参数化打包
Helm Chart 目录结构与核心文件组织
Helm Chart 是 Kubernetes 应用的打包格式,由固定目录结构组成。一个典型的 Chart 包含:Chart.yaml(声明 chart 名称、版本、appVersion 等元信息)、templates/ 目录(存放 Deployment、Service 等 K8s 资源的 Go 模板文件)、values.yaml(提供模板渲染的默认参数值)、charts/ 目录(存放依赖的子 Chart)、_helpers.tpl(定义可复用的命名模板函数)。
Chart.yaml 是 Chart 的元数据文件,其中 apiVersion 指定 Chart API 版本(v2 用于 Helm 3),name 定义 Chart 名称,version 是 Chart 本身的版本号(遵循语义化版本),appVersion 是所打包应用的版本号。type 字段可以是 application(可安装的完整应用)或 library(仅提供模板复用的库 Chart)。
templates 目录中的每个文件都是一个 Go template,Helm 渲染时将其转换为有效的 Kubernetes YAML。模板文件可以使用 .Values 引用 values.yaml 中的参数,使用条件判断、循环等逻辑控制生成的资源。_helpers.tpl 文件以下划线开头,不会被渲染为独立资源,专门用于定义可复用的模板片段,如标准化的标签生成器、名称生成器等。
以 LLM 推理服务为例,一个完整的 Chart 可能包含:templates/deployment.yaml(推理容器)、templates/service.yaml(API 服务)、templates/hpa.yaml(自动扩缩)、templates/configmap.yaml(模型配置)、templates/redis.yaml(缓存子 Chart)。values.yaml 定义 GPU 副本数、模型镜像版本、推理端口等默认参数。
# Chart.yamlapiVersion: v2name: llm-servingversion: 1.2.0appVersion: '2.1.0'type: applicationdescription: LLM inference serving chartdependencies: - name: redis version: '17.x.x' repository: https://charts.bitnami.com/bitnami condition: redis.enabled
values.yaml 分层配置与值覆盖机制
Helm 的 values 机制支持多层级配置覆盖。默认值在 values.yaml 中定义,部署时可通过 -f values-prod.yaml 加载环境特定配置覆盖默认值,也可用 --set image.tag=v1.2.3 临时覆盖单个参数。多个 -f 文件按顺序加载,后面的覆盖前面的同名键。
覆盖规则是浅层合并(shallow merge):同级键被整体替换而非深度合并。例如子 Chart 定义了 config.data: {a: 1, b: 2},父 Chart 的 values 中设置 config.data: {c: 3},结果是 config.data 整体变为 {c: 3},而非 {a: 1, b: 2, c: 3}。这一点与 Kustomize 的 strategic merge 不同,需要特别注意。
values 优先级从低到高:Chart 自带 values.yaml < 第一个 -f 文件 < 第二个 -f 文件 < --set 参数。这种分层机制使得同一套 Chart 模板可以适配不同环境:values-dev.yaml 设置 1 个副本无 GPU,values-prod.yaml 设置 3 个 GPU 副本并启用 HPA,切换环境只需更换 values 文件。
子 Chart 的 values 通过父 Chart 的 values 中子 Chart 名称作为键传入。例如父 Chart 依赖 redis 子 Chart,可以在父 Chart 的 values.yaml 中通过 redis: {architecture: replication, auth: {enabled: true}} 传递参数给子 Chart。global 键下的值对所有子 Chart 可见,适合传递镜像仓库地址等全局配置。
# values-dev.yamlreplicaCount: 1gpu: enabled: false resources: limits: {}image: tag: '2.1.0-dev'redis: enabled: true architecture: standalone # values-prod.yamlreplicaCount: 3gpu: enabled: true resources: limits: nvidia.com/gpu: 1image: tag: '2.1.0'hpa: enabled: true minReplicas: 3 maxReplicas: 10redis: enabled: true architecture: replication
Go Template 模板函数与条件渲染逻辑
Helm 模板使用 Go template 语法,通过 {{ .Values.xxx }} 引用 values 参数渲染到 YAML 中。支持 if/else 条件判断(如根据 .Values.hpa.enabled 决定是否生成 HPA 资源)、range 循环(如遍历 .Values.ports 生成多端口 Service 配置)。
内置函数增强了模板表达力:include 复用 _helpers.tpl 中定义的命名模板(如 include "llm-serving.fullname" . 生成标准化的资源名称);required 标记必填参数,缺失时终止渲染并报错;toYaml 将值转为 YAML 格式(常用于 labels 和 annotations);tpl 将字符串作为模板再次渲染(用于动态模板场景)。
条件渲染是 Helm 模板的核心能力。典型用法包括:根据 .Values.gpu.enabled 决定是否添加 GPU 资源限制;根据 .Values.hpa.enabled 决定是否渲染 HPA 资源文件;根据 .Values.ingress.enabled 决定是否创建 Ingress 资源。每个条件分支确保只生成当前环境需要的资源。
range 循环用于处理列表型配置。例如遍历 .Values.extraEnv 生成多个环境变量、遍历 .Values.service.ports 生成多个端口定义、遍历 .Values.volumes 生成多个卷挂载。循环内部可以嵌套条件判断,实现灵活的模板逻辑。
# templates/deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: {{ include "llm-serving.fullname" . }} labels: {{- include "llm-serving.labels" . | nindent 4 }}spec: replicas: {{ .Values.replicaCount }} template: spec: containers: - name: model-server image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" resources: limits: {{- if .Values.gpu.enabled }} nvidia.com/gpu: {{ .Values.gpu.count }} {{- end }} memory: {{ .Values.resources.memory | quote }} requests: cpu: {{ .Values.resources.cpu | quote }}
Chart 依赖管理与子 Chart 组合复用
复杂应用通常拆分为多个子 Chart 通过依赖管理组合。在 Chart.yaml 的 dependencies 字段声明依赖的子 Chart(如 redis、prometheus),每个依赖指定 name、version 和 repository。执行 helm dependency update 会下载子 Chart 到 charts/ 目录,或执行 helm dependency build 根据 Chart.lock 精确构建依赖。
父 Chart 通过 values.yaml 中与子 Chart 同名的键传递参数。例如依赖 redis 子 Chart 时,在父 Chart 的 values.yaml 中通过 redis: {architecture: replication} 传递参数。子 Chart 接收到的 values 是父 Chart 中 redis 键下的内容,而非整个 values。
global 键是特殊的全局配置通道,其下的值对所有子 Chart 可见。适合传递镜像仓库地址、公共标签、全局环境变量等需要跨 Chart 共享的配置。子 Chart 通过 .Values.global.imageRegistry 引用全局值。
Chart.lock 文件锁定依赖的精确版本(类似 package-lock.json),确保团队成员和 CI 环境使用完全一致的依赖版本。使用 helm dependency update 更新依赖时会更新 Chart.lock,建议将其提交到版本控制中。
Helm Release 版本管理与回滚机制
Helm 跟踪每次部署为一个 release,每次 install 或 upgrade 都会生成递增的 revision 版本号。helm history 命令查看 release 的所有版本历史,包括每个 revision 的状态(deployed/superseded/failed)、更新时间、Chart 版本和 app 版本。
helm rollback 命令将 release 回退到指定 revision。例如 helm rollback my-release 3 将 my-release 回退到第 3 个 revision。回退操作本身也会生成新的 revision 记录。如果不指定 revision 号,默认回退到上一个版本。
release 信息(包括渲染后的 manifests 和 values)存储在 K8s 集群内的 Secret(Helm 3 默认)或 ConfigMap 中。这意味着即使本地配置丢失,也能从集群恢复 release 状态。每个 release 的数据存储在 kube-system 命名空间的 sh.helm.release.v1.<release-name>.v<revision> Secret 中。
helm upgrade 执行的是三方合并(3-way strategic merge):对比上次部署的 manifest、当前集群实际状态和新 manifest。但已删除的 K8s 资源(如手动删除的 Service)Helm 不会自动重建,除非使用 --force 强制重建所有资源或 helm uninstall 后重新 install。
# 模拟 Helm release 版本管理import jsonfrom datetime import datetime class HelmRelease: def __init__(self, name, namespace='default'): self.name = name self.namespace = namespace self.revisions = [] self.current_revision = 0 def install(self, chart, values): self.current_revision += 1 self.revisions.append({ 'revision': self.current_revision, 'chart': chart, 'values': values, 'status': 'deployed', 'timestamp': datetime.now().isoformat() }) return self.current_revision def upgrade(self, chart, values): if self.revisions: self.revisions[-1]['status'] = 'superseded' return self.install(chart, values) def rollback(self, target_revision=None): if target_revision is None: for rev in reversed(self.revisions[:-1]): if rev['status'] == 'superseded': target_revision = rev['revision'] break target = next(r for r in self.revisions if r['revision'] == target_revision) self.revisions[-1]['status'] = 'superseded' return self.install(target['chart'], target['values']) def history(self): return [(r['revision'], r['status'], r['chart']) for r in self.revisions] # Demorelease = HelmRelease('llm-serving')print(f"Install v1: revision {release.install('llm-serving-1.0.0', {'replicas': 1})}")print(f"Upgrade v2: revision {release.upgrade('llm-serving-1.1.0', {'replicas': 3})}")print(f"Upgrade v3: revision {release.upgrade('llm-serving-1.2.0', {'replicas': 5})}")print(f"Rollback: revision {release.rollback(2)}")print(f"History: {release.history()}")
私有 Chart 仓库搭建与 OCI 注册表集成
团队共享 Chart 需要搭建私有仓库。传统方式是搭建 HTTP 服务器(如 ChartMuseum)作为 Chart 仓库,通过 helm repo add 添加地址。Chart 打包为 .tgz 文件上传到仓库,helm repo update 刷新本地索引后即可安装。
Helm 3 引入 OCI 协议支持,可以将 Chart 推送到 Harbor、AWS ECR、GitHub Container Registry 等支持 OCI 的镜像仓库,与容器镜像统一管理。使用 helm package 打包 Chart,helm push 推送到 OCI registry,helm pull 按需拉取。这种方式无需额外搭建 Chart 仓库基础设施。
私有仓库支持 RBAC 权限控制,可以限制哪些团队/用户可以推送和拉取特定 Chart。Chart 签名验证(helm package --sign)使用 GPG 密钥对 Chart 进行签名,安装时验证签名确保 Chart 未被篡改。
在 AI 平台场景中,可以将所有推理服务的 Helm Chart 推送到组织私有 OCI registry,配合 CI/CD 流水线自动打包和推送。模型团队只需在 values 中指定 Chart 版本,即可从私有仓库拉取经过验证的部署模板。
AI 推理服务栈的 Helm 一体化部署实践
AI 推理服务栈通常包含模型推理容器、API 网关、Redis 缓存、Prometheus 监控等多个服务。通过 Helm 将这些服务打包为一个 Chart:主 Chart 管理核心推理资源(Deployment、Service、HPA),Redis 和 Prometheus 作为子 Chart 依赖。
values 分层管理环境差异化配置:values.yaml 定义所有参数的默认值,values-dev.yaml 设置开发环境参数(1 副本、无 GPU、单节点 Redis),values-prod.yaml 设置生产环境参数(3 GPU 副本、HPA、Redis 集群)。部署时通过 -f 指定环境文件即可。
部署前使用 helm template 命令本地预览渲染结果,将模板渲染为纯 YAML 输出到终端,无需连接集群即可检查配置正确性。这对于 CI/CD 流水线中的配置验证特别有用:在部署前执行 helm template 并对比预期输出,确保参数变更不会导致配置错误。
集成到 CI/CD 流水线:模型训练完成后触发 CI 流水线,构建推理镜像并推送到镜像仓库,然后执行 helm upgrade --install model-serving ./chart --set image.tag=$CI_COMMIT_SHA --wait。通过 Chart 的 appVersion 和 image.tag 参数化管理镜像版本,每次代码提交自动更新镜像版本并滚动发布。部署失败时执行 helm rollback 自动回退。
# CI/CD 流水线中的 Helm 部署步骤# 1. 本地预览验证# helm template llm-serving ./chart -f values-prod.yaml # 2. 部署到集群# helm upgrade --install llm-serving ./chart \# -f values-prod.yaml \# --set image.tag=$CI_COMMIT_SHORT_SHA \# --wait --timeout 300s # 3. 验证部署状态# helm status llm-serving# helm history llm-serving # 4. 回滚(如果部署失败)# helm rollback llm-serving
AI 应用场景
AI推理服务栈的参数化部署与环境隔离
阶段:模型部署
解决的问题:AI推理服务需要在不同环境(开发/测试/生产)部署且配置参数不同,手工编写多个YAML文件容易出错且难以维护。
具体案例:为LLM推理服务编写Helm Chart,包含Deployment、Service、HPA、ConfigMap等资源模板。values.yaml定义GPU副本数、模型镜像版本、推理端口等参数。dev环境values-dev.yaml设置1个副本且不启用GPU,prod环境values-prod.yaml设置3个GPU副本并启用HPA。通过helm install llm-serving ./chart -f values-prod.yaml一键部署,切换环境只需更换values文件无需修改模板。
模型版本迭代的Helm CI/CD自动化发布
阶段:模型部署
解决的问题:AI模型从训练完成到上线部署需要自动化且可回滚的交付流程,手动部署无法保证一致性和可追溯性。
具体案例:在GitLab CI中配置Helm部署阶段:模型训练完成后触发CI流水线,构建推理镜像并推送到镜像仓库,然后执行helm upgrade --install model-serving ./chart --set image.tag=$CI_COMMIT_SHORT_SHA --wait。通过Chart的appVersion和image.tag参数化管理镜像版本,每次代码提交自动更新镜像版本并滚动发布。如果部署后健康检查失败,执行helm rollback model-serving自动回退到上一个成功的release版本。
Python 演示
import yamlimport jsonfrom dataclasses import dataclass, fieldfrom typing import Dict, List, Optional @dataclassclass HelmValues: def __init__(self, data: Dict = None): self._data = data or {} def get(self, key: str, default=None): keys = key.split('.') val = self._data for k in keys: if isinstance(val, dict) and k in val: val = val[k] else: return default return val def merge(self, other: 'HelmValues') -> 'HelmValues': result = dict(self._data) for key, value in other._data.items(): result[key] = value return HelmValues(result) def render_template(template_str: str, values: HelmValues) -> str: result = template_str import re placeholders = re.findall(r'\{\{\s*\.Values\.(\S+?)\s*\}\}', template_str) for ph in placeholders: val = values.get(ph, '') result = result.replace('{{{{ .Values.{0} }}}}'.format(ph), str(val)) return result def simulate_helm_install(chart_name: str, values_files: List[Dict]) -> Dict: base_values = HelmValues() for vf in values_files: base_values = base_values.merge(HelmValues(vf)) deployment_template = '''apiVersion: apps/v1kind: Deploymentmetadata: name: {name}spec: replicas: {{ .Values.replicaCount }} template: spec: containers: - name: model-server image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"''' rendered = deployment_template.replace('{name}', chart_name) rendered = render_template(rendered, base_values) return { 'release': chart_name, 'values': base_values._data, 'manifest': rendered } # Demo: multi-environment deploymentbase = {'replicaCount': 1, 'image': {'repository': 'llm-model', 'tag': 'latest'}}dev = {'replicaCount': 1, 'gpu': {'enabled': False}}prod = {'replicaCount': 3, 'gpu': {'enabled': True}, 'image': {'tag': 'v2.1.0'}} print('=== Dev Environment ===')dev_result = simulate_helm_install('llm-serving', [base, dev])print(f"Replicas: {dev_result['values']['replicaCount']}")print(f"Image tag: {dev_result['values']['image']['tag']}") print('\n=== Prod Environment ===')prod_result = simulate_helm_install('llm-serving', [base, prod])print(f"Replicas: {prod_result['values']['replicaCount']}")print(f"Image tag: {prod_result['values']['image']['tag']}")print(f"GPU enabled: {prod_result['values']['gpu']['enabled']}')
class HelmValues:定义 HelmValues 类封装 values 数据,提供 get 和 merge 方法def get(self, key: str, default=None):支持点号分隔的路径访问,如 'image.tag' 获取嵌套值def merge(self, other: 'HelmValues') -> 'HelmValues':实现浅层合并:同级键整体替换,模拟 Helm 的 values 覆盖行为def render_template(template_str, values):模拟 Go template 渲染:正则匹配 {{ .Values.xxx }} 占位符并替换为实际值def simulate_helm_install(chart_name, values_files):模拟 helm install:按顺序加载多个 values 文件并合并,然后渲染模板base_values = base_values.merge(HelmValues(vf))依次合并每个 values 文件,后者覆盖前者的同名键dev_result = simulate_helm_install('llm-serving', [base, dev])开发环境:base + dev values 合并,1 副本无 GPUprod_result = simulate_helm_install('llm-serving', [base, prod])生产环境:base + prod values 合并,3 副本启用 GPU,镜像 tag 为 v2.1.0
常见问答
Helm 3 和 Helm 2 的主要区别是什么?
helm upgrade 和 helm rollback 的区别是什么?
如何在 Helm 模板中引用子 Chart 的值?
常见误区
误区认为Helm只是一个YAML模板渲染工具,与envsubst或jinja2没有本质区别
正解Helm不仅是模板引擎,更是release生命周期管理器。它维护每次部署的revision版本历史,支持helm rollback回滚到任意历史版本,还管理Chart依赖关系和私有仓库。模板渲染只是其功能的一部分。
为何常见:学习者第一次接触Helm时主要看到模板渲染功能,容易忽略其release管理和版本历史能力。
误区认为values.yaml的合并是深度合并,子键级别的覆盖会保留未覆盖的兄弟键
正解Helm的values合并是浅层合并:父chart覆盖子chart同名的顶级键是整体替换而非深度合并。如果子chart有一个config.data对象,父chart的config.data只设了一个子键,结果是子chart的config.data整个被父chart的替换,而非两个对象的字段合并。
为何常见:其他配置工具(如Kustomize的strategic merge)支持深度合并,学习者把这种预期带到Helm中导致配置被意外覆盖。
误区认为helm upgrade只更新发生变化的K8s资源,不影响未修改的资源
正解helm upgrade执行的是三方合并(3-way strategic merge):对比上次部署的manifest、当前集群实际状态和新manifest。但已删除的K8s资源(如手动删除的Service)Helm不会自动重建,除非使用--force强制重建所有资源或helm uninstall后重新install。
为何常见:学习者期望helm upgrade像kubectl apply一样是声明式的幂等操作,但Helm的三方合并在处理已删除资源时行为与预期不同。
参考来源
- webpageHelm Official DocumentationHelm 官方文档,包含 Chart 开发指南、values 配置、模板函数参考、最佳实践等完整内容。
- webpageHelm GitHub RepositoryHelm 开源仓库,包含源码、Issue 追踪和贡献指南。
- webpageHelm Chart Best PracticesHelm Chart 开发最佳实践指南,涵盖 conventions、values、templates 等方面。
- webpageKubernetes Documentation - HelmKubernetes 官方文档中关于 Helm 安装和使用的指南。