Helm 包管理
概述
Helm 是 Kubernetes 的包管理工具,类似于 Linux 系统中的 apt/yum 或 Node.js 的 npm。Helm 使用称为 Chart 的打包格式来定义、安装和升级复杂的 Kubernetes 应用程序,通过模板化能力实现一份配置多环境部署。
一、Helm 核心概念
1.1 Chart(图表包)
Chart 是 Helm 的打包格式,是一个描述 Kubernetes 资源的文件和目录集合。一个 Chart 可以包含 Deployment、Service、ConfigMap 等 Kubernetes 资源定义,并支持通过模板参数实现定制化。
Chart 的命名规范遵循语义化版本(SemVer 2.0),格式为 v<major>.<minor>.<patch>。
1.2 Repository(仓库)
Chart 仓库是一个存放 Chart 包的 HTTP/HTTPS 服务器,通过 index.yaml 索引文件管理所有 Chart 的版本信息。Helm 默认提供官方 Stable 仓库,同时也支持 Bitnami、Harbor 等第三方仓库以及 OCI 兼容的容器镜像仓库。
1.3 Release(发布实例)
Release 是 Chart 在 Kubernetes 集群中运行的一个实例。同一个 Chart 可以在同一集群中部署多次,每次部署生成独立的 Release。例如,redis Chart 可以部署为 redis-dev 和 redis-prod 两个 Release。
1.4 Values(配置值)
Values 是 Chart 的配置参数集合,允许用户在部署时通过 YAML 文件或命令行参数覆盖 Chart 中的默认值,实现环境差异化配置。
1.5 架构说明
Helm 采用客户端-服务端架构:
- Helm CLI(客户端):负责 Chart 的本地开发、打包、搜索以及与 Tiller(Helm 2)或直接与 Kubernetes API(Helm 3)交互。
- Tiller(Helm 2,已弃用):服务端组件,运行在集群内,接收客户端请求并执行安装/升级等操作。
- Helm 3 无 Tiller:Helm 3 移除了 Tiller,直接在客户端通过 Kubernetes API 完成所有操作,使用 Kubernetes RBAC 进行权限控制,安全性更高。
# Helm 版本确认
helm version
# 版本迁移说明
# Helm 2 -> Helm 3 主要变化:
# - 移除 Tiller,安全性提升
# - 三路策略合并(Three-way Strategic Merge Patch)
# - 新增 OCI 仓库支持
# - JSON Schema 校验支持
# - Chart 仓库不再需要 Helm Serve二、Chart 目录结构
一个标准 Chart 项目的目录结构如下:
my-chart/
├── Chart.yaml # Chart 元数据定义
├── values.yaml # 默认配置值
├── values.schema.json # 可选的 JSON Schema 校验文件
├── charts/ # 子 Chart 依赖目录
├── templates/ # Kubernetes 资源模板目录
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── _helpers.tpl # 通用模板辅助函数
│ ├── NOTES.txt # 安装后提示信息模板
│ └── tests/ # Chart 测试模板
│ └── test-connection.yaml
└── crds/ # CRD 定义目录(不参与模板渲染)2.1 Chart.yaml 文件
Chart.yaml 是 Chart 的元数据文件,定义了 Chart 的名称、版本、描述等信息。
apiVersion: v2 # Helm 3 使用 v2
name: my-app # Chart 名称
description: A sample Helm chart for Spring Boot application
type: application # 类型:application 或 library
version: 0.1.0 # Chart 版本号
appVersion: "1.16.0" # 关联的应用版本号
kubeVersion: ">=1.19.0" # 兼容的 Kubernetes 版本约束
keywords:
- spring-boot
- microservice
home: https://example.com
sources:
- https://github.com/example/my-app
maintainers:
- name: Developer
email: dev@example.com
url: https://example.com
dependencies:
- name: mysql
version: ">=9.0.0"
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled # 条件依赖,根据 values 控制是否安装
alias: database # 别名,避免多依赖冲突
- name: redis
version: "~17.0.0"
repository: "@bitnami" # 使用仓库别名
tags:
- cache
import-values:
- child: defaults
parent: redis-defaults
deprecated: false
annotations:
category: Infrastructure2.2 values.yaml 文件
values.yaml 是 Chart 的默认配置值文件,用户在安装时可以覆盖其中的任何参数。
# 应用副本数
replicaCount: 1
# 镜像配置
image:
repository: nginx
pullPolicy: IfNotPresent
# 镜像标签,通常被 CI/CD 覆盖
tag: ""
# 镜像拉取密钥
imagePullSecrets:
- name: registry-credentials
# 应用名称覆盖
nameOverride: ""
# 完整名称覆盖
fullnameOverride: ""
# 服务账号
serviceAccount:
create: true
automount: true
annotations: {}
name: ""
# Pod 注解
podAnnotations: {}
# Pod 标签
podLabels: {}
# Pod 安全上下文
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
# 容器安全上下文
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
# 服务配置
service:
type: ClusterIP
port: 80
targetPort: 8080
protocol: TCP
# 入站流量配置
ingress:
enabled: false
className: "nginx"
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
# 资源限制
resources:
limits:
cpu: "1"
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
# 自动扩缩容
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# 探针配置
probes:
liveness:
enabled: true
path: /actuator/health
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readiness:
enabled: true
path: /actuator/health/readiness
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
startup:
enabled: false
path: /actuator/health
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
# 环境变量
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
# 环境变量来源(Secret/ConfigMap)
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
# 外部 Secret 注入
extraEnvVarsSecret: ""
# 持久化存储
persistence:
enabled: false
storageClass: ""
accessMode: ReadWriteOnce
size: 8Gi
mountPath: /data
existingClaim: ""
# 节点选择器
nodeSelector: {}
# 容忍度
tolerations: []
# 亲和性
affinity: {}
# 挂载卷配置
extraVolumes: []
extraVolumeMounts: []
# 配置项生命周期
lifecycle: {}2.3 values.schema.json 文件
Helm 3 支持使用 JSON Schema 对 values 进行校验,确保用户提供的值类型和结构正确。
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["replicaCount", "image"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 0,
"description": "Number of replicas"
},
"image": {
"type": "object",
"required": ["repository", "tag"],
"properties": {
"repository": {
"type": "string"
},
"tag": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "Never", "IfNotPresent"]
}
}
},
"service": {
"type": "object",
"properties": {
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
}
}
},
"resources": {
"type": "object",
"properties": {
"requests": {
"type": "object",
"properties": {
"cpu": { "type": "string", "pattern": "^[0-9]+m?$" },
"memory": { "type": "string", "pattern": "^[0-9]+(Mi|Gi|Ki)$" }
}
}
}
}
}
}2.4 _helpers.tpl 文件
_helpers.tpl 文件用于定义通用的模板辅助函数,其文件名以下划线开头,表示该文件不会产生 Kubernetes 资源。
{{- /*
Generate basic labels for the application.
Usage:
{{ include "my-app.labels" . }}
*/}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
{{ include "my-app.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{- /*
Selector labels for identifying Pods.
Usage:
{{ include "my-app.selectorLabels" . }}
*/}}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- /*
Create the name of the application.
Usage:
{{ include "my-app.name" . }}
*/}}
{{- define "my-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- /*
Create a fully qualified app name.
Usage:
{{ include "my-app.fullname" . }}
*/}}
{{- 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 }}
{{- /*
Create the chart name and version.
Usage:
{{ include "my-app.chart" . }}
*/}}
{{- define "my-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- /*
Generate image repository string.
Usage:
{{ include "my-app.image" . }}
*/}}
{{- define "my-app.image" -}}
{{- $registry := .Values.image.registry | default "docker.io" -}}
{{- $repository := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default (printf "v%s" .Chart.AppVersion) -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- end }}
{{- /*
Generate image pull secret names.
Usage:
{{ include "my-app.imagePullSecrets" . }}
*/}}
{{- define "my-app.imagePullSecrets" -}}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{- range .Values.imagePullSecrets }}
- name: {{ .name }}
{{- end }}
{{- end }}
{{- end }}
{{- /*
Return the appropriate apiVersion for ingress.
Usage:
{{ include "my-app.ingress.apiVersion" . }}
*/}}
{{- define "my-app.ingress.apiVersion" -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.Version -}}
networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.Version -}}
networking.k8s.io/v1beta1
{{- else -}}
extensions/v1beta1
{{- end }}
{{- end }}2.5 NOTES.txt 文件
NOTES.txt 在 Chart 安装/升级成功后输出到控制台,为用户提供使用指导。
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{ .Release.Name }}
$ helm get all {{ .Release.Name }}
Application URL:
{{- if .Values.ingress.enabled }}
http{{ if .Values.ingress.tls }}s{{ end }}://{{ (index .Values.ingress.hosts 0).host }}{{ (index (index .Values.ingress.hosts 0).paths 0).path }}
{{- else if eq .Values.service.type "ClusterIP" }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "my-app.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- else if eq .Values.service.type "NodePort" }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-app.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 eq .Values.service.type "LoadBalancer" }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running:
kubectl get svc --namespace {{ .Release.Namespace }} -w {{ include "my-app.fullname" . }}
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "my-app.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}" }}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- end }}三、模板语法
Helm 使用 Go 模板引擎(text/template)对 Kubernetes 资源进行参数化渲染,并扩展了 sprig 函数库提供了丰富的字符串、数学、日期等操作函数。
3.1 内置对象
Helm 模板中可访问以下内置对象:
| 对象 | 描述 | 常用字段 |
|---|---|---|
Values | 从 values.yaml 和用户传入的值合并后的配置对象 | .Values.replicaCount, .Values.image.repository |
Release | 当前 Release 的元数据 | .Release.Name, .Release.Namespace, .Release.Service, .Release.Revision, .Release.IsUpgrade, .Release.IsInstall |
Chart | Chart.yaml 中的元数据 | .Chart.Name, .Chart.Version, .Chart.AppVersion, .Chart.Description |
Files | Chart 中非模板文件的访问接口 | .Files.Get "config/default.conf", .Files.GetBytes "key.pem" |
Capabilities | Kubernetes 集群的能力信息 | .Capabilities.KubeVersion.Version, .Capabilities.APIVersions.Has "apps/v1/Deployment" |
Template | 当前正在执行的模板信息 | .Template.Name, .Template.BasePath |
Subcharts | 子 Chart 集合 | 通过 dependencies 引入的子 Chart |
模板访问示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}3.2 流程控制
3.2.1 if/else 条件判断
条件判断用于根据值的有无或布尔状态决定是否渲染某段内容。
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "my-app.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}条件判断规则:
| 值 | 布尔结果 |
|---|---|
false, 0, ""(空字符串), nil | false |
| 非空 map、slice、string、非零数字 | true |
3.2.2 with 作用域切换
with 将当前作用域 . 切换为指定对象,在此范围内可以直接访问该对象的字段。
{{- with .Values.service }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-app.fullname" $ }}
labels:
{{- include "my-app.labels" $ | nindent 4 }}
spec:
type: {{ .type | default "ClusterIP" }}
ports:
- port: {{ .port }}
targetPort: {{ .targetPort | default .port }}
protocol: {{ .protocol | default "TCP" }}
name: http
selector:
{{- include "my-app.selectorLabels" $ | nindent 4 }}
{{- end }}注意:在 with 块内,. 指向了 .Values.service,要访问全局对象需要使用 $(根上下文)。
3.2.3 range 循环
range 用于遍历数组或 map 结构的数据。
# 遍历环境变量数组
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "my-app.fullname" . }}
data:
{{- range $key, $value := .Values.configMapData }}
{{ $key }}: {{ $value | quote }}
{{- end }}# 遍历 Ingress 主机规则
spec:
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType | default "Prefix" }}
backend:
service:
name: {{ include "my-app.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}range 常用模式:
# 遍历简单数组
{{- range .Values.extraPorts }}
- name: {{ .name }}
containerPort: {{ .containerPort }}
{{- end }}
# 遍历 map
{{- range $k, $v := .Values.labels }}
{{ $k }}: {{ $v }}
{{- end }}
# 遍历带索引的数组
{{- range $index, $port := .Values.ports }}
- name: port-{{ $index }}
containerPort: {{ $port }}
{{- end }}
# 在 range 内部使用 $root 避免作用域丢失
{{- range .Values.deployments }}
- name: {{ .name }}
image: {{ $.Values.global.imageRegistry }}/{{ .image }}
{{- end }}3.3 变量
Helm 模板中变量用 $ 前缀标识,用于在 with 或 range 作用域中引用外部上下文。
{{- $root := . }} # 保存根上下文
{{- $appName := include "my-app.name" . }} # 保存模板输出
{{- $fullName := printf "%s-%s" .Release.Name .Chart.Name }}
{{- $labels := (dict "app" $appName "tier" "backend") }}
{{- range .Values.deployments }}
{{- $deployName := printf "%s-%s" $fullName .name }}
{{- if .enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $deployName }}
labels:
app: {{ $appName }}
component: {{ .name }}
{{- range $k, $v := $labels }}
{{ $k }}: {{ $v }}
{{- end }}
spec:
replicas: {{ $root.Values.replicaCount }}
{{- end }}
{{- end }}3.4 命名模板(Named Templates)
命名模板相当于函数,通过 define 定义,通过 template 或 include 调用。
3.4.1 define 定义
{{- /* 定义通用 labels 模板 */}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}3.4.2 template 调用
template 是 Go 原生函数,直接将模板内容渲染到当前位置(不能用于管道)。
metadata:
labels:
{{- template "my-app.labels" . }}3.4.3 include 调用
include 是 Helm 扩展函数,将模板渲染为字符串并返回,可以用于管道传递。
metadata:
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
template:
metadata:
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}template 与 include 的区别:
| 特性 | template | include |
|---|---|---|
| 返回值 | 直接写入输出 | 返回字符串 |
| 管道支持 | 不支持 | 支持 |
| 缩进处理 | 需要在调用处处理 | 可配合 nindent/indent |
| 性能 | 略快 | 因字符串转换有微小开销 |
推荐实践: 始终使用 include + nindent,避免缩进问题。
3.5 常用模板函数
Helm 内建了大量函数,以下是常用的几类:
3.5.1 字符串函数
{{ upper "hello" }} # "HELLO"
{{ lower "HELLO" }} # "hello"
{{ title "hello world" }} # "Hello World"
{{ trimSuffix "-" "app-" }} # "app"
{{ trimPrefix "my-" "my-app" }} # "app"
{{ quote .Values.name }} # 添加双引号
{{ squote .Values.name }} # 添加单引号
{{ repeat 3 "ab" }} # "ababab"
{{ substr 0 3 "hello" }} # "hel"
{{ nospace "hello world" }} # "helloworld"
{{ hasPrefix "my" "my-app" }} # true
{{ hasSuffix "app" "my-app" }} # true
{{ contains "app" "my-app" }} # true
{{ printf "app-%s" .Release.Name }} # 格式化字符串3.5.2 数学函数
{{ add 1 2 }} # 3
{{ sub 5 3 }} # 2
{{ mul 2 3 }} # 6
{{ div 10 3 }} # 3
{{ mod 10 3 }} # 1
{{ max 1 2 3 }} # 3
{{ min 1 2 3 }} # 13.5.3 列表与字典函数
{{ list "a" "b" "c" }} # 创建列表
{{ prepend (list "b" "c") "a" }} # ["a" "b" "c"]
{{ append (list "a" "b") "c" }} # ["a" "b" "c"]
{{ first (list "a" "b") }} # "a"
{{ last (list "a" "b") }} # "b"
{{ has "a" (list "a" "b") }} # true
{{ dict "key1" "val1" "key2" "val2" }} # 创建字典
{{ keys (dict "a" 1 "b" 2) }} # ["a" "b"]
{{ values (dict "a" 1 "b" 2) }} # [1 2]
{{ merge (dict "a" 1) (dict "b" 2) }} # 合并字典3.5.4 类型转换与默认值
{{ default "default-value" .Values.optional }} # 设置默认值
{{ empty .Values.list }} # 检查是否为空
{{ required "name is required" .Values.name }} # 必填值校验
{{ fail "Invalid configuration" }} # 模板渲染时抛出错误
{{ toYaml .Values | nindent 8 }} # 转为 YAML 并缩进
{{ fromYaml "key: value" }} # 从 YAML 字符串解析
{{ toJson .Values }} # 转为 JSON
{{ fromJson "{\"key\":\"value\"}" }} # 从 JSON 字符串解析3.5.5 版本比较
{{ semverCompare ">=1.19.0-0" .Capabilities.KubeVersion.Version }} # 版本比较
{{ semverCompare ">=9.0.0 <10.0.0" "9.3.0" }} # 版本范围匹配3.5.6 缩进格式化
{{ include "my-app.labels" . | indent 4 }} # 每行缩进 4 个空格
{{ include "my-app.labels" . | nindent 4 }} # 换行后每行缩进 4 个空格四、Values 管理
4.1 默认值机制
Helm 从多个层级合并 values,优先级从低到高为:
values.yaml中的默认值(最低优先级)- 父 Chart 的 values(子 Chart 场景)
-f/--values指定的 YAML 文件(按顺序,后覆盖前)--set命令行参数--set-string命令行参数(强制视为字符串)--set-json命令行参数(JSON 格式)
4.2 --set 覆盖
# 覆盖单个值
helm install my-app ./my-chart --set image.tag=v1.2.3
# 覆盖多个值
helm install my-app ./my-chart \
--set image.tag=v1.2.3 \
--set replicaCount=3 \
--set service.type=NodePort
# 覆盖嵌套值(使用逗号或反斜线)
helm install my-app ./my-chart \
--set image.tag=v1.2.3,resources.limits.cpu=2
# 覆盖数组值
helm install my-app ./my-chart \
--set 'ports[0].name=http,ports[0].containerPort=8080'
# 覆盖 map 值
helm install my-app ./my-chart \
--set 'nodeSelector.kubernetes\\.io/os=linux'
# 强制字符串类型
helm install my-app ./my-chart \
--set-string image.tag=1.2.3 # 防止数字被解析为浮点数
# JSON 格式设置
helm install my-app ./my-chart \
--set-json 'resources={"limits":{"cpu":"2","memory":"4Gi"}}'4.3 多 values 文件
# 基础配置 + 环境覆盖
helm install my-app ./my-chart \
-f values.yaml \
-f values-prod.yaml
# 多个文件叠加(右侧文件优先级更高)
helm install my-app ./my-chart \
--values values.yaml \
--values values-prod.yaml \
--values values-region-asia.yamlvalues-prod.yaml 示例:
# 生产环境覆盖配置
replicaCount: 5
image:
tag: "v1.2.3-prod"
resources:
limits:
cpu: "4"
memory: 8Gi
requests:
cpu: "2"
memory: 4Gi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
ingress:
enabled: true
hosts:
- host: prod.example.com
paths:
- path: /
pathType: Prefix
probes:
liveness:
initialDelaySeconds: 60
readiness:
initialDelaySeconds: 40
nodeSelector:
kubernetes.io/hostname: production-pool4.4 子 Chart values
子 Chart 的 values 在父 Chart 的 values.yaml 中以子 Chart 名称作为顶层键:
# 父 Chart values.yaml
global:
imageRegistry: registry.example.com
storageClass: fast-ssd
mysql:
enabled: true
architecture: standalone
auth:
rootPassword: "root123"
database: my_app
username: app_user
password: app_pass
primary:
resources:
requests:
memory: 512Mi
cpu: 500m
redis:
enabled: true
architecture: standalone
auth:
password: redis_pass
master:
resources:
requests:
memory: 256Mi
cpu: 250m条件依赖控制:
# Chart.yaml 中配置 condition
dependencies:
- name: mysql
condition: mysql.enabled # 当 mysql.enabled 为 false 时不安装 mysql
- name: redis
condition: redis.enabled全局值(global)传递:
子 Chart 可以通过 .Values.global 访问父 Chart 中定义的全局值,实现多子 Chart 共享配置。
4.5 values 文件最佳实践
# 目录组织建议
helm-charts/
├── charts/
│ └── my-app/
│ ├── Chart.yaml
│ ├── values.yaml # 默认值(开发环境)
│ ├── values-staging.yaml # 预发布环境覆盖
│ ├── values-prod.yaml # 生产环境覆盖
│ ├── values-prod-apse1.yaml # 生产环境区域特定覆盖
│ └── templates/
│ └── ...
└── environments/
├── dev/
│ └── values.yaml
├── staging/
│ └── values.yaml
└── prod/
├── common.yaml
├── region-apse1.yaml
└── region-euw2.yaml五、Chart 仓库
5.1 添加仓库
# 添加官方 Stable 仓库
helm repo add stable https://charts.helm.sh/stable
# 添加 Bitnami 仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
# 添加私有仓库(需要认证)
helm repo add private https://helm.example.com/charts \
--username admin \
--password secret
# 添加 OCI 仓库
helm repo add my-oci-repo oci://registry.example.com/helm-charts
# 添加仓库别名
helm repo add jetstack https://charts.jetstack.io
# 查看已添加的仓库列表
helm repo list
# 更新仓库索引
helm repo update
# 移除仓库
helm repo remove bitnami
# 生成仓库索引(用于自建仓库服务器)
helm repo index ./charts --url https://helm.example.com/charts5.2 搜索 Chart
# 搜索仓库中的 Chart
helm search hub nginx # 搜索 Helm Hub
helm search repo nginx # 搜索本地已添加的仓库
helm search repo bitnami/nginx # 搜索特定仓库的 Chart
# 搜索特定版本
helm search repo bitnami/nginx --versions
helm search repo bitnami/nginx --version ">=15.0.0"
# 搜索已安装的 Release
helm search release my-app
# 查看 Chart 详细信息
helm show chart bitnami/nginx # 显示 Chart 元数据
helm show values bitnami/nginx # 显示默认 values
helm show readme bitnami/nginx # 显示 README
helm show all bitnami/nginx # 显示所有信息5.3 打包 Chart
# 打包 Chart 到当前目录
helm package ./my-app
# 打包并指定输出目录
helm package ./my-app -d ./dist
# 打包并更新 Chart 版本号(自动追加版本前缀)
helm package ./my-app --version 1.2.3
# 打包并指定 appVersion
helm package ./my-app --app-version 2.0.0
# 打包时签名(需要 GPG 密钥)
helm package ./my-app --sign --key 'John Doe' --keyring ~/.gnupg/secring.gpg
# 检查 Chart 包内容
helm show chart my-app-0.1.0.tgz5.4 推送 Chart 包
# 将 Chart 包推送到 OCI 兼容仓库
helm push my-app-0.1.0.tgz oci://registry.example.com/helm-charts
# 推送到 ChartMuseum 等自定义仓库(需配置插件)
helm cm-push my-app-0.1.0.tgz my-repo
# 保存 Chart 到本地仓库索引
helm repo index --merge index.yaml .5.5 OCI 仓库
Helm 3.8+ 支持 OCI(Open Container Initiative)标准的容器镜像仓库用于存储 Chart。
# 登录 OCI 仓库
helm registry login registry.example.com \
--username admin \
--password secret
# 登出 OCI 仓库
helm registry logout registry.example.com
# 推送 Chart 到 OCI 仓库
helm push my-app-0.1.0.tgz oci://registry.example.com/helm-charts
# 从 OCI 仓库拉取 Chart
helm pull oci://registry.example.com/helm-charts/my-app --version 0.1.0
# 从 OCI 仓库安装 Chart
helm install my-app oci://registry.example.com/helm-charts/my-app --version 0.1.0
# 从 OCI 仓库查看 Chart 信息
helm show all oci://registry.example.com/helm-charts/my-app --version 0.1.0
# OCI 仓库仓库别名配置
helm repo add my-oci oci://registry.example.com/helm-charts
helm install my-app my-oci/my-app --version 0.1.0OCI 仓库配置注意事项:
- 需要 Helm 3.8.0 及以上版本
- 需要仓库服务支持 OCI 规范(如 Harbor 2.0+, Azure Container Registry, Amazon ECR)
- OCI 仓库不支持
helm search功能 - Chart 按照 SemVer 规范以 tag 形式存储
- 推荐在 CI/CD 中使用 OCI 仓库实现不可变发布
六、完整示例:Spring Boot 微服务 Chart
以下是一个完整的 Spring Boot 微服务 Helm Chart 实现。
6.1 目录结构
spring-boot-app/
├── Chart.yaml
├── values.yaml
├── values-prod.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── pvc.yaml
│ └── NOTES.txt
└── .helmignore6.2 Chart.yaml
apiVersion: v2
name: spring-boot-app
description: A Helm chart for deploying Spring Boot microservice to Kubernetes
type: application
version: 0.1.0
appVersion: "1.0.0"
kubeVersion: ">=1.19.0-0"
home: https://github.com/example/spring-boot-app
sources:
- https://github.com/example/spring-boot-app
maintainers:
- name: Platform Team
email: platform@example.com
url: https://platform.example.com
keywords:
- spring-boot
- java
- microservice
dependencies:
- name: mysql
version: "~9.0.0"
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled
tags:
- database
annotations:
artifacthub.io/changes: |
- kind: added
description: Initial release of Spring Boot application chart
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/category: application6.3 _helpers.tpl
{{- /*
Expand the name of the chart.
*/}}
{{- define "spring-boot-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- /*
Create a default fully qualified app name.
*/}}
{{- define "spring-boot-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 }}
{{- /*
Create chart name and version as used by the chart label.
*/}}
{{- define "spring-boot-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- /*
Common labels
*/}}
{{- define "spring-boot-app.labels" -}}
helm.sh/chart: {{ include "spring-boot-app.chart" . }}
{{ include "spring-boot-app.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{- /*
Selector labels
*/}}
{{- define "spring-boot-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "spring-boot-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- /*
Create the name of the service account to use
*/}}
{{- define "spring-boot-app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "spring-boot-app.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{- /*
Generate image reference
*/}}
{{- define "spring-boot-app.image" -}}
{{- $registry := .Values.image.registry | default "" -}}
{{- $repository := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default (printf "v%s" .Chart.AppVersion) -}}
{{- if $registry -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- else -}}
{{- printf "%s:%s" $repository $tag -}}
{{- end -}}
{{- end }}
{{- /*
Return the appropriate apiVersion for ingress.
*/}}
{{- define "spring-boot-app.ingress.apiVersion" -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.Version -}}
networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.Version -}}
networking.k8s.io/v1beta1
{{- else -}}
extensions/v1beta1
{{- end }}
{{- end }}
{{- /*
Return the appropriate apiVersion for HPA.
*/}}
{{- define "spring-boot-app.hpa.apiVersion" -}}
{{- if semverCompare ">=1.23-0" .Capabilities.KubeVersion.Version -}}
autoscaling/v2
{{- else -}}
autoscaling/v2beta2
{{- end }}
{{- end }}6.4 deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "spring-boot-app.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
annotations:
{{- toYaml .Values.deploymentAnnotations | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
strategy:
type: {{ .Values.deploymentStrategy.type | default "RollingUpdate" }}
{{- if eq (.Values.deploymentStrategy.type | default "RollingUpdate") "RollingUpdate" }}
rollingUpdate:
maxUnavailable: {{ .Values.deploymentStrategy.maxUnavailable | default "25%" }}
maxSurge: {{ .Values.deploymentStrategy.maxSurge | default "25%" }}
{{- end }}
selector:
matchLabels:
{{- include "spring-boot-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "spring-boot-app.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
spec:
{{- include "spring-boot-app.imagePullSecrets" . | nindent 6 }}
serviceAccountName: {{ include "spring-boot-app.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds | default 60 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "spring-boot-app.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- if .Values.actuator.enabled }}
- name: actuator
containerPort: {{ .Values.actuator.port | default 8081 }}
protocol: TCP
{{- end }}
{{- if .Values.probes.liveness.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: {{ .Values.probes.liveness.port | default .Values.service.targetPort }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.liveness.failureThreshold }}
{{- end }}
{{- if .Values.probes.readiness.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: {{ .Values.probes.readiness.port | default .Values.service.targetPort }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
successThreshold: {{ .Values.probes.readiness.successThreshold }}
{{- end }}
{{- if .Values.probes.startup.enabled }}
startupProbe:
httpGet:
path: {{ .Values.probes.startup.path }}
port: {{ .Values.probes.startup.port | default .Values.service.targetPort }}
initialDelaySeconds: {{ .Values.probes.startup.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.startup.periodSeconds }}
timeoutSeconds: {{ .Values.probes.startup.timeoutSeconds }}
failureThreshold: {{ .Values.probes.startup.failureThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.lifecycle }}
lifecycle:
{{- toYaml .Values.lifecycle | nindent 12 }}
{{- end }}
{{- with .Values.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}6.5 service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "spring-boot-app.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if .Values.service.clusterIP }}
clusterIP: {{ .Values.service.clusterIP }}
{{- end }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
{{- if .Values.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges:
{{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }}
{{- end }}
ports:
- name: http
port: {{ .Values.service.port }}
protocol: TCP
targetPort: {{ .Values.service.targetPort }}
{{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
{{- if .Values.actuator.enabled }}
- name: actuator
port: {{ .Values.actuator.port | default 8081 }}
protocol: TCP
targetPort: {{ .Values.actuator.port | default 8081 }}
{{- end }}
{{- range .Values.service.extraPorts }}
- name: {{ .name }}
port: {{ .port }}
protocol: {{ .protocol | default "TCP" }}
targetPort: {{ .targetPort | default .port }}
{{- end }}
selector:
{{- include "spring-boot-app.selectorLabels" . | nindent 4 }}6.6 configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "spring-boot-app.fullname" . }}-config
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
data:
application.yaml: |
server:
port: {{ .Values.service.targetPort }}
shutdown: graceful
spring:
application:
name: {{ .Chart.Name }}
jpa:
hibernate:
ddl-auto: {{ .Values.config.jpa.ddlAuto | default "validate" }}
{{- if .Values.mysql.enabled }}
datasource:
url: jdbc:mysql://{{ .Release.Name }}-mysql:3306/{{ .Values.mysql.auth.database }}
username: {{ .Values.mysql.auth.username }}
password: ${MYSQL_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: {{ .Values.config.datasource.maxPoolSize | default 10 }}
minimum-idle: {{ .Values.config.datasource.minIdle | default 5 }}
idle-timeout: 30000
max-lifetime: 600000
{{- end }}
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
probes:
enabled: true
show-details: {{ .Values.config.health.showDetails | default "when-authorized" }}
metrics:
tags:
application: {{ .Chart.Name }}
release: {{ .Release.Name }}
logging:
level:
root: {{ .Values.config.logging.rootLevel | default "INFO" }}
com.example: {{ .Values.config.logging.appLevel | default "DEBUG" }}
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
{{- if .Values.config.additionalConfig }}
{{- toYaml .Values.config.additionalConfig | nindent 2 }}
{{- end }}
app.properties: |
app.name={{ .Chart.Name }}
app.version={{ .Chart.AppVersion }}
app.environment={{ .Values.global.environment | default .Release.Namespace }}6.7 ingress.yaml
{{- if .Values.ingress.enabled -}}
{{- $apiVersion := include "spring-boot-app.ingress.apiVersion" . }}
apiVersion: {{ $apiVersion }}
kind: Ingress
metadata:
name: {{ include "spring-boot-app.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
annotations:
{{- if .Values.ingress.annotations }}
{{- toYaml .Values.ingress.annotations | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- 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 }}
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.Version }}
pathType: {{ .pathType | default "Prefix" }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.Version }}
service:
name: {{ include "spring-boot-app.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- else }}
serviceName: {{ include "spring-boot-app.fullname" $ }}
servicePort: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}6.8 hpa.yaml
{{- if .Values.autoscaling.enabled }}
{{- $hpaApiVersion := include "spring-boot-app.hpa.apiVersion" . }}
apiVersion: {{ $hpaApiVersion }}
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "spring-boot-app.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "spring-boot-app.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
{{- if semverCompare ">=1.23-0" .Capabilities.KubeVersion.Version }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- else }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}
{{- if .Values.autoscaling.customMetrics }}
{{- toYaml .Values.autoscaling.customMetrics | nindent 4 }}
{{- end }}
behavior:
scaleDown:
stabilizationWindowSeconds: {{ .Values.autoscaling.scaleDownStabilization | default 300 }}
policies:
- type: Percent
value: {{ .Values.autoscaling.scaleDownPolicyPercent | default 10 }}
periodSeconds: {{ .Values.autoscaling.scaleDownPolicyPeriod | default 60 }}
scaleUp:
stabilizationWindowSeconds: {{ .Values.autoscaling.scaleUpStabilization | default 0 }}
policies:
- type: Percent
value: {{ .Values.autoscaling.scaleUpPolicyPercent | default 100 }}
periodSeconds: {{ .Values.autoscaling.scaleUpPolicyPeriod | default 60 }}
- type: Pods
value: {{ .Values.autoscaling.scaleUpPolicyPods | default 4 }}
periodSeconds: {{ .Values.autoscaling.scaleUpPolicyPeriod | default 60 }}
selectPolicy: Max
{{- end }}6.9 serviceaccount.yaml
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "spring-boot-app.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "spring-boot-app.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}6.10 values.yaml(默认值 - 开发环境)
global:
environment: dev
replicaCount: 1
nameOverride: ""
fullnameOverride: ""
image:
registry: ""
repository: example/spring-boot-app
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets: []
serviceAccount:
create: true
automount: true
annotations: {}
name: ""
deploymentAnnotations: {}
deploymentStrategy:
type: RollingUpdate
maxUnavailable: 25%
maxSurge: 25%
podAnnotations: {}
podLabels: {}
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
terminationGracePeriodSeconds: 60
service:
type: ClusterIP
port: 80
targetPort: 8080
protocol: TCP
annotations: {}
extraPorts: []
ingress:
enabled: false
className: nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
hosts:
- host: dev.example.com
paths:
- path: /api
pathType: Prefix
tls: []
actuator:
enabled: true
port: 8081
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 200m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
scaleDownStabilization: 300
scaleDownPolicyPercent: 10
scaleDownPolicyPeriod: 60
scaleUpStabilization: 0
scaleUpPolicyPercent: 100
scaleUpPolicyPods: 4
scaleUpPolicyPeriod: 60
customMetrics: []
probes:
liveness:
enabled: true
path: /actuator/health/liveness
port:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readiness:
enabled: true
path: /actuator/health/readiness
port:
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
startup:
enabled: true
path: /actuator/health
port:
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
env:
- name: SPRING_PROFILES_ACTIVE
value: "dev"
- name: JAVA_OPTS
value: "-Xms256m -Xmx512m -XX:+UseG1GC"
envFrom:
- configMapRef:
name: ""
- secretRef:
name: ""
extraVolumes: []
extraVolumeMounts: []
nodeSelector: {}
tolerations: []
affinity: {}
lifecycle: {}
config:
jpa:
ddlAuto: update
datasource:
maxPoolSize: 10
minIdle: 5
health:
showDetails: always
logging:
rootLevel: INFO
appLevel: DEBUG
additionalConfig: {}
mysql:
enabled: false
architecture: standalone
auth:
database: my_app
username: app_user
password: dev_password
primary:
resources:
requests:
memory: 256Mi
cpu: 250m6.11 values-prod.yaml(生产环境覆盖)
global:
environment: prod
replicaCount: 5
image:
pullPolicy: Always
tag: "v1.0.0-prod"
imagePullSecrets:
- name: registry-credentials
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/app-prod-role
deploymentStrategy:
type: RollingUpdate
maxUnavailable: 1
maxSurge: 1
podAnnotations:
sidecar.istio.io/inject: "true"
vault.hashicorp.com/agent-inject: "true"
podSecurityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 2001
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
service:
type: ClusterIP
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/cors-enabled: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- api.example.com
secretName: api-tls-prod
resources:
limits:
cpu: "4"
memory: 8Gi
requests:
cpu: "2"
memory: 4Gi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
scaleDownStabilization: 600
scaleUpPolicyPercent: 100
scaleUpPolicyPeriod: 60
probes:
liveness:
initialDelaySeconds: 60
periodSeconds: 30
readiness:
initialDelaySeconds: 40
periodSeconds: 15
startup:
failureThreshold: 60
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: JAVA_OPTS
value: "-Xms4g -Xmx4g -XX:+UseG1GC -XX:+PrintGCDetails"
- name: SPRING_CLOUD_CONFIG_ENABLED
value: "false"
nodeSelector:
node-type: compute-optimized
tolerations:
- key: CriticalAddonsOnly
operator: Exists
effect: NoSchedule
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- spring-boot-app
topologyKey: kubernetes.io/hostname
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 30
config:
jpa:
ddlAuto: validate
datasource:
maxPoolSize: 50
minIdle: 10
health:
showDetails: when-authorized
logging:
rootLevel: WARN
appLevel: INFO七、生命周期钩子(Hooks)
Helm 提供生命周期钩子,允许在 Release 生命周期的特定节点执行任务,如数据库迁移、资源初始化等。
7.1 支持的钩子类型
| 钩子名称 | 触发时机 | 典型用途 |
|---|---|---|
pre-install | 资源安装之前 | 创建数据库、初始化配置 |
post-install | 安装成功之后 | 发送通知、健康检查 |
pre-delete | 删除操作之前 | 备份数据、发送下线通知 |
post-delete | 删除成功之后 | 清理外部资源 |
pre-upgrade | 升级操作之前 | 数据库备份、Schema 迁移 |
post-upgrade | 升级成功之后 | 验证应用健康、刷新缓存 |
pre-rollback | 回滚操作之前 | 保存当前状态快照 |
post-rollback | 回滚成功之后 | 恢复缓存、清理临时资源 |
test | 运行 helm test 时 | 验证 Release 可用性 |
7.2 钩子基础示例
# templates/pre-install-db-migration.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-db-migration
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
template:
spec:
restartPolicy: Never
serviceAccountName: {{ include "my-app.serviceAccountName" . }}
containers:
- name: migration
image: {{ include "my-app.image" . }}
command:
- java
- -jar
- /app/app.jar
- --spring.profiles.active=migration
env:
{{- toYaml .Values.env | nindent 12 }}
envFrom:
{{- toYaml .Values.envFrom | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
backoffLimit: 2# templates/post-install-notify.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-notify
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": hook-succeeded
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
template:
spec:
restartPolicy: Never
containers:
- name: notify
image: curlimages/curl:latest
command:
- /bin/sh
- -c
|
curl -X POST -H "Content-Type: application/json" \
-d '{"text": "Release {{ .Release.Name }} version {{ .Chart.Version }} deployed successfully to {{ .Release.Namespace }}"}' \
{{ .Values.notification.webhookUrl | quote }}
backoffLimit: 1# templates/pre-upgrade-backup.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-backup-{{ .Release.Revision }}
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-10"
"helm.sh/hook-delete-policy": before-hook-creation
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
template:
spec:
restartPolicy: Never
serviceAccountName: {{ include "my-app.serviceAccountName" . }}
containers:
- name: backup
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
echo "Creating database backup before upgrade..."
kubectl exec -n {{ .Release.Namespace }} {{ .Release.Name }}-mysql-0 \
-- mysqldump --all-databases > /tmp/backup-{{ .Release.Revision }}.sql
echo "Backup completed: backup-{{ .Release.Revision }}.sql"
backoffLimit: 27.3 钩子注解说明
每个钩子 Job 需要三个关键注解:
| 注解 | 描述 | 示例值 |
|---|---|---|
helm.sh/hook | 指定钩子类型,多个用逗号分隔 | pre-install,pre-upgrade |
helm.sh/hook-weight | 执行权重,数字越小越先执行 | -5、0、5(默认 0) |
helm.sh/hook-delete-policy | 钩子资源删除策略 | before-hook-creation, hook-succeeded, hook-failed |
删除策略说明:
before-hook-creation:新钩子创建前删除旧资源(默认)hook-succeeded:钩子执行成功后删除hook-failed:钩子执行失败后删除(便于排查)
多个策略可组合使用,如 before-hook-creation,hook-succeeded。
7.4 钩子使用注意事项
- 钩子资源不会被作为 Release 的一部分进行管理,
helm delete不会删除钩子创建的 Job/Pod - 钩子 Job 失败会导致整个安装/升级/回滚操作失败
- 同类型钩子按权重排序执行,同权重按文件名和资源名称排序
- 钩子不能同时绑定
pre-和post-类型的多个钩子事件 hook-succeeded和hook-failed删除策略只在 Helm 3 中支持- 钩子资源建议设置
restartPolicy: Never,避免意外重试 - 使用
helm.sh/hook-delete-policy: hook-succeeded避免遗留已完成的 Job
八、常用命令速查表
8.1 Release 管理命令
| 操作 | 命令 | 说明 |
|---|---|---|
| 安装 Chart | helm install [name] [chart] [flags] | 安装一个 Chart 到集群 |
| 安装(生成名称) | helm install [chart] --generate-name | 自动生成 Release 名称 |
| 安装(dry-run) | helm install [name] [chart] --dry-run --debug | 预览模板渲染结果 |
| 升级 Release | helm upgrade [name] [chart] [flags] | 升级一个 Release |
| 安装或升级 | helm upgrade --install [name] [chart] [flags] | 如果不存在则安装,存在则升级 |
| 回滚 Release | helm rollback [name] [revision] [flags] | 回滚到指定版本 |
| 卸载 Release | helm uninstall [name] [flags] | 卸载一个 Release |
| 查看 Release | helm list [flags] | 列出所有 Release |
| 查看 Release 状态 | helm status [name] | 查看 Release 详细信息 |
| 查看 Release 详情 | helm get all [name] | 获取 Release 的所有资源 |
| 查看 Release values | helm get values [name] | 查看 Release 的 values 配置 |
| 查看 Release manifest | helm get manifest [name] | 查看 Release 的 Kubernetes 资源清单 |
| 查看 Release notes | helm get notes [name] | 查看 Release 的安装提示 |
| 查看 Release hooks | helm get hooks [name] | 查看 Release 的钩子资源 |
| Release 历史 | helm history [name] | 查看 Release 的所有修订版本 |
| 测试 Release | helm test [name] | 运行 Chart 中的测试模板 |
| 渲染模板 | helm template [name] [chart] [flags] | 本地渲染模板(不安装) |
| 验证 Chart | helm lint [chart] [flags] | 检查 Chart 语法和最佳实践 |
8.2 Chart 管理命令
| 操作 | 命令 | 说明 |
|---|---|---|
| 创建 Chart | helm create [name] | 创建一个新的 Chart 脚手架 |
| 打包 Chart | helm package [chart] [flags] | 将 Chart 打包为 .tgz 文件 |
| 拉取 Chart | helm pull [chart] [flags] | 下载 Chart 到本地 |
| 查看 Chart 信息 | helm show [chart] [flags] | 查看 Chart 的详细信息 |
| 查看 Chart values | helm show values [chart] | 查看 Chart 的默认配置值 |
| 依赖更新 | helm dependency update [chart] | 更新 Chart 依赖到 charts/ 目录 |
| 依赖构建 | helm dependency build [chart] | 从锁文件重建依赖 |
| 依赖列表 | helm dependency list [chart] | 列出 Chart 的依赖关系 |
8.3 仓库管理命令
| 操作 | 命令 | 说明 |
|---|---|---|
| 添加仓库 | helm repo add [name] [url] | 添加 Chart 仓库 |
| 列出现有仓库 | helm repo list | 列出所有已添加的仓库 |
| 更新仓库 | helm repo update [name] | 更新仓库索引缓存 |
| 搜索仓库 | helm search repo [keyword] | 在仓库中搜索 Chart |
| 搜索 Hub | helm search hub [keyword] | 在 Helm Hub 中搜索 Chart |
| 移除仓库 | helm repo remove [name] | 移除已添加的仓库 |
| 生成索引 | helm repo index [dir] [flags] | 生成仓库索引文件 |
8.4 OCI 仓库命令
| 操作 | 命令 | 说明 |
|---|---|---|
| 登录 OCI 仓库 | helm registry login [host] [flags] | 登录 OCI 兼容仓库 |
| 登出 OCI 仓库 | helm registry logout [host] | 登出 OCI 兼容仓库 |
| 推送 OCI Chart | helm push [chart] oci://[host]/[repo] | 推送 Chart 到 OCI 仓库 |
| 拉取 OCI Chart | helm pull oci://[host]/[repo] [flags] | 从 OCI 仓库拉取 Chart |
| 从 OCI 安装 | helm install [name] oci://[host]/[repo] [flags] | 从 OCI 仓库安装 Chart |
8.5 常用命令组合示例
# 完整的安装+升级+回滚流程
# Step 1: 首次安装应用(开发环境)
helm install my-app ./spring-boot-app \
--namespace dev \
--create-namespace \
--values values.yaml \
--set image.tag=v1.0.0
# Step 2: 升级应用
helm upgrade my-app ./spring-boot-app \
--namespace dev \
--values values.yaml \
--set image.tag=v1.1.0
# Step 3: 使用 --install 实现幂等操作(不存在则安装,存在则升级)
helm upgrade --install my-app ./spring-boot-app \
--namespace prod \
--create-namespace \
--values values.yaml \
--values values-prod.yaml \
--set image.tag=v1.2.0 \
--atomic \
--timeout 10m0s \
--wait
# Step 4: 查看部署历史
helm history my-app --namespace prod
# Step 5: 回滚到上一个版本
helm rollback my-app 1 --namespace prod
# Step 6: 清理已删除 Release 的历史记录
helm list --uninstalled --all-namespaces
helm uninstall my-app --keep-history # 保留历史记录8.6 选项标志速查
| 标志 | 说明 |
|---|---|
--atomic | 升级失败时自动回滚到上一个版本 |
--wait | 等待所有 Pod 就绪后才标记为成功 |
--timeout duration | 等待操作的超时时间(默认 5m) |
--force | 强制替换资源和策略 |
--dry-run | 模拟执行,不实际修改集群 |
--debug | 输出详细的调试信息 |
--no-hooks | 跳过钩子执行 |
--recreate-pods | 升级时重新创建所有 Pod |
--reset-values | 重置值为 Chart 默认值 |
--reuse-values | 复用上一次 Release 的 values |
--cleanup-on-fail | 失败时清理新创建的资源 |
--create-namespace | 如果命名空间不存在则创建 |
--description string | 为 Release 添加描述信息 |
--render-subchart-notes | 渲染子 Chart 的 NOTES.txt |
--set-json | 以 JSON 格式设置值 |
--set-string | 以字符串类型设置值 |
--set-file | 从文件读取值 |
8.7 Lint 与测试
# Chart 语法检查
helm lint ./spring-boot-app
# 严格模式(所有警告视为错误)
helm lint ./spring-boot-app --strict
# 使用指定 values 检查
helm lint ./spring-boot-app -f values-prod.yaml
# 本地渲染预览
helm template my-app ./spring-boot-app \
--values values-prod.yaml \
--debug
# 运行 Chart 测试
helm test my-app --namespace prod --logs
# 验证 Chart 在不同 K8s 版本下的兼容性
helm template my-app ./spring-boot-app \
--kube-version 1.25.0 \
--api-versions networking.k8s.io/v1