Chapter 02

Chart 基础结构

使用 helm create 生成脚手架,深入解析 Chart.yaml、values.yaml、templates/ 目录中每个文件的作用与最佳实践。

helm create:生成 Chart 脚手架

# 创建名为 myapp 的 Chart
helm create myapp

# 查看生成的目录结构
tree myapp/
Chart 目录结构

myapp/
├── Chart.yaml          # Chart 元数据(名称、版本、依赖)
├── values.yaml         # 默认配置值(可被覆盖)
├── charts/             # 子 Chart(依赖)存放目录
├── .helmignore         # 类似 .gitignore,打包时排除的文件
└── templates/          # K8s YAML 模板文件
    ├── deployment.yaml # Deployment 模板
    ├── service.yaml    # Service 模板
    ├── ingress.yaml    # Ingress 模板
    ├── hpa.yaml        # HorizontalPodAutoscaler 模板
    ├── serviceaccount.yaml
    ├── _helpers.tpl    # 命名模板定义(下划线开头不渲染)
    ├── NOTES.txt       # 安装完成后显示的提示信息
    └── tests/
        └── test-connection.yaml  # helm test 用的 Pod

Chart.yaml:Chart 元数据

Chart.yaml 是 Chart 的"身份证",包含名称、版本、描述和依赖声明。

apiVersion: v2                   # Helm 3 使用 v2,Helm 2 使用 v1
name: myapp                        # Chart 名称(必须与目录名一致)
version: 1.2.0                    # Chart 版本,遵循 SemVer 2.0
appVersion: "2.5.1"              # 应用程序版本(字符串,建议加引号)
description: A Helm chart for my application
type: application                 # application(可安装)或 library(模板库)
keywords:
  - myapp
  - web
home: https://github.com/example/myapp
maintainers:
  - name: Alice
    email: alice@example.com
dependencies:                     # 声明依赖的子 Chart
  - name: postgresql
    version: ">=13.0.0"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled  # 条件启用
version vs appVersion
version 是 Chart 本身的版本(Chart 文件结构、模板的变化),遵循 SemVer。appVersion 是被打包的应用程序版本(如 nginx:1.25.3 中的 1.25.3),是字符串,仅供参考展示用。两者独立演进。
type: library
库类型的 Chart 只包含命名模板(_helpers.tpl),不能直接安装,只能被其他 Chart 依赖引用。用于在多个 Chart 间共享通用模板片段。

values.yaml:默认配置值

values.yaml 定义 Chart 的所有可配置参数及其默认值。这是 Helm 多环境配置能力的核心。

# values.yaml 示例(helm create 生成的精简版)

replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: ""                       # 空字符串表示使用 appVersion

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false               # 通过 enabled 控制资源是否创建
  className: ""
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 50m
    memory: 64Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

nodeSelector: {}
tolerations: []
affinity: {}

templates/ 目录:核心文件解析

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}        # 引用命名模板
  labels:
    {{- include "myapp.labels" . | nindent 4 }} # 标准标签
spec:
  {{- if not .Values.autoscaling.enabled }}    # 条件:HPA 未启用才设置 replicas
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          ports:
            - containerPort: {{ .Values.service.port }}

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "myapp.selectorLabels" . | nindent 4 }}

ingress.yaml(条件创建)

{{- if .Values.ingress.enabled - }}  # ingress.enabled=true 才渲染此文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "myapp.fullname" . }}
spec:
  {{- if .Values.ingress.className }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}  # 遍历 hosts 数组
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: {{ .pathType }}
          {{- end }}
    {{- end }}
{{- end }}

NOTES.txt:安装后提示

安装 Chart 后,Helm 会渲染并打印 NOTES.txt 的内容,通常用于告诉用户如何访问应用。

1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ range .Values.ingress.hosts }}{{ .host }}{{ end }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} \
    -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "myapp.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} \
    -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
  kubectl get svc --namespace {{ .Release.Namespace }} {{ include "myapp.fullname" . }}
{{- else if contains "ClusterIP" .Values.service.type }}
  kubectl port-forward --namespace {{ .Release.Namespace }} \
    svc/{{ include "myapp.fullname" . }} 8080:{{ .Values.service.port }}
  echo "Visit http://127.0.0.1:8080"
{{- end }}

.helmignore:打包排除规则

# 打包 Chart 时排除这些文件(helm package 命令)
.DS_Store
.git/
.gitignore
.helmignore
*.md
tests/
# 开发用的本地 values 文件
values-local.yaml

实战:解析 nginx Chart 结构

# 下载 Chart 到本地(不安装)
helm pull bitnami/nginx --untar

# 查看 Chart 元数据
cat nginx/Chart.yaml

# 查看所有 values(约 600 行配置)
helm show values bitnami/nginx | wc -l

# 预览渲染结果(不实际安装)
helm template my-nginx bitnami/nginx \
  --set replicaCount=2 \
  --set service.type=LoadBalancer

# 验证模板语法
helm lint nginx/
helm template 是调试神器

helm template 会在本地渲染所有模板并输出最终的 YAML,不需要连接 K8s 集群。在提交之前用它验证渲染结果是否符合预期,这是开发 Chart 的必备习惯。

本章小结

Chart 由 Chart.yaml(元数据)、values.yaml(默认配置)和 templates/(K8s YAML 模板)三部分构成。helm create 快速生成标准脚手架,包含 Deployment、Service、Ingress、HPA 等常用模板。templates/ 中以下划线开头的文件(如 _helpers.tpl)不会渲染为 K8s 资源,NOTES.txt 用于安装后用户提示。下一章深入 Go 模板语法。