使用helm创建属于自己的chart
作为一名运维工作者,学习 Helm 是管理 Kubernetes 应用的关键一步。下面我将为你详细讲解如何使用 helm create
创建自己的 Chart,并逐步将其完善以应用到生产环境中。
⚙️ 理解 Helm Chart 基础
Helm Chart 就像 Kubernetes 应用的“安装包”,它包含了一组预配置的 Kubernetes 资源定义(如 Deployment、Service 等)。使用 Helm 可以让你轻松地打包、配置、部署和管理这些应用。
一个典型的 Helm Chart 目录结构如下所示(以名为 mychart
的 Chart 为例):
mychart/
├── charts/ # 存放其他依赖的 Chart(子 Chart)
├── templates/ # 模板文件目录
│ ├── deployment.yaml # 应用部署模板
│ ├── service.yaml # 服务模板
│ └── NOTES.txt # 安装后提示信息
├── Chart.yaml # Chart 元数据文件
├── values.yaml # 默认配置值文件
└── .helmignore # 忽略文件列表(类似 .gitignore)
🛠️ 第一步:创建 Helm Chart
-
使用
helm create
搭建脚手架
打开终端,执行以下命令来创建一个名为myapp
的 Chart:$ helm create myapp
这个命令会生成一个包含基本目录结构和示例文件的 Chart
$ tree myapp/ myapp/ ├── charts ├── Chart.yaml ├── templates │ ├── deployment.yaml │ ├── _helpers.tpl │ ├── hpa.yaml │ ├── ingress.yaml │ ├── NOTES.txt │ ├── serviceaccount.yaml │ ├── service.yaml │ └── tests │ └── test-connection.yaml └── values.yaml3 directories, 10 files
-
解读生成的文件
创建完成后,你需要了解几个核心文件的作用:Chart.yaml
:描述 Chart 的基本信息,如名称、版本、描述等,你需要根据你的应用修改这些内容。values.yaml
:这是 Chart 的默认配置值文件。所有在模板中引用的参数(例如镜像名称、副本数等)都可以在这里定义默认值。templates/
目录:存放所有 Kubernetes 资源模板文件(如 Deployment、Service 等)。这些文件使用 Go 模板语法,并可以引用values.yaml
中定义的参数。
🔧 第二步:定制化你的 Chart
默认生成的 Chart 是一个 Nginx 示例,你需要修改它以部署自己的应用。
-
修改
Chart.yaml
用文本编辑器打开myapp/Chart.yaml
,更新其中的元数据,例如:cat Chart.yaml |grep -v '^#'
apiVersion: v2 name: myapp description: A Helm chart for Kubernetestype: applicationversion: 0.1.0appVersion: "1.16.0"
-
配置
values.yaml
这是定制应用的核心。用文本编辑器打开myapp/values.yaml
,你会看到很多可配置的项。根据你的应用需求修改,例如:replicaCount: 2 # 指定副本数量image:repository: nginx # 你的应用镜像仓库地址tag: "latest" # 镜像标签pullPolicy: IfNotPresentservice:type: ClusterIPport: 80resources:limits:memory: "128Mi"cpu: "500m"requests:memory: "128Mi"cpu: "500m"
-
编写或调整
templates/
下的模板文件
根据你的应用所需 Kubernetes 资源,在templates/
目录下创建或修改相应的 YAML 模板文件。这些模板会使用values.yaml
中定义的参数。例如,一个简化的
templates/deployment.yaml
长这样:apiVersion: apps/v1 kind: Deployment metadata:name: {{ .Release.Name }}-deployment # Helm会替换Release.Name为发布名labels:app: nginx spec:replicas: {{ .Values.replicaCount }} # 使用values.yaml中的replicaCount值selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: {{ .Values.image.repository }}:{{ .Values.image.tag }} # 使用values.yaml中的镜像信息ports:- containerPort: 80
你可以根据需要,参照 Kubernetes 文档和 Helm 模板指南,在
templates/
目录下创建其他资源模板,如service.yaml
、ingress.yaml
等。
🧪 第三步:调试与测试
在正式部署前,务必进行调试和测试。
-
使用
helm lint
检查 Chart 格式
在 Chart 目录(myapp/
)的上一级,运行:helm lint ./myapp #结果输出 ==> Linting ./myapp [INFO] Chart.yaml: icon is recommended1 chart(s) linted, 0 chart(s) failed
这个命令会检查 Chart 的语法和结构是否正确。
-
使用
helm template
或--dry-run
渲染模板
为了预览 Helm 实际会生成哪些 Kubernetes 资源清单,可以使用:helm install my-dry-run ./myapp --dry-run --debug
#输出结果如下: install.go:214: [debug] Original chart version: "" install.go:231: [debug] CHART PATH: /root/helm/test/myappNAME: my-dry-run LAST DEPLOYED: Wed Oct 15 17:13:21 2025 NAMESPACE: default STATUS: pending-install REVISION: 1 USER-SUPPLIED VALUES: {}COMPUTED VALUES: affinity: {} autoscaling:enabled: falsemaxReplicas: 100minReplicas: 1targetCPUUtilizationPercentage: 80 fullnameOverride: "" image:pullPolicy: IfNotPresentrepository: nginxtag: "" imagePullSecrets: [] ingress:annotations: {}className: ""enabled: falsehosts:- host: chart-example.localpaths:- path: /pathType: ImplementationSpecifictls: [] nameOverride: "" nodeSelector: {} podAnnotations: {} podLabels: {} podSecurityContext: {} replicaCount: 1 resources: {} securityContext: {} service:port: 80type: ClusterIP serviceAccount:annotations: {}automount: truecreate: truename: "" tolerations: [] volumeMounts: [] volumes: []HOOKS: --- # Source: myapp/templates/tests/test-connection.yaml apiVersion: v1 kind: Pod metadata:name: "my-dry-run-myapp-test-connection"labels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helmannotations:"helm.sh/hook": test spec:containers:- name: wgetimage: busyboxcommand: ['wget']args: ['my-dry-run-myapp:80']restartPolicy: Never MANIFEST: --- # Source: myapp/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata:name: my-dry-run-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm automountServiceAccountToken: true --- # Source: myapp/templates/service.yaml apiVersion: v1 kind: Service metadata:name: my-dry-run-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm spec:type: ClusterIPports:- port: 80targetPort: httpprotocol: TCPname: httpselector:app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-run --- # Source: myapp/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: my-dry-run-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm spec:replicas: 1selector:matchLabels:app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runtemplate:metadata:labels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: my-dry-runapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helmspec:serviceAccountName: my-dry-run-myappsecurityContext:{}containers:- name: myappsecurityContext:{}image: "nginx:1.16.0"imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80protocol: TCPlivenessProbe:httpGet:path: /port: httpreadinessProbe:httpGet:path: /port: httpresources:{}NOTES: 1. Get the application URL by running these commands:export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=myapp,app.kubernetes.io/instance=my-dry-run" -o jsonpath="{.items[0].metadata.name}")export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")echo "Visit http://127.0.0.1:8080 to use your application"kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
或者
helm template ./myapp
# 输出结果如下: --- # Source: myapp/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata:name: release-name-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nameapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm automountServiceAccountToken: true --- # Source: myapp/templates/service.yaml apiVersion: v1 kind: Service metadata:name: release-name-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nameapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm spec:type: ClusterIPports:- port: 80targetPort: httpprotocol: TCPname: httpselector:app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-name --- # Source: myapp/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: release-name-myapplabels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nameapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helm spec:replicas: 1selector:matchLabels:app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nametemplate:metadata:labels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nameapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helmspec:serviceAccountName: release-name-myappsecurityContext:{}containers:- name: myappsecurityContext:{}image: "nginx:1.16.0"imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80protocol: TCPlivenessProbe:httpGet:path: /port: httpreadinessProbe:httpGet:path: /port: httpresources:{} --- # Source: myapp/templates/tests/test-connection.yaml apiVersion: v1 kind: Pod metadata:name: "release-name-myapp-test-connection"labels:helm.sh/chart: myapp-0.1.0app.kubernetes.io/name: myappapp.kubernetes.io/instance: release-nameapp.kubernetes.io/version: "1.16.0"app.kubernetes.io/managed-by: Helmannotations:"helm.sh/hook": test spec:containers:- name: wgetimage: busyboxcommand: ['wget']args: ['release-name-myapp:80']restartPolicy: Never
这不会真正部署应用,而是将渲染后的模板输出到终端,方便你检查。
-
在测试环境安装 Chart
在测试 Kubernetes 集群中,使用helm install
命令部署你的 Chart:helm install my-test-release ./myapp
查看部署状态:
helm list kubectl get pods -l app=myapp
🚀 第四步:准备生产部署
将 Chart 用于生产环境,需要考虑更多因素。
-
管理不同环境的配置
不要直接修改默认的values.yaml
来适应不同环境(如开发、测试、生产)。 推荐的做法是:-
保留
values.yaml
作为默认配置。 -
为不同环境创建各自的 values 文件,例如
values-production.yaml
。 -
在部署时,使用
-f
参数指定特定环境的 values 文件:helm install -f values-production.yaml my-prod-release ./myapp
-
-
打包和版本化管理 Chart
-
打包 Chart:使用
helm package
命令将 Chart 打包成一个.tgz
压缩文件,便于分发和存储。helm package ./myapp
-
版本控制:将你的 Chart 代码(包括
Chart.yaml
、values.yaml
和templates/
)纳入版本控制系统(如 Git)。 -
Chart 版本:每次对 Chart 进行重大更改时,记得在
Chart.yaml
中更新version
字段。遵循语义化版本控制规范。
-
-
考虑使用 Chart 仓库
对于团队协作和持续集成/持续部署(CI/CD),可以搭建或使用现有的 Helm Chart 仓库来集中存储和管理 Chart。
📝 生产环境 Chart 进阶要点
- 安全性:确保配置文件不包含敏感信息(如密码、密钥)。对于敏感数据,使用 Kubernetes Secrets,并通过 Helm 模板引用。
- 资源管理:在
values.yaml
中明确定义容器的资源请求(requests
)和限制(limits
),这对于生产环境稳定性至关重要。 - 健康检查:在 Deployment 模板中配置 livenessProbe(存活性探针) 和 readinessProbe(就绪性探针),这是生产级应用的必要配置。
- 考虑使用 CI/CD 流水线:结合 Git 和 CI/CD 工具(如 Jenkins、GitLab CI),可以实现 Chart 的自动化测试、打包和部署。
💎 简单总结
使用 Helm 创建并管理应用的基本工作流可以概括为:创建 Chart -> 定制配置与模板 -> 调试验证 -> 管理部署。作为新手,从修改默认 Chart 开始,逐步理解模板和值文件的工作原理,再尝试为不同环境定制配置。
本文来源于我的微信公众号Linux运维小白
,我会持续更新文章,欢迎大家关注,互相交流学习。