前言
Helm 是 Kubernetes 的包管理工具,通过 Chart 可以方便地打包和部署应用。本文将介绍 Spring Boot Helm Chart 的完整方案。
Helm 基础
1. 安装 Helm
# macOS
brew install helm
# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Windows
choco install kubernetes-helm
2. 验证安装
# 查看版本
helm version
# 查看帮助
helm --help
# 添加仓库
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
# 搜索 Chart
helm search repo nginx
helm search hub wordpress
3. 常用命令
# 创建 Chart
helm create demo
# 打包 Chart
helm package ./demo
# 安装 Chart
helm install demo ./demo
# 升级 Chart
helm upgrade demo ./demo
# 卸载 Chart
helm uninstall demo
# 查看状态
helm list
helm status demo
# 回滚
helm rollback demo 1
# 查看历史
helm history demo
Chart 结构
1. 目录结构
demo/
├── Chart.yaml # Chart 元数据
├── values.yaml # 默认配置值
├── values-prod.yaml # 生产环境配置
├── Chart.lock # 依赖锁定文件
├── templates/ # 模板目录
│ ├── NOTES.txt # 安装说明
│ ├── _helpers.tpl # 辅助模板
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── ingress.yaml
│ └── hpa.yaml
├── charts/ # 子 Chart 依赖
└── .helmignore # 忽略文件
2. Chart.yaml
apiVersion: v2
name: demo
description: Spring Boot Demo Application
type: application
# Chart 版本
version: 1.0.0
# 应用版本
appVersion: "1.0.0"
# 关键词
keywords:
- spring-boot
- java
- web
# 维护者
maintainers:
- name: Your Name
email: your-email@example.com
# 依赖
dependencies:
- name: mysql
version: 9.0.0
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled
- name: redis
version: 17.0.0
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
3. values.yaml
# 副本数
replicaCount: 3
# 镜像配置
image:
repository: demo
pullPolicy: IfNotPresent
tag: "1.0.0"
# 镜像拉取密钥
imagePullSecrets: []
# 服务名称
nameOverride: ""
fullnameOverride: ""
# 服务账号
serviceAccount:
create: true
annotations: {}
name: ""
# Pod 注解
podAnnotations: {}
# Pod 安全上下文
podSecurityContext: {}
# 容器安全上下文
securityContext: {}
# 服务配置
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress 配置
ingress:
enabled: false
className: nginx
annotations: {}
hosts:
- host: demo.example.com
paths:
- path: /
pathType: Prefix
tls: []
# 资源限制
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
# 自动扩缩容
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# 健康检查
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
# 环境变量
env:
SPRING_PROFILES_ACTIVE: prod
JAVA_OPTS: "-Xms512m -Xmx512m"
# 配置映射
configMap:
application: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/demo
username: root
redis:
host: redis
port: 6379
# 敏感信息
secret:
enabled: true
data:
DB_PASSWORD: ""
JWT_SECRET: ""
# 子 Chart 配置
mysql:
enabled: true
auth:
rootPassword: "root-password"
database: demo
primary:
persistence:
enabled: true
size: 8Gi
redis:
enabled: true
auth:
enabled: true
password: "redis-password"
master:
persistence:
enabled: true
size: 8Gi
模板语法
1. 基础模板
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "demo.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "demo.selectorLabels" . | nindent 8 }}
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "demo.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
2. 辅助模板
# templates/_helpers.tpl
{{/*
扩展名称
*/}}
{{- define "demo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
创建完整的名称
*/}}
{{- define "demo.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 }}
{{/*
创建 Chart 标签
*/}}
{{- define "demo.labels" -}}
helm.sh/chart: {{ include "demo.chart" . }}
{{ include "demo.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
通用选择器标签
*/}}
{{- define "demo.selectorLabels" -}}
app.kubernetes.io/name: {{ include "demo.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
创建服务账号名称
*/}}
{{- define "demo.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "demo.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
创建 Chart 版本
*/}}
{{- define "demo.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
3. 条件渲染
# templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "demo.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
4. 循环渲染
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "demo.fullname" . }}-config
labels:
{{- include "demo.labels" . | nindent 4 }}
data:
application.yml: |
{{- toYaml .Values.configMap.application | nindent 4 }}
{{- range $key, $value := .Values.extraConfig }}
{{ $key }}: {{ $value | quote }}
{{- end }}
多环境管理
1. 多 values 文件
# values-dev.yaml
replicaCount: 1
image:
tag: "latest"
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
mysql:
primary:
persistence:
size: 2Gi
# values-test.yaml
replicaCount: 2
image:
tag: "1.0.0"
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
# values-prod.yaml
replicaCount: 3
image:
tag: "1.0.0"
pullPolicy: Always
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
ingress:
enabled: true
hosts:
- host: demo.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: demo-tls
hosts:
- demo.example.com
2. 安装命令
# 开发环境
helm install demo ./demo -f values-dev.yaml -n dev
# 测试环境
helm install demo ./demo -f values-test.yaml -n test
# 生产环境
helm install demo ./demo -f values-prod.yaml -n prod
# 覆盖单个值
helm install demo ./demo --set replicaCount=5
3. 环境升级
# 升级 Chart
helm upgrade demo ./demo -f values-prod.yaml -n prod
# 回滚到上一个版本
helm rollback demo -n prod
# 回滚到指定版本
helm rollback demo 2 -n prod
# 查看历史
helm history demo -n prod
私有仓库
1. 创建仓库
# 打包 Chart
helm package ./demo
# 创建仓库索引
helm repo index . --url https://charts.example.com
# 上传到服务器
# 使用 FTP、SCP 或云存储
2. 添加仓库
# 添加仓库
helm repo add myrepo https://charts.example.com
# 添加认证仓库
helm repo add myrepo https://charts.example.com \
--username admin \
--password secret
# 更新仓库
helm repo update
# 搜索 Chart
helm search repo demo
3. Chart Museum
# 启动 Chart Museum
docker run -d --name chartmuseum \
-p 8080:8080 \
-v $(pwd)/charts:/charts \
chartmuseum/chartmuseum:latest \
--storage local \
--storage-local-rootdir /charts
# 上传 Chart
curl --data-binary "@demo-1.0.0.tgz" \
http://localhost:8080/api/charts
最佳实践
1. Chart 规范
# ✅ 推荐
apiVersion: v2
name: demo
version: 1.0.0
appVersion: "1.0.0"
description: 清晰的描述
keywords:
- spring-boot
- java
maintainers:
- name: Team
email: team@example.com
# ❌ 不推荐
apiVersion: v1 # 使用旧版本
version: 1 # 缺少 minor 和 patch
2. 模板规范
# ✅ 推荐
{{- include "demo.labels" . | nindent 4 }}
{{- if .Values.ingress.enabled }}
{{- end }}
{{- range .Values.hosts }}
{{- end }}
# ❌ 不推荐
{{include "demo.labels" .}} # 缺少空格
{{ if .Values.ingress.enabled }} # 缺少 -
3. 值文件规范
# ✅ 推荐
# 分组配置
image:
repository: demo
tag: "1.0.0"
# 清晰的注释
# 副本数
replicaCount: 3
# ❌ 不推荐
# 没有分组
repository: demo
tag: "1.0.0"
replicaCount: 3
4. 测试 Chart
# 语法检查
helm lint ./demo
# 模板渲染测试
helm template demo ./demo
# 空运行
helm install demo ./demo --dry-run --debug
# 单元测试(使用 helm-unittest 插件)
helm unittest ./demo
5. CI/CD 集成
# .github/workflows/helm.yml
name: Helm Chart CI
on:
push:
tags:
- 'chart-v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
- name: Lint Chart
run: helm lint ./demo
- name: Package Chart
run: helm package ./demo
- name: Upload Chart
run: |
curl --data-binary "@demo-*.tgz" \
https://charts.example.com/api/charts
总结
Helm Chart 要点:
- ✅ Chart 结构 - Chart.yaml、values.yaml、templates
- ✅ 模板语法 - 函数、条件、循环
- ✅ 多环境 - values-dev/test/prod.yaml
- ✅ 私有仓库 - Chart Museum、认证
- ✅ 最佳实践 - 规范、测试、CI/CD
Helm 是 Kubernetes 应用部署的标准工具。