从 0 到 1 构建自定义 Helm Chart
制作自己的 Helm Chart(Helm 包)是 Kubernetes 应用打包和分发的核心步骤,需遵循标准化目录结构、配置模板化、依赖管理等规范。以下是 从 0 到 1 构建自定义 Helm Chart 的详细步骤,包含核心概念、实操流程、最佳实践及常见问题。
一、前置准备:环境搭建
在开始前,需确保本地已安装以下工具:
工具 | 作用 | 安装验证命令 |
---|---|---|
Helm | Chart 构建、打包、部署的核心工具 | helm version |
Kubernetes 集群 | (可选)用于测试 Chart 部署 | kubectl cluster-info |
文本编辑器 | 编写 Chart 配置(如 VS Code、Vim) | - |
-
Helm 安装参考:官方文档(支持 Windows/macOS/Linux)
-
若暂无 K8s 集群,可使用
minikube
或kind
快速搭建本地测试环境。
二、核心概念:Helm Chart 目录结构
一个标准的 Helm Chart 需遵循固定目录结构,确保兼容性和可维护性。创建后默认结构如下:
my-chart/ # Chart 根目录(名称建议与应用名一致)
├── Chart.yaml # Chart 元数据(版本、描述、依赖等)
├── values.yaml # 全局配置变量(可被模板引用,支持用户自定义)
├── charts/ # 子 Chart 依赖(如依赖的 Redis、MySQL 等)
├── templates/ # K8s 资源模板目录(核心)
│ ├── deployment.yaml # Deployment 模板(定义应用副本、镜像等)
│ ├── service.yaml # Service 模板(定义访问入口)
│ ├── ingress.yaml # (可选)Ingress 模板(定义域名路由)
│ ├── _helpers.tpl # (可选)模板函数/变量(复用逻辑)
│ └── NOTES.txt # (可选)部署后提示信息(如访问地址)
└── .helmignore # (可选)打包时忽略的文件(如日志、临时文件)
三、实操步骤:构建自定义 Chart
以 部署一个 Nginx 应用 为例,演示完整的 Chart 制作流程。
步骤 1:创建基础 Chart 框架
使用 helm create
命令自动生成标准目录结构(避免手动创建目录的繁琐):
# 创建名为 "my-nginx-chart" 的 Chart(名称可自定义)
helm create my-nginx-chart
执行后,会在当前目录生成 my-nginx-chart
文件夹,包含上述所有默认文件。
步骤 2:编辑 Chart 元数据(Chart.yaml)
Chart.yaml
是 Chart 的 “身份证”,记录基本信息和依赖,需根据实际需求修改:
apiVersion: v2 # Chart API 版本(v2 是 Helm 3 推荐版本)
name: my-nginx-chart # Chart 名称(必须与根目录一致)
description: A custom Helm Chart for Nginx deployment # 应用描述
type: application # Chart 类型(application:应用类;library:库类,供其他 Chart 引用)
version: 0.1.0 # Chart 版本(遵循 SemVer 2 规范:主版本.次版本.补丁版本)
appVersion: "1.25" # 应用本身的版本(如 Nginx 版本)
# (可选)依赖其他 Chart,如需要 Redis 可添加如下配置
dependencies:
- name: redis # 依赖的 Chart 名称(需在 Helm 仓库中存在)
version: 17.10.0 # 依赖的 Chart 版本
repository: https://charts.bitnami.com/bitnami # 依赖的 Chart 仓库地址
- 关键说明:
* `version`:Chart 版本变更需遵循规则(如修复 bug 升补丁版,新增功能升次版本,不兼容变更升主版本);
* `dependencies`:若有依赖,后续需执行 `helm dependency update` 拉取依赖到 `charts/` 目录。
步骤 3:配置全局变量(values.yaml)
values.yaml
是 Chart 的 “配置中心”,定义模板中可引用的变量(用户部署时可通过 --set
或自定义 values
文件覆盖)。以 Nginx 为例,修改核心配置:
# 1. 应用基础配置
replicaCount: 2 # 部署的副本数(默认 1,此处改为 2 实现高可用)
# 2. 镜像配置(核心)
image:
repository: nginx # 镜像仓库(如私有仓库可写 "harbor.example.com/nginx")
pullPolicy: IfNotPresent # 镜像拉取策略(Always/IfNotPresent/Never)
tag: "1.25" # 镜像标签(与 appVersion 保持一致)
# 3. 镜像拉取密钥(可选,私有仓库需配置)
imagePullSecrets:
- name: my-registry-secret # 需提前在 K8s 集群中创建该 Secret
# 4. Service 配置(访问入口)
service:
type: NodePort # Service 类型(ClusterIP/NodePort/LoadBalancer)
port: 80 # Service 暴露的端口
nodePort: 30080 # NodePort 端口(范围 30000-32767,可选)
# 5. 资源限制(可选,避免应用占用过多资源)
resources:
limits:
cpu: "500m" # 最大 CPU 占用(500 毫核 = 0.5 核)
memory: "512Mi"# 最大内存占用
requests:
cpu: "100m" # 最小 CPU 申请
memory: "128Mi"# 最小内存申请
# 6. (可选)Nginx 自定义配置(通过 ConfigMap 挂载)
nginxConfig:
serverName: localhost
root: /usr/share/nginx/html
- 设计原则:
* 只保留 “需要用户自定义” 的变量(如副本数、镜像地址、端口),固定逻辑不放入 `values.yaml`;
* 变量命名需清晰(如 `image.repository` 而非 `img.repo`),便于用户理解。
步骤 4:编写 K8s 资源模板(templates/ 目录)
templates/
目录是 Chart 的核心,存放 带 Helm 模板语法的 K8s YAML 文件(模板语法基于 Go Template,支持变量引用、条件判断、循环等)。
以下是核心模板文件的编写示例:
模板 1:Deployment 模板(templates/deployment.yaml)
定义 Nginx 应用的部署逻辑(副本、镜像、挂载、资源限制等):
apiVersion: apps/v1
kind: Deployment
metadata:
# 引用 values 中的 Chart 名称和版本,生成唯一名称
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: {{ .Chart.Name }} # 固定标签(Chart 名称)
release: {{ .Release.Name }} # 动态标签(Helm Release 名称,每次部署唯一)
spec:
replicas: {{ .Values.replicaCount }} # 引用 values 中的副本数
selector:
matchLabels:
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
spec:
# (可选)引用镜像拉取密钥
imagePullSecrets:
{{- toYaml .Values.imagePullSecrets | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
# 引用镜像配置(仓库:标签)
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
# 容器端口(需与 Nginx 内部端口一致,默认 80)
ports:
- name: http
containerPort: 80
protocol: TCP
# (可选)健康检查(确保容器正常运行)
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30 # 启动后延迟 30s 检查
periodSeconds: 10 # 每 10s 检查一次
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
# (可选)引用资源限制
resources:
{{- toYaml .Values.resources | nindent 12 }}
# (可选)挂载 ConfigMap 到容器(自定义 Nginx 配置)
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
# (可选)定义 ConfigMap 卷(与上面的 volumeMounts 对应)
volumes:
- name: nginx-config
configMap:
name: {{ .Release.Name }}-nginx-config
items:
- key: default.conf
path: default.conf
模板 2:Service 模板(templates/service.yaml)
定义应用的访问入口,关联 Deployment 的 Pod:
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
spec:
type: {{ .Values.service.type }} # 引用 values 中的 Service 类型
selector:
# 必须与 Deployment 中 Pod 的标签一致,否则无法关联
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
ports:
- port: {{ .Values.service.port }} # Service 暴露的端口
targetPort: http # 对应容器的端口(与 Deployment 中 port.name 一致)
protocol: TCP
name: http
{{- if eq .Values.service.type "NodePort" }}
nodePort: {{ .Values.service.nodePort }} # 仅 NodePort 类型需要
{{- end }}
模板 3:(可选)ConfigMap 模板(templates/configmap.yaml)
用于挂载自定义 Nginx 配置(避免直接修改镜像):
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-nginx-config
labels:
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
data:
default.conf: |
server {
listen 80;
server_name {{ .Values.nginxConfig.serverName }}; # 引用 values 中的配置
root {{ .Values.nginxConfig.root }};
location / {
index index.html;
}
}
模板 4:(可选)NOTES.txt(部署后提示)
部署成功后,Helm 会输出该文件的内容,指导用户访问应用:
恭喜!{{ .Chart.Name }} 已成功部署到 {{ .Release.Namespace }} 命名空间。
访问方式:
1. 若 Service 类型为 NodePort:
curl http://<K8s-Node-IP>:{{ .Values.service.nodePort }}
2. 若 Service 类型为 ClusterIP(仅集群内访问):
kubectl exec -it <任意Pod名> -- curl http://{{ .Release.Name }}-{{ .Chart.Name }}:{{ .Values.service.port }}
查看 Pod 状态:
kubectl get pods -l app={{ .Chart.Name }} -n {{ .Release.Namespace }}
步骤 5:处理依赖(可选)
若 Chart.yaml
中定义了依赖(如 Redis),需执行以下命令拉取依赖到 charts/
目录:
# 进入 Chart 根目录
cd my-nginx-chart
# 拉取依赖(自动下载到 charts/ 目录)
helm dependency update
执行后,会生成 Chart.lock
文件(记录依赖的精确版本,确保每次部署依赖一致),不可手动修改。
步骤 6:验证 Chart 合法性
使用 helm lint
命令检查 Chart 是否存在语法错误、目录结构问题等:
# 检查当前目录的 Chart
helm lint my-nginx-chart
-
若输出
1 chart(s) linted, 0 chart(s) failed
,说明 Chart 合法; -
若有错误(如模板语法错误、变量未定义),需根据提示修复。
步骤 7:本地测试部署(可选)
在正式打包前,可通过 helm install --dry-run
模拟部署,验证模板渲染后的 K8s 配置是否正确:
# 模拟部署(--dry-run 不实际创建资源,--debug 输出渲染后的 YAML)
helm install my-nginx-test ./my-nginx-chart --dry-run --debug
- 查看输出的 YAML 内容,确认:
* 镜像地址、副本数、端口等是否与 `values.yaml` 一致;
* 标签匹配(Deployment 的 selector 与 Pod 标签一致);
* 依赖资源(如 ConfigMap)是否正确挂载。
步骤 8:打包 Chart
将 Chart 打包为 .tgz
文件(便于分发和部署):
# 打包当前目录的 Chart,输出 my-nginx-chart-0.1.0.tgz(名称=Chart名-版本)
helm package my-nginx-chart
打包后,当前目录会生成 .tgz
文件,可上传到 Helm 仓库(如 Harbor、ChartMuseum)供他人使用。
四、部署自定义 Chart
打包完成后,可在 K8s 集群中部署该 Chart:
# 1. 部署 Chart(指定 Release 名称为 my-nginx,使用自定义 values 文件)
helm install my-nginx ./my-nginx-chart \
--namespace my-nginx-namespace \ # 部署到指定命名空间(需提前创建)
--set replicaCount=3 \ # 覆盖 values 中的副本数(优先级高于 values.yaml)
--values my-custom-values.yaml # (可选)使用自定义 values 文件覆盖配置
# 2. 查看部署状态
helm list -n my-nginx-namespace # 查看 Release 状态
kubectl get pods -n my-nginx-namespace # 查看 Pod 是否正常运行
# 3. (可选)更新部署(修改配置后执行)
helm upgrade my-nginx ./my-nginx-chart --set image.tag=1.26 -n my-nginx-namespace
# 4. (可选)卸载部署
helm uninstall my-nginx -n my-nginx-namespace
五、最佳实践
- 模板复用:将重复逻辑(如标签、注释)放入
templates/_helpers.tpl
,例如:
# 定义标签模板,可在其他模板中引用
{{- define "my-nginx-chart.labels" }}
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
version: {{ .Chart.Version }}
{{- end }}
在 Deployment 中引用:
metadata:
labels:
{{- include "my-nginx-chart.labels" . | nindent 4 }}
-
变量校验:使用
helm lint
定期检查,避免变量未定义或类型错误; -
版本管理:严格遵循 SemVer 2 规范管理
Chart.yaml
中的version
,避免版本混乱; -
安全配置:
-
敏感信息(如密码、密钥)不放入
values.yaml
,改用 K8s Secret,通过values.yaml
引用 Secret 名称; -
私有镜像仓库需配置
imagePullSecrets
,避免硬编码凭证;
- 文档完善:在 Chart 根目录添加
README.md
,说明:
-
Chart 功能、适用场景;
-
values.yaml
中关键变量的含义; -
部署步骤和依赖要求。
六、常见问题
- 模板渲染错误:
-
报错
undefined variable
:检查变量名是否与values.yaml
一致(如.Values.image.repo
应为.Values.image.repository
); -
语法错误:确保模板标签闭合(如
{{ .Release.Name }}
而非{{ .Release.Name
)。
- 依赖拉取失败:
-
检查
Chart.yaml
中repository
地址是否正确(如 Bitnami 仓库地址是否为https://charts.bitnami.com/bitnami
); -
网络问题:配置 Helm 代理(
export HTTP_PROXY=http://proxy:port
)。
- Pod 启动失败:
-
镜像拉取失败:检查
image.repository
是否正确,私有仓库是否配置imagePullSecrets
; -
健康检查失败:调整
livenessProbe
/readinessProbe
的initialDelaySeconds
(如 Nginx 启动较慢,可设为 30s)。
通过以上步骤,你已完成自定义 Helm Chart 的制作、验证、打包和部署。可根据实际应用(如 Java 服务、Python 服务)调整 templates/
目录下的资源模板和 values.yaml
配置,实现标准化的 K8s 应用管理。
他人使用你制作的 my-nginx-chart-0.1.0.tgz Helm 包
非常简单,无需手动解压,直接通过 Helm 命令即可完成部署。以下是详细步骤和使用方式:
一、直接使用本地包部署(适合测试或内部共享)
如果对方能直接获取到 .tgz 文件(例如通过文件传输、共享目录等),可直接用以下命令部署:
步骤 1:将包传输给使用者
通过邮件、网盘、scp 等方式,将 my-nginx-chart-0.1.0.tgz 发送给对方,对方保存到本地目录(如 ~/charts/)。
步骤 2:部署本地包
使用者执行以下 Helm 命令部署:
# 部署本地的 Chart 包,指定 Release 名称(如 my-nginx-deploy)
helm install my-nginx-deploy ~/charts/my-nginx-chart-0.1.0.tgz \
--namespace my-nginx # 可选:指定部署的命名空间(需提前创建)
说明:
Helm 会自动处理 .tgz 包的解压和解析,无需手动执行 tar -zxvf 等命令;
部署时可通过 --set 覆盖配置(如修改副本数):
# 示例:部署时将副本数改为 3,端口改为 30081
helm install my-nginx-deploy ~/charts/my-nginx-chart-0.1.0.tgz \
--set replicaCount=3 \
--set service.nodePort=30081 \
--namespace my-nginx
二、通过 Helm 仓库共享(适合团队 / 公开分发)
如果需要多人长期使用,更推荐将 .tgz 包上传到 Helm 仓库(类似 Docker 镜像仓库),使用者通过仓库地址拉取部署,步骤如下:
步骤 1:你需要搭建或使用现有 Helm 仓库
常见的 Helm 仓库方案:
公开仓库:如 Artifact Hub(需审核);
私有仓库:如 Harbor(支持 Helm 仓库)、ChartMuseum、GitLab Pages 等。
以 Harbor 为例,上传步骤:
在 Harbor 中创建一个 Helm 仓库(如 my-charts);
使用 helm push 命令上传包:
# 先添加 Harbor 仓库到本地 Helm
helm repo add my-harbor https://harbor.example.com/chartrepo/my-charts \
--username 用户名 --password 密码
# 上传 .tgz 包到仓库
helm push my-nginx-chart-0.1.0.tgz my-harbor
步骤 2:使用者通过仓库部署
使用者添加你的仓库到本地:
helm repo add my-harbor https://harbor.example.com/chartrepo/my-charts \
--username 用户名 --password 密码
# 更新仓库索引(确保获取最新 Chart)
helm repo update
搜索并部署你的 Chart:
# 搜索仓库中的 Chart
helm search repo my-harbor/my-nginx-chart
# 部署(无需本地保存 .tgz 文件,直接从仓库拉取)
helm install my-nginx-deploy my-harbor/my-nginx-chart \
--version 0.1.0 \ # 指定版本(可选,默认最新版)
--namespace my-nginx
三、验证部署结果
无论通过哪种方式部署,使用者都可以通过以下命令验证:
# 查看已部署的 Release
helm list -n my-nginx
# 查看 Pod 状态(确保 Running)
kubectl get pods -n my-nginx
# 查看 Service 信息(获取访问地址)
kubectl get svc -n my-nginx
如果部署时包含 NOTES.txt, Helm 会自动输出访问指南,例如:
恭喜!my-nginx-chart 已成功部署到 my-nginx 命名空间。
访问方式:
curl http://<节点IP>:30080
四、总结
本地包使用:适合临时测试,直接用 helm install <包路径> 部署,无需解压;
仓库分发:适合团队协作,上传到仓库后,使用者通过仓库地址一键部署;
灵活性:使用者可通过 --set 或自定义 values.yaml 覆盖默认配置,无需修改你的原始包。
这种方式既简化了部署流程,又保证了配置的灵活性,是 Helm 包的标准使用方式。
(注:文档部分内容可能由 AI 生成)