作为一名运维工作者,学习 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

  1. 使用 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.yaml
    
    3 directories, 10 files
    
  2. 解读生成的文件
    创建完成后,你需要了解几个核心文件的作用:

    • Chart.yaml:描述 Chart 的基本信息,如名称、版本、描述等,你需要根据你的应用修改这些内容。
    • values.yaml:这是 Chart 的默认配置值文件。所有在模板中引用的参数(例如镜像名称、副本数等)都可以在这里定义默认值。
    • templates/ 目录:存放所有 Kubernetes 资源模板文件(如 Deployment、Service 等)。这些文件使用 Go 模板语法,并可以引用 values.yaml 中定义的参数。

🔧 第二步:定制化你的 Chart

默认生成的 Chart 是一个 Nginx 示例,你需要修改它以部署自己的应用。

  1. 修改 Chart.yaml
    用文本编辑器打开 myapp/Chart.yaml,更新其中的元数据,例如:

    cat Chart.yaml |grep -v '^#'
    
    apiVersion: v2
    name: myapp
    description: A Helm chart for Kubernetes
    
    type: application
    
    version: 0.1.0
    
    appVersion: "1.16.0"
    
  2. 配置 values.yaml
    这是定制应用的核心。用文本编辑器打开 myapp/values.yaml,你会看到很多可配置的项。根据你的应用需求修改,例如:

    replicaCount: 2  # 指定副本数量
    
    image:
      repository: nginx  # 你的应用镜像仓库地址
      tag: "latest"      # 镜像标签
      pullPolicy: IfNotPresent
    
    service:
      type: ClusterIP
      port: 80
    
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
      requests:
        memory: "128Mi"
        cpu: "500m"
    
  3. 编写或调整 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: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: {{ .Values.image.repository }}:{{ .Values.image.tag }}  # 使用values.yaml中的镜像信息
            ports:
            - containerPort: 80
    

    你可以根据需要,参照 Kubernetes 文档和 Helm 模板指南,在 templates/ 目录下创建其他资源模板,如 service.yamlingress.yaml 等。

🧪 第三步:调试与测试

在正式部署前,务必进行调试和测试。

  1. 使用 helm lint 检查 Chart 格式
    在 Chart 目录(myapp/)的上一级,运行:

    helm lint ./myapp
    #结果输出
    ==> Linting ./myapp
    [INFO] Chart.yaml: icon is recommended
    
    1 chart(s) linted, 0 chart(s) failed
    

    这个命令会检查 Chart 的语法和结构是否正确。

  2. 使用 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/myapp
    
    NAME: 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: false
      maxReplicas: 100
      minReplicas: 1
      targetCPUUtilizationPercentage: 80
    fullnameOverride: ""
    image:
      pullPolicy: IfNotPresent
      repository: nginx
      tag: ""
    imagePullSecrets: []
    ingress:
      annotations: {}
      className: ""
      enabled: false
      hosts:
      - host: chart-example.local
        paths:
        - path: /
          pathType: ImplementationSpecific
      tls: []
    nameOverride: ""
    nodeSelector: {}
    podAnnotations: {}
    podLabels: {}
    podSecurityContext: {}
    replicaCount: 1
    resources: {}
    securityContext: {}
    service:
      port: 80
      type: ClusterIP
    serviceAccount:
      annotations: {}
      automount: true
      create: true
      name: ""
    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.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: my-dry-run
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
      annotations:
        "helm.sh/hook": test
    spec:
      containers:
        - name: wget
          image: busybox
          command: ['wget']
          args: ['my-dry-run-myapp:80']
      restartPolicy: Never
    MANIFEST:
    ---
    # Source: myapp/templates/serviceaccount.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: my-dry-run-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: my-dry-run
        app.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-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: my-dry-run
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      type: ClusterIP
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: my-dry-run
    ---
    # Source: myapp/templates/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-dry-run-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: my-dry-run
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: myapp
          app.kubernetes.io/instance: my-dry-run
      template:
        metadata:
          labels:
            helm.sh/chart: myapp-0.1.0
            app.kubernetes.io/name: myapp
            app.kubernetes.io/instance: my-dry-run
            app.kubernetes.io/version: "1.16.0"
            app.kubernetes.io/managed-by: Helm
        spec:
          serviceAccountName: my-dry-run-myapp
          securityContext:
            {}
          containers:
            - name: myapp
              securityContext:
                {}
              image: "nginx:1.16.0"
              imagePullPolicy: IfNotPresent
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
              livenessProbe:
                httpGet:
                  path: /
                  port: http
              readinessProbe:
                httpGet:
                  path: /
                  port: http
              resources:
                {}
    
    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-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: release-name
        app.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-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      type: ClusterIP
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: release-name
    ---
    # Source: myapp/templates/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: release-name-myapp
      labels:
        helm.sh/chart: myapp-0.1.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: myapp
          app.kubernetes.io/instance: release-name
      template:
        metadata:
          labels:
            helm.sh/chart: myapp-0.1.0
            app.kubernetes.io/name: myapp
            app.kubernetes.io/instance: release-name
            app.kubernetes.io/version: "1.16.0"
            app.kubernetes.io/managed-by: Helm
        spec:
          serviceAccountName: release-name-myapp
          securityContext:
            {}
          containers:
            - name: myapp
              securityContext:
                {}
              image: "nginx:1.16.0"
              imagePullPolicy: IfNotPresent
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
              livenessProbe:
                httpGet:
                  path: /
                  port: http
              readinessProbe:
                httpGet:
                  path: /
                  port: http
              resources:
                {}
    ---
    # 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.0
        app.kubernetes.io/name: myapp
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
      annotations:
        "helm.sh/hook": test
    spec:
      containers:
        - name: wget
          image: busybox
          command: ['wget']
          args: ['release-name-myapp:80']
      restartPolicy: Never
    

    这不会真正部署应用,而是将渲染后的模板输出到终端,方便你检查。

  3. 在测试环境安装 Chart
    在测试 Kubernetes 集群中,使用 helm install 命令部署你的 Chart:

    helm install my-test-release ./myapp
    

    查看部署状态:

    helm list
    kubectl get pods -l app=myapp
    

🚀 第四步:准备生产部署

将 Chart 用于生产环境,需要考虑更多因素。

  1. 管理不同环境的配置
    不要直接修改默认的 values.yaml 来适应不同环境(如开发、测试、生产)。 推荐的做法是:

    • 保留 values.yaml 作为默认配置。

    • 为不同环境创建各自的 values 文件,例如 values-production.yaml

    • 在部署时,使用 -f 参数指定特定环境的 values 文件:

      helm install -f values-production.yaml my-prod-release ./myapp
      
  2. 打包和版本化管理 Chart

    • 打包 Chart:使用 helm package 命令将 Chart 打包成一个 .tgz 压缩文件,便于分发和存储。

      helm package ./myapp
      
    • 版本控制:将你的 Chart 代码(包括 Chart.yamlvalues.yamltemplates/)纳入版本控制系统(如 Git)。

    • Chart 版本:每次对 Chart 进行重大更改时,记得在 Chart.yaml 中更新 version 字段。遵循语义化版本控制规范。

  3. 考虑使用 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运维小白,我会持续更新文章,欢迎大家关注,互相交流学习。
在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐