[add]: helm-chart-scaffolding

This commit is contained in:
2026-03-28 23:13:00 +08:00
parent f5e906429c
commit 845e5decca
5 changed files with 1560 additions and 0 deletions

View File

@@ -0,0 +1,560 @@
---
名称helm-chart-scaffolding
描述:设计、组织和管理 Helm 图表,用于对 Kubernetes 应用程序进行模板化和打包,同时使用可重复使用的配置。在创建 Helm 图表、打包 Kubernetes 应用程序或实现模板化部署时使用。
---
# Helm 图表模板生成
有关为打包和部署 Kubernetes 应用程序创建、组织和管理 Helm 图表的全面指南。
## 目的
此技能提供了构建可投入生产的 Helm 图表的分步说明,包括图表结构、模板模式、值管理以及验证策略。
## 何时使用此技能
在您需要以下情况时使用此技能:
- 从零开始创建新的 Helm 图表
- 将 Kubernetes 应用程序打包以进行分发
- 使用 Helm 管理多环境部署
- 为可重用的 Kubernetes 清单实现模板化
- 设置 Helm 图表仓库
- 遵循 Helm 最佳实践和约定
## Helm 概述
**Helm** 是 Kubernetes 的包管理器:
- 为可重用性创建 Kubernetes 清单模板
- 管理应用程序的发布和回滚
- 处理图表之间的依赖关系
- 为部署提供版本控制
- 简化跨环境的配置管理
## 分步工作流程
### 1. 初始化图表结构
**创建新图表:**
```bash
helm create my-app
```
**标准图表结构:**
```
my-app/
├── Chart.yaml # 图表元数据
├── values.yaml # 默认配置值
├── charts/ # 图表依赖
├── templates/ # Kubernetes 清单模板
│ ├── NOTES.txt # 安装后说明
│ ├── _helpers.tpl # 模板辅助函数
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ └── tests/
│ └── test-connection.yaml
└── .helmignore # 忽略的文件
```
### 2. 配置 Chart.yaml
**图表元数据定义了包:**
```yaml
apiVersion: v2
name: my-app
description: 一个用于我的应用程序的 Helm 图表
type: application
version: 1.0.0 # 图表版本
appVersion: "2.1.0" # 应用程序版本
# 图表发现关键字
keywords:
- web
- api
- backend
# 维护者信息
maintainers:
- name: yanghuajun336
email: yanghuajun336@example.com
url: https://github.com/yanghuajun336/my-app
# 源码仓库
sources:
- https://github.com/yanghuajun336/my-app
# 主页
home: https://github.com/yanghuajun336
# 图表图标
icon: https://example.com/icon.png
# 依赖
dependencies:
- name: postgresql
version: "12.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
```
**参考:** 请参阅 `assets/Chart.yaml.template` 获取完整示例
### 3. 设计 values.yaml 结构
**按层次组织值:**
```yaml
# 镜像配置
image:
repository: myapp
tag: "1.0.0"
pullPolicy: IfNotPresent
# 副本数量
replicaCount: 3
# Service 配置
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress 配置
ingress:
enabled: false
className: nginx
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
# 资源限制
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# 自动扩缩容
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
# 环境变量
env:
- name: LOG_LEVEL
value: "info"
# ConfigMap 数据
configMap:
data:
APP_MODE: production
# 依赖
postgresql:
enabled: true
auth:
database: myapp
username: myapp
redis:
enabled: false
```
**参考:** 请参阅 `assets/values.yaml.template` 获取完整结构
### 4. 创建模板文件
**使用 Go 模板语法结合 Helm 函数:**
**templates/deployment.yaml**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- toYaml .Values.env | nindent 12 }}
```
### 5. 创建模板辅助函数
**templates/\_helpers.tpl**
```yaml
{{/*
展开图表名称。
*/}}
{{- define "my-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
创建默认的完整限定应用名称。
*/}}
{{- define "my-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
通用标签
*/}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
{{ include "my-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
选择器标签
*/}}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
```
### 6. 管理依赖
**在 Chart.yaml 中添加依赖:**
```yaml
dependencies:
- name: postgresql
version: "12.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
```
**更新依赖:**
```bash
helm dependency update
helm dependency build
```
**覆盖依赖的配置值:**
```yaml
# values.yaml
postgresql:
enabled: true
auth:
database: myapp
username: myapp
password: changeme
primary:
persistence:
enabled: true
size: 10Gi
```
### 7. 测试与验证
**验证命令:**
```bash
# 检查图表语法
helm lint my-app/
# 模拟安装(不实际部署)
helm install my-app ./my-app --dry-run --debug
# 渲染模板
helm template my-app ./my-app
# 使用指定值文件渲染模板
helm template my-app ./my-app -f values-prod.yaml
# 显示计算后的值
helm show values ./my-app
```
**验证脚本:**
```bash
#!/bin/bash
set -e
echo "正在检查图表语法..."
helm lint .
echo "正在测试模板渲染..."
helm template test-release . --dry-run
echo "正在检查必填值..."
helm template test-release . --validate
echo "所有验证均已通过!"
```
**参考:** 请参阅 `scripts/validate-chart.sh`
### 8. 打包与分发
**打包图表:**
```bash
helm package my-app/
# 生成文件my-app-1.0.0.tgz
```
**创建图表仓库:**
```bash
# 生成索引文件
helm repo index .
# 上传到仓库
# AWS S3 示例
aws s3 sync . s3://my-helm-charts/ --exclude "*" --include "*.tgz" --include "index.yaml"
```
**使用图表:**
```bash
helm repo add my-repo https://charts.example.com
helm repo update
helm install my-app my-repo/my-app
```
### 9. 多环境配置
**针对不同环境的值文件:**
```
my-app/
├── values.yaml # 默认配置
├── values-dev.yaml # 开发环境
├── values-staging.yaml # 预发布环境
└── values-prod.yaml # 生产环境
```
**values-prod.yaml**
```yaml
replicaCount: 5
image:
tag: "2.1.0"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
ingress:
enabled: true
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
postgresql:
enabled: true
primary:
persistence:
size: 100Gi
```
**使用环境值文件安装:**
```bash
helm install my-app ./my-app -f values-prod.yaml --namespace production
```
### 10. 实现 Hook 与测试
**安装前 Hook**
```yaml
# templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-db-setup
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: db-setup
image: postgres:15
command: ["psql", "-c", "CREATE DATABASE myapp"]
restartPolicy: Never
```
**测试连接:**
```yaml
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "my-app.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "my-app.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
```
**运行测试:**
```bash
helm test my-app
```
## 常用模式
### 模式 1条件化资源
```yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "my-app.fullname" . }}
spec:
# ...
{{- end }}
```
### 模式 2遍历列表
```yaml
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
```
### 模式 3引入外部文件
```yaml
data:
config.yaml: |
{{- .Files.Get "config/application.yaml" | nindent 4 }}
```
### 模式 4全局值
```yaml
global:
imageRegistry: docker.io
imagePullSecrets:
- name: regcred
# 在模板中使用:
image: {{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}
```
## 最佳实践
1. **使用语义化版本号** 管理图表和应用版本
2. **为所有配置项添加注释** 在 values.yaml 中
3. **使用模板辅助函数** 处理重复逻辑
4. **打包前验证图表** 确保无误
5. **明确固定依赖版本号**
6. **为可选资源使用条件判断**
7. **遵循命名规范**(小写字母,连字符分隔)
8. **在 NOTES.txt 中** 提供使用说明
9. **通过辅助函数** 统一添加标签
10. **在所有环境中** 测试安装流程
## 故障排查
**模板渲染错误:**
```bash
helm template my-app ./my-app --debug
```
**依赖问题:**
```bash
helm dependency update
helm dependency list
```
**安装失败:**
```bash
helm install my-app ./my-app --dry-run --debug
kubectl get events --sort-by='.lastTimestamp'
```
## 相关技能
- `k8s-manifest-generator` - 用于创建基础 Kubernetes 清单
- `gitops-workflow` - 用于自动化 Helm 图表部署

View File

@@ -0,0 +1,42 @@
apiVersion: v2
name: <chart-name>
description: <Chart description>
type: application
version: 0.1.0
appVersion: "1.0.0"
keywords:
- <keyword1>
- <keyword2>
home: https://github.com/<org>/<repo>
sources:
- https://github.com/<org>/<repo>
maintainers:
- name: <Maintainer Name>
email: <maintainer@example.com>
url: https://github.com/<username>
icon: https://example.com/icon.png
kubeVersion: ">=1.24.0"
dependencies:
- name: postgresql
version: "12.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
tags:
- database
- name: redis
version: "17.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
tags:
- cache
annotations:
category: Application
licenses: Apache-2.0

View File

@@ -0,0 +1,185 @@
## 全局配置,供子 chart 共享
global:
imageRegistry: docker.io
imagePullSecrets: []
storageClass: ""
## 镜像相关配置
image:
registry: docker.io
repository: myapp/web
tag: "" # 默认为 .Chart.AppVersion
pullPolicy: IfNotPresent # 镜像拉取策略
## chart 名称覆盖
nameOverride: ""
fullnameOverride: ""
## 副本数量与修订历史
replicaCount: 3 # 副本数
revisionHistoryLimit: 10 # 保留历史版本数
## ServiceAccount 配置
serviceAccount:
create: true # 是否创建 ServiceAccount
annotations: {} # 关联注解
name: "" # 指定名称
## Pod 注解
podAnnotations:
prometheus.io/scrape: "true" # Prometheus 采集
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
## Pod 安全上下文
podSecurityContext:
runAsNonRoot: true # 非 root 用户运行
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
## 容器安全上下文
securityContext:
allowPrivilegeEscalation: false # 禁止权限提升
readOnlyRootFilesystem: true # 只读根文件系统
capabilities:
drop:
- ALL
## Service 配置
service:
type: ClusterIP # 服务类型
port: 80 # 服务端口
targetPort: http # 目标端口
annotations: {} # 服务注解
sessionAffinity: None # 会话亲和性
## Ingress 配置
ingress:
enabled: false # 是否启用 Ingress
className: nginx # Ingress 类名
annotations: {} # Ingress 注解
hosts:
- host: app.example.com # 绑定域名
paths:
- path: / # 路径
pathType: Prefix # 路径类型
tls: [] # TLS 配置
## 资源限制与请求
resources:
limits:
cpu: 500m # 最大 CPU
memory: 512Mi # 最大内存
requests:
cpu: 250m # 最小 CPU
memory: 256Mi # 最小内存
## 存活探针
livenessProbe:
httpGet:
path: /health/live
port: http
initialDelaySeconds: 30 # 首次探测延迟
periodSeconds: 10 # 探测周期
## 就绪探针
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 5 # 首次探测延迟
periodSeconds: 5 # 探测周期
## 自动扩缩容
autoscaling:
enabled: false # 是否启用自动扩缩容
minReplicas: 2 # 最小副本数
maxReplicas: 10 # 最大副本数
targetCPUUtilizationPercentage: 80 # 目标 CPU 利用率
targetMemoryUtilizationPercentage: 80 # 目标内存利用率
## Pod 弹性预算
podDisruptionBudget:
enabled: true # 是否启用 PDB
minAvailable: 1 # 最小可用副本数
## 节点调度相关
nodeSelector: {} # 节点选择器
tolerations: [] # 容忍
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- '{{ include "my-app.name" . }}'
topologyKey: kubernetes.io/hostname
## 环境变量
env: [] # 额外环境变量
# - name: LOG_LEVEL
# value: "info"
## ConfigMap 配置
configMap:
enabled: true # 是否启用 ConfigMap
data: {} # 配置数据
# APP_MODE: production
# DATABASE_HOST: postgres.example.com
## Secret 配置(生产建议用外部 secret 管理)
secrets:
enabled: false # 是否启用 Secret
data: {}
## 持久化存储
persistence:
enabled: false # 是否启用持久化
storageClass: "" # 存储类
accessMode: ReadWriteOnce # 访问模式
size: 10Gi # 存储大小
annotations: {} # 存储注解
## PostgreSQL 依赖
postgresql:
enabled: false # 是否启用 PostgreSQL
auth:
database: myapp # 数据库名
username: myapp # 用户名
password: changeme # 密码
primary:
persistence:
enabled: true # 是否持久化
size: 10Gi # 存储大小
## Redis 依赖
redis:
enabled: false # 是否启用 Redis
auth:
enabled: false # 是否启用认证
master:
persistence:
enabled: false # 是否持久化
## Prometheus ServiceMonitor
serviceMonitor:
enabled: false # 是否启用 ServiceMonitor
interval: 30s # 采集间隔
scrapeTimeout: 10s # 超时时间
labels: {} # 额外标签
## 网络策略
networkPolicy:
enabled: false # 是否启用网络策略
policyTypes:
- Ingress
- Egress
ingress: [] # 入站规则
egress: [] # 出站规则

View File

@@ -0,0 +1,509 @@
# Helm Chart 结构参考
Helm Chart 组织结构、文件规范与最佳实践完整指南。
## 标准 Chart 目录结构
```
my-app/
├── Chart.yaml # Chart 元数据(必需)
├── Chart.lock # 依赖锁定文件(自动生成)
├── values.yaml # 默认配置值(必需)
├── values.schema.json # values 验证 JSON Schema
├── .helmignore # 打包时忽略的文件模式
├── README.md # Chart 文档
├── LICENSE # Chart 许可证
├── charts/ # Chart 依赖(打包内置)
│ └── postgresql-12.0.0.tgz
├── crds/ # 自定义资源定义CRD
│ └── my-crd.yaml
├── templates/ # Kubernetes 清单模板(必需)
│ ├── NOTES.txt # 安装后说明
│ ├── _helpers.tpl # 模板辅助函数
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── networkpolicy.yaml
│ └── tests/
│ └── test-connection.yaml
└── files/ # 附加文件
└── config/
└── app.conf
```
## Chart.yaml 规范
### API 版本 v2Helm 3+
```yaml
apiVersion: v2 # 必需API 版本
name: my-application # 必需Chart 名称
version: 1.2.3 # 必需Chart 版本SemVer
appVersion: "2.5.0" # 应用版本
description: A Helm chart for my application # 必需
type: application # Chart 类型application 或 library
keywords: # 搜索关键词
- web
- api
- backend
home: https://example.com # 项目主页
sources: # 源码地址
- https://github.com/example/my-app
maintainers: # 维护者列表
- name: John Doe
email: john@example.com
url: https://github.com/johndoe
icon: https://example.com/icon.png # Chart 图标 URL
kubeVersion: ">=1.24.0" # 兼容的 Kubernetes 版本
deprecated: false # 标记为已弃用
annotations: # 自定义注解
example.com/release-notes: https://example.com/releases/v1.2.3
dependencies: # Chart 依赖
- name: postgresql
version: "12.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
tags:
- database
import-values:
- child: database
parent: database
alias: db
```
## Chart 类型
### Application Chart应用 Chart
```yaml
type: application
```
- 标准 Kubernetes 应用
- 可安装和管理
- 包含 K8s 资源模板
### Library Chart库 Chart
```yaml
type: library
```
- 共享模板辅助函数
- 不可直接安装
- 作为其他 Chart 的依赖使用
- 无 templates/ 目录
## Values 文件组织
### values.yaml默认值
```yaml
# 全局值(与子 Chart 共享)
global:
imageRegistry: docker.io
imagePullSecrets: []
# 镜像配置
image:
registry: docker.io
repository: myapp/web
tag: "" # 默认使用 .Chart.AppVersion
pullPolicy: IfNotPresent
# Deployment 配置
replicaCount: 1
revisionHistoryLimit: 10
# Pod 配置
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# 容器安全
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Service
service:
type: ClusterIP
port: 80
targetPort: http
annotations: {}
# 资源限制
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
# 自动伸缩
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# 节点选择
nodeSelector: {}
tolerations: []
affinity: {}
# 监控
serviceMonitor:
enabled: false
interval: 30s
```
### values.schema.json值校验
```json
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1
},
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": {
"type": "string"
},
"tag": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
}
}
},
"required": ["image"]
}
```
## 模板文件
### 模板命名规范
- **小写加连字符**`deployment.yaml``service-account.yaml`
- **局部模板**:以下划线开头,如 `_helpers.tpl`
- **测试文件**:放在 `templates/tests/` 目录下
- **CRD**:放在 `crds/` 目录下(不做模板化)
### 常用模板
#### \_helpers.tpl
```yaml
{{/*
标准命名辅助函数
*/}}
{{- define "my-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "my-app.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- define "my-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
通用标签
*/}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
{{ include "my-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/*
镜像名称辅助函数
*/}}
{{- define "my-app.image" -}}
{{- $registry := .Values.global.imageRegistry | default .Values.image.registry -}}
{{- $repository := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- end -}}
```
#### NOTES.txt
```
感谢安装 {{ .Chart.Name }}。
您的 Release 名称为 {{ .Release.Name }}。
查看 Release 详情,请运行:
$ helm status {{ .Release.Name }}
$ helm get all {{ .Release.Name }}
{{- if .Values.ingress.enabled }}
应用访问地址:
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}{{ .path }}
{{- end }}
{{- else }}
通过以下命令获取应用访问地址:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "my-app.name" . }}" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward $POD_NAME 8080:80
echo "访问 http://127.0.0.1:8080"
{{- end }}
```
## 依赖管理
### 声明依赖
```yaml
# Chart.yaml
dependencies:
- name: postgresql
version: "12.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled # 通过 values 启用/禁用
tags: # 依赖分组
- database
import-values: # 从子 Chart 导入 values
- child: database
parent: database
alias: db # 通过 .Values.db 引用
```
### 管理依赖
```bash
# 更新依赖
helm dependency update
# 列出依赖
helm dependency list
# 构建依赖
helm dependency build
```
### Chart.lock
`helm dependency update` 自动生成:
```yaml
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 12.0.0
digest: sha256:abcd1234...
generated: "2024-01-01T00:00:00Z" # 生成时间
```
## .helmignore
从 Chart 包中排除的文件:
```
# 开发文件
.git/
.gitignore
*.md
docs/
# 构建产物
*.swp
*.bak
*.tmp
*.orig
# CI/CD
.travis.yml
.gitlab-ci.yml
Jenkinsfile
# 测试
test/
*.test
# IDE
.vscode/
.idea/
*.iml
```
## 自定义资源定义CRDs
将 CRD 放在 `crds/` 目录下:
```
crds/
├── my-app-crd.yaml
└── another-crd.yaml
```
**CRD 重要说明:**
- CRD 在所有模板之前安装
- CRD 不支持模板化(不可使用 `{{ }}` 语法)
- CRD 不会随 Chart 升级或删除
- 使用 `helm install --skip-crds` 可跳过安装
## Chart 版本管理
### 语义化版本
- **Chart 版本**Chart 有变更时递增
- MAJOR不兼容的破坏性变更
- MINOR新功能向后兼容
- PATCHBug 修复
- **App 版本**:所部署应用的版本号
- 可为任意字符串
- 不强制遵循 SemVer
```yaml
version: 2.3.1 # Chart 版本
appVersion: "1.5.0" # 应用版本
```
## Chart 测试
### 测试文件
```yaml
# templates/tests/test-connection.yaml连接测试
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "my-app.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "my-app.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
```
### 运行测试
```bash
helm test my-release
helm test my-release --logs
```
## Hooks钩子
Helm Hooks 允许在特定生命周期节点介入执行:
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-migration
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
```
### Hook 类型
- `pre-install`:模板渲染前执行
- `post-install`:所有资源加载完成后执行
- `pre-delete`:任何资源删除前执行
- `post-delete`:所有资源删除后执行
- `pre-upgrade`:升级前执行
- `post-upgrade`:升级后执行
- `pre-rollback`:回滚前执行
- `post-rollback`:回滚后执行
- `test`:配合 `helm test` 执行
### Hook 权重
控制 Hook 执行顺序(-5 到 5数值越小越先执行
### Hook 删除策略
- `before-hook-creation`:创建新 Hook 前删除上一个
- `hook-succeeded`:执行成功后删除
- `hook-failed`:执行失败后删除
## 最佳实践
1. **使用辅助函数**处理重复的模板逻辑
2. **字符串加引号**`{{ .Values.name | quote }}`
3. **使用 values.schema.json** 校验 values
4. **在 values.yaml 中注释所有字段**
5. **使用语义化版本**管理 Chart 版本
6. **精确锁定依赖版本**
7. **包含 NOTES.txt** 提供使用说明
8. **为关键功能编写测试**
9. **使用 Hooks** 处理数据库迁移
10. **保持 Chart 职责单一**——每个 Chart 只负责一个应用
## Chart 仓库结构
```
helm-charts/
├── index.yaml
├── my-app-1.0.0.tgz
├── my-app-1.1.0.tgz
├── my-app-1.2.0.tgz
└── another-chart-2.0.0.tgz
```
### 创建仓库索引
```bash
helm repo index . --url https://charts.example.com
```
## 相关资源
- [Helm 官方文档](https://helm.sh/docs/)
- [Chart 模板指南](https://helm.sh/docs/chart_template_guide/)
- [最佳实践](https://helm.sh/docs/chart_best_practices/)

View File

@@ -0,0 +1,264 @@
#!/bin/bash
set -e
CHART_DIR="${1:-.}"
RELEASE_NAME="test-release"
echo "═══════════════════════════════════════════════════════"
echo " Helm Chart Validation"
echo "═══════════════════════════════════════════════════════"
echo "═══════════════════════════════════════════════════════"
echo " Helm Chart 校验"
echo "═══════════════════════════════════════════════════════"
echo ""
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# 颜色
success() {
echo -e "${GREEN}${NC} $1"
}
warning() {
echo -e "${YELLOW}${NC} $1"
}
error() {
echo -e "${RED}${NC} $1"
}
# Check if Helm is installed
if ! command -v helm &> /dev/null; then
error "未检测到 Helm请先安装 Helm"
exit 1
fi
echo "📦 Chart directory: $CHART_DIR"
echo ""
echo "📦 Chart 目录: $CHART_DIR"
# 1. Check chart structure
echo "1⃣ Checking chart structure..."
echo "1⃣ 检查 Chart 结构..."
if [ ! -f "$CHART_DIR/Chart.yaml" ]; then
error "未找到 Chart.yaml"
exit 1
fi
success "Chart.yaml exists"
if [ ! -f "$CHART_DIR/values.yaml" ]; then
error "未找到 values.yaml"
exit 1
fi
success "values.yaml exists"
if [ ! -d "$CHART_DIR/templates" ]; then
error "未找到 templates/ 目录"
exit 1
fi
success "templates/ directory exists"
echo ""
# 2. Lint the chart
echo "2⃣ Linting chart..."
echo "2⃣ Lint 检查..."
if helm lint "$CHART_DIR"; then
success "Lint 检查通过"
else
error "Lint 检查未通过"
exit 1
fi
echo ""
# 3. Check Chart.yaml
echo "3⃣ Validating Chart.yaml..."
echo "3⃣ 校验 Chart.yaml..."
CHART_NAME=$(grep "^name:" "$CHART_DIR/Chart.yaml" | awk '{print $2}')
CHART_VERSION=$(grep "^version:" "$CHART_DIR/Chart.yaml" | awk '{print $2}')
APP_VERSION=$(grep "^appVersion:" "$CHART_DIR/Chart.yaml" | awk '{print $2}' | tr -d '"')
if [ -z "$CHART_NAME" ]; then
error "未找到 Chart 名称"
exit 1
fi
success "Chart name: $CHART_NAME"
if [ -z "$CHART_VERSION" ]; then
error "未找到 Chart 版本"
exit 1
fi
success "Chart version: $CHART_VERSION"
if [ -z "$APP_VERSION" ]; then
warning "未指定 App 版本"
else
success "App 版本: $APP_VERSION"
fi
echo ""
# 4. Test template rendering
echo "4⃣ Testing template rendering..."
echo "4⃣ 测试模板渲染..."
if helm template "$RELEASE_NAME" "$CHART_DIR" > /dev/null 2>&1; then
success "模板渲染成功"
else
error "模板渲染失败"
helm template "$RELEASE_NAME" "$CHART_DIR"
exit 1
fi
echo ""
# 5. Dry-run installation
echo "5⃣ Testing dry-run installation..."
echo "5⃣ Dry-run 安装测试..."
if helm install "$RELEASE_NAME" "$CHART_DIR" --dry-run --debug > /dev/null 2>&1; then
success "Dry-run 安装成功"
else
error "Dry-run 安装失败"
exit 1
fi
echo ""
# 6. Check for required Kubernetes resources
echo "6⃣ Checking generated resources..."
echo "6⃣ 检查生成的资源..."
MANIFESTS=$(helm template "$RELEASE_NAME" "$CHART_DIR")
if echo "$MANIFESTS" | grep -q "kind: Deployment"; then
success "检测到 Deployment"
else
warning "未检测到 Deployment"
fi
if echo "$MANIFESTS" | grep -q "kind: Service"; then
success "检测到 Service"
else
warning "未检测到 Service"
fi
if echo "$MANIFESTS" | grep -q "kind: ServiceAccount"; then
success "检测到 ServiceAccount"
else
warning "未检测到 ServiceAccount"
fi
echo ""
# 7. Check for security best practices
echo "7⃣ Checking security best practices..."
echo "7⃣ 检查安全最佳实践..."
if echo "$MANIFESTS" | grep -q "runAsNonRoot: true"; then
success "已设置非 root 用户运行"
else
warning "未显式设置非 root 用户运行"
fi
if echo "$MANIFESTS" | grep -q "readOnlyRootFilesystem: true"; then
success "已启用只读根文件系统"
else
warning "未启用只读根文件系统"
fi
if echo "$MANIFESTS" | grep -q "allowPrivilegeEscalation: false"; then
success "已禁用权限提升"
else
warning "未显式禁用权限提升"
fi
echo ""
# 8. Check for resource limits
echo "8⃣ Checking resource configuration..."
echo "8⃣ 检查资源配置..."
if echo "$MANIFESTS" | grep -q "resources:"; then
if echo "$MANIFESTS" | grep -q "limits:"; then
success "已定义资源 limits"
else
warning "未定义资源 limits"
fi
if echo "$MANIFESTS" | grep -q "requests:"; then
success "已定义资源 requests"
else
warning "未定义资源 requests"
fi
else
warning "未定义资源配置"
fi
echo ""
# 9. Check for health probes
echo "9⃣ Checking health probes..."
echo "9⃣ 检查健康探针..."
if echo "$MANIFESTS" | grep -q "livenessProbe:"; then
success "已配置 livenessProbe"
else
warning "未配置 livenessProbe"
fi
if echo "$MANIFESTS" | grep -q "readinessProbe:"; then
success "已配置 readinessProbe"
else
warning "未配置 readinessProbe"
fi
echo ""
# 10. Check dependencies
if [ -f "$CHART_DIR/Chart.yaml" ] && grep -q "^dependencies:" "$CHART_DIR/Chart.yaml"; then
echo "🔟 检查依赖..."
if helm dependency list "$CHART_DIR" > /dev/null 2>&1; then
success "依赖项有效"
if [ -f "$CHART_DIR/Chart.lock" ]; then
success "Chart.lock 文件存在"
else
warning "缺少 Chart.lock 文件(请运行 'helm dependency update'"
fi
else
error "依赖检查失败"
fi
echo ""
fi
# 11. Check for values schema
if [ -f "$CHART_DIR/values.schema.json" ]; then
echo "1⃣1⃣ 校验 values.schema.json..."
success "values.schema.json present"
# Validate schema if jq is available
if command -v jq &> /dev/null; then
if jq empty "$CHART_DIR/values.schema.json" 2>/dev/null; then
success "values.schema.json 是有效 JSON"
else
error "values.schema.json 不是有效 JSON"
exit 1
fi
fi
echo ""
fi
# Summary
echo "═══════════════════════════════════════════════════════"
echo " Validation Complete!"
echo " 校验完成!"
echo "═══════════════════════════════════════════════════════"
echo ""
echo "Chart: $CHART_NAME"
echo "Version: $CHART_VERSION"
echo "版本: $CHART_VERSION"
if [ -n "$APP_VERSION" ]; then
echo "App 版本: $APP_VERSION"
fi
echo ""
success "All validations passed!"
success "所有校验均通过!"
echo ""
echo "Next steps:"
echo " • helm package $CHART_DIR"
echo " • helm package $CHART_DIR"
echo " • helm install my-release $CHART_DIR"
echo " • helm test my-release"
echo " • helm install my-release $CHART_DIR"
echo " • helm test my-release"
echo ""