diff --git a/skills/yanghuajun336/helm-chart-scaffolding/SKILL.md b/skills/yanghuajun336/helm-chart-scaffolding/SKILL.md new file mode 100644 index 0000000..03e0ffd --- /dev/null +++ b/skills/yanghuajun336/helm-chart-scaffolding/SKILL.md @@ -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 图表部署 diff --git a/skills/yanghuajun336/helm-chart-scaffolding/assets/Chart.yaml.template b/skills/yanghuajun336/helm-chart-scaffolding/assets/Chart.yaml.template new file mode 100644 index 0000000..74dfe6e --- /dev/null +++ b/skills/yanghuajun336/helm-chart-scaffolding/assets/Chart.yaml.template @@ -0,0 +1,42 @@ +apiVersion: v2 +name: +description: +type: application +version: 0.1.0 +appVersion: "1.0.0" + +keywords: + - + - + +home: https://github.com// + +sources: + - https://github.com// + +maintainers: + - name: + email: + url: https://github.com/ + +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 diff --git a/skills/yanghuajun336/helm-chart-scaffolding/assets/values.yaml.template b/skills/yanghuajun336/helm-chart-scaffolding/assets/values.yaml.template new file mode 100644 index 0000000..b76b3ad --- /dev/null +++ b/skills/yanghuajun336/helm-chart-scaffolding/assets/values.yaml.template @@ -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: [] # 出站规则 diff --git a/skills/yanghuajun336/helm-chart-scaffolding/references/chart-structure.md b/skills/yanghuajun336/helm-chart-scaffolding/references/chart-structure.md new file mode 100644 index 0000000..7e8b928 --- /dev/null +++ b/skills/yanghuajun336/helm-chart-scaffolding/references/chart-structure.md @@ -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 版本 v2(Helm 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:新功能,向后兼容 + - PATCH:Bug 修复 + +- **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/) diff --git a/skills/yanghuajun336/helm-chart-scaffolding/scripts/validate-chart.sh b/skills/yanghuajun336/helm-chart-scaffolding/scripts/validate-chart.sh new file mode 100755 index 0000000..857eab5 --- /dev/null +++ b/skills/yanghuajun336/helm-chart-scaffolding/scripts/validate-chart.sh @@ -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 ""