本文目录导读:

- 项目结构
- Chart.yaml
- values.yaml
- templates/_helpers.tpl
- templates/deployment.yaml
- templates/service.yaml
- templates/configmap.yaml
- templates/secrets.yaml
- templates/hpa.yaml
- templates/ingress.yaml
- templates/serviceaccount.yaml
- templates/pvc.yaml
- templates/NOTES.txt
- values-dev.yaml
- values-prod.yaml
- 使用方法
- Dockerfile 示例
我将为您提供一个完整的Java应用Helm chart案例,包括详细的配置和使用说明。
项目结构
java-app-chart/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
├── templates/
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secrets.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ └── pvc.yaml
└── charts/
Chart.yaml
apiVersion: v2
name: java-app
description: A Helm chart for Java Spring Boot application
type: application
version: 1.0.0
appVersion: "1.0.0"
# 依赖项(如果需要)
dependencies:
- name: mysql
version: 9.4.0
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled
- name: redis
version: 17.3.7
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
values.yaml
# 默认配置
replicaCount: 2
image:
repository: your-registry/java-app
tag: latest
pullPolicy: Always
# 指定镜像拉取密钥
pullSecrets:
- name: registry-secret
nameOverride: ""
fullnameOverride: ""
# 环境变量配置
env:
normal:
- name: JAVA_OPTS
value: "-Xms256m -Xmx512m"
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: TZ
value: "Asia/Shanghai"
# 引用ConfigMap的配置
fromConfigMap:
- configMapRef:
name: java-app-config
# 引用Secret的配置
fromSecret:
- secretRef:
name: java-app-secret
# 容器端口配置
ports:
- name: http
containerPort: 8080
protocol: TCP
# 健康检查
livenessProbe:
httpGet:
path: /actuator/health
port: http
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# 资源限制
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: "500m"
memory: 512Mi
# 自动扩缩容
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# Service配置
service:
type: ClusterIP
port: 80
targetPort: http
annotations: {}
# Ingress配置
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: nginx
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
backend:
serviceName: java-app
servicePort: 80
tls:
- secretName: java-app-tls
hosts:
- app.example.com
# ConfigMap配置
configMap:
enabled: true
data:
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/java_app
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
redis:
host: redis
port: 6379
logging:
level:
root: INFO
com.example: DEBUG
# Secret配置
secrets:
enabled: true
data:
DB_PASSWORD: UGFzc3dvcmQxMjM= # Base64编码
REDIS_PASSWORD: UGFzc3dvcmQxMjM=$
# 持久化存储
persistence:
enabled: true
storageClass: ""
accessMode: ReadWriteOnce
size: 10Gi
existingClaim: ""
annotations: {}
# ServiceAccount
serviceAccount:
create: true
name: ""
annotations: {}
# 其他配置
nodeSelector: {}
tolerations: []
affinity: {}
# 滚动更新策略
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
# 自定义标签
labels:
app.kubernetes.io/name: java-app
app.kubernetes.io/managed-by: helm
templates/_helpers.tpl
{{- define "java-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "java-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 "java-app.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
{{ include "java-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.labels }}
{{ toYaml . }}
{{- end }}
{{- end -}}
{{- define "java-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "java-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "java-app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "java-app.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "java-app.fullname" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
{{- toYaml .Values.strategy | nindent 4 }}
selector:
matchLabels:
{{- include "java-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "java-app.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "java-app.serviceAccountName" . }}
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
{{- toYaml .Values.ports | nindent 12 }}
env:
{{- toYaml .Values.env.normal | nindent 12 }}
{{- if .Values.env.fromConfigMap }}
{{- toYaml .Values.env.fromConfigMap | nindent 12 }}
{{- end }}
{{- if .Values.env.fromSecret }}
{{- toYaml .Values.env.fromSecret | nindent 12 }}
{{- end }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
{{- if .Values.configMap.enabled }}
- name: config
mountPath: /app/config
readOnly: true
{{- end }}
{{- if .Values.persistence.enabled }}
- name: data
mountPath: /app/data
{{- end }}
volumes:
{{- if .Values.configMap.enabled }}
- name: config
configMap:
name: {{ include "java-app.fullname" . }}-config
{{- end }}
{{- if .Values.persistence.enabled }}
- name: data
persistentVolumeClaim:
claimName: {{ if .Values.persistence.existingClaim }}{{ .Values.persistence.existingClaim }}{{ else }}{{ include "java-app.fullname" . }}{{ end }}
{{- 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 }}
templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "java-app.fullname" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "java-app.selectorLabels" . | nindent 4 }}
templates/configmap.yaml
{{- if .Values.configMap.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "java-app.fullname" . }}-config
labels:
{{- include "java-app.labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.configMap.data }}
{{ $key }}: |
{{ $value | indent 4 }}
{{- end }}
{{- end }}
templates/secrets.yaml
{{- if .Values.secrets.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "java-app.fullname" . }}-secret
labels:
{{- include "java-app.labels" . | nindent 4 }}
type: Opaque
data:
{{- range $key, $value := .Values.secrets.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
templates/hpa.yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "java-app.fullname" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "java-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 }}
templates/ingress.yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "java-app.fullname" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- toYaml .Values.ingress.tls | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "java-app.fullname" $ }}
port:
number: {{ .backend.servicePort }}
{{- end }}
{{- end }}
{{- end }}
templates/serviceaccount.yaml
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "java-app.serviceAccountName" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
templates/pvc.yaml
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "java-app.fullname" . }}
labels:
{{- include "java-app.labels" . | nindent 4 }}
{{- with .Values.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- end }}
templates/NOTES.txt
Get the application URL:
{{- if .Values.ingress.enabled }}
http://{{ .Values.ingress.hosts[0].host }}
{{- else if eq .Values.service.type "ClusterIP" }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "java-app.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:8080
{{- else if eq .Values.service.type "NodePort" }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "java-app.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo "Visit http://$NODE_IP:$NODE_PORT to use your application"
{{- end }}
values-dev.yaml
replicaCount: 1
image:
repository: your-registry/java-app
tag: dev-latest
pullPolicy: Always
env:
normal:
- name: SPRING_PROFILES_ACTIVE
value: "dev"
- name: JAVA_OPTS
value: "-Xms128m -Xmx256m"
resources:
limits:
cpu: "500m"
memory: 512Mi
requests:
cpu: "250m"
memory: 256Mi
autoscaling:
enabled: false
ingress:
enabled: false
service:
type: ClusterIP
values-prod.yaml
replicaCount: 3
image:
repository: your-registry/java-app
tag: stable
pullPolicy: IfNotPresent
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "1"
memory: 1Gi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
ingress:
enabled: true
hosts:
- host: prod-app.example.com
paths:
- path: /
pathType: Prefix
backend:
servicePort: 80
tls:
- secretName: prod-tls
hosts:
- prod-app.example.com
使用方法
安装chart
# 基本安装 helm install java-app ./java-app-chart # 指定环境 helm install java-app ./java-app-chart -f values-dev.yaml helm install java-app ./java-app-chart -f values-prod.yaml # 指定命名空间 helm install java-app ./java-app-chart -n my-namespace --create-namespace
升级
# 升级release helm upgrade java-app ./java-app-chart -f values-prod.yaml # 指定版本 helm upgrade java-app ./java-app-chart --set image.tag=1.0.1
回滚
# 查看历史版本 helm history java-app # 回滚到指定版本 helm rollback java-app 2
卸载
helm uninstall java-app -n my-namespace
调试和测试
# 渲染模板 helm template java-app ./java-app-chart -f values-dev.yaml # 检查语法 helm lint ./java-app-chart # 渲染并调试 helm install java-app ./java-app-chart --dry-run --debug
Dockerfile 示例
# 多阶段构建 FROM maven:3.8.4-openjdk-17 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 运行时镜像 FROM openjdk:17-jdk-alpine WORKDIR /app COPY --from=build /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
这个完整的Helm chart案例包含了Java应用部署所需的所有资源,可以根据实际需求进行调整和扩展。