913 lines
21 KiB
Markdown
913 lines
21 KiB
Markdown
|
|
# Helm Charts
|
||
|
|
|
||
|
|
## Chart Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
mychart/
|
||
|
|
├── Chart.yaml # Chart metadata
|
||
|
|
├── values.yaml # Default values
|
||
|
|
├── values.schema.json # Values validation schema
|
||
|
|
├── charts/ # Dependency charts
|
||
|
|
├── templates/ # Template files
|
||
|
|
│ ├── NOTES.txt # Post-install notes
|
||
|
|
│ ├── _helpers.tpl # Template helpers
|
||
|
|
│ ├── deployment.yaml
|
||
|
|
│ ├── service.yaml
|
||
|
|
│ ├── ingress.yaml
|
||
|
|
│ ├── configmap.yaml
|
||
|
|
│ ├── secret.yaml
|
||
|
|
│ ├── serviceaccount.yaml
|
||
|
|
│ ├── hpa.yaml
|
||
|
|
│ └── tests/
|
||
|
|
│ └── test-connection.yaml
|
||
|
|
├── .helmignore # Ignore patterns
|
||
|
|
└── README.md # Chart documentation
|
||
|
|
```
|
||
|
|
|
||
|
|
## Chart.yaml
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: v2
|
||
|
|
name: myapp
|
||
|
|
description: A Helm chart for MyApp on Kubernetes
|
||
|
|
type: application
|
||
|
|
version: 1.2.0
|
||
|
|
appVersion: "2.5.0"
|
||
|
|
|
||
|
|
keywords:
|
||
|
|
- web
|
||
|
|
- application
|
||
|
|
- microservice
|
||
|
|
|
||
|
|
home: https://example.com
|
||
|
|
sources:
|
||
|
|
- https://github.com/example/myapp
|
||
|
|
|
||
|
|
maintainers:
|
||
|
|
- name: DevOps Team
|
||
|
|
email: devops@example.com
|
||
|
|
url: https://example.com/team
|
||
|
|
|
||
|
|
icon: https://example.com/logo.png
|
||
|
|
|
||
|
|
dependencies:
|
||
|
|
- name: postgresql
|
||
|
|
version: "12.x.x"
|
||
|
|
repository: https://charts.bitnami.com/bitnami
|
||
|
|
condition: postgresql.enabled
|
||
|
|
tags:
|
||
|
|
- database
|
||
|
|
|
||
|
|
- name: redis
|
||
|
|
version: "17.x.x"
|
||
|
|
repository: https://charts.bitnami.com/bitnami
|
||
|
|
condition: redis.enabled
|
||
|
|
tags:
|
||
|
|
- cache
|
||
|
|
|
||
|
|
annotations:
|
||
|
|
category: Application
|
||
|
|
```
|
||
|
|
|
||
|
|
## values.yaml
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Default values for myapp
|
||
|
|
replicaCount: 3
|
||
|
|
|
||
|
|
image:
|
||
|
|
repository: myregistry.io/myapp
|
||
|
|
pullPolicy: IfNotPresent
|
||
|
|
tag: "" # Overrides the image tag (default is .Chart.AppVersion)
|
||
|
|
|
||
|
|
imagePullSecrets:
|
||
|
|
- name: registry-credentials
|
||
|
|
|
||
|
|
nameOverride: ""
|
||
|
|
fullnameOverride: ""
|
||
|
|
|
||
|
|
serviceAccount:
|
||
|
|
create: true
|
||
|
|
annotations: {}
|
||
|
|
name: ""
|
||
|
|
|
||
|
|
podAnnotations:
|
||
|
|
prometheus.io/scrape: "true"
|
||
|
|
prometheus.io/port: "8080"
|
||
|
|
|
||
|
|
podSecurityContext:
|
||
|
|
runAsNonRoot: true
|
||
|
|
runAsUser: 1000
|
||
|
|
fsGroup: 2000
|
||
|
|
seccompProfile:
|
||
|
|
type: RuntimeDefault
|
||
|
|
|
||
|
|
securityContext:
|
||
|
|
allowPrivilegeEscalation: false
|
||
|
|
capabilities:
|
||
|
|
drop:
|
||
|
|
- ALL
|
||
|
|
readOnlyRootFilesystem: true
|
||
|
|
|
||
|
|
service:
|
||
|
|
type: ClusterIP
|
||
|
|
port: 80
|
||
|
|
targetPort: 8080
|
||
|
|
annotations: {}
|
||
|
|
|
||
|
|
ingress:
|
||
|
|
enabled: true
|
||
|
|
className: "nginx"
|
||
|
|
annotations:
|
||
|
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||
|
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||
|
|
hosts:
|
||
|
|
- host: myapp.example.com
|
||
|
|
paths:
|
||
|
|
- path: /
|
||
|
|
pathType: Prefix
|
||
|
|
tls:
|
||
|
|
- secretName: myapp-tls
|
||
|
|
hosts:
|
||
|
|
- myapp.example.com
|
||
|
|
|
||
|
|
resources:
|
||
|
|
limits:
|
||
|
|
cpu: 500m
|
||
|
|
memory: 512Mi
|
||
|
|
requests:
|
||
|
|
cpu: 100m
|
||
|
|
memory: 128Mi
|
||
|
|
|
||
|
|
autoscaling:
|
||
|
|
enabled: true
|
||
|
|
minReplicas: 3
|
||
|
|
maxReplicas: 10
|
||
|
|
targetCPUUtilizationPercentage: 80
|
||
|
|
targetMemoryUtilizationPercentage: 80
|
||
|
|
|
||
|
|
nodeSelector: {}
|
||
|
|
|
||
|
|
tolerations: []
|
||
|
|
|
||
|
|
affinity:
|
||
|
|
podAntiAffinity:
|
||
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||
|
|
- weight: 100
|
||
|
|
podAffinityTerm:
|
||
|
|
labelSelector:
|
||
|
|
matchExpressions:
|
||
|
|
- key: app.kubernetes.io/name
|
||
|
|
operator: In
|
||
|
|
values:
|
||
|
|
- myapp
|
||
|
|
topologyKey: kubernetes.io/hostname
|
||
|
|
|
||
|
|
livenessProbe:
|
||
|
|
httpGet:
|
||
|
|
path: /health
|
||
|
|
port: http
|
||
|
|
initialDelaySeconds: 30
|
||
|
|
periodSeconds: 10
|
||
|
|
timeoutSeconds: 5
|
||
|
|
failureThreshold: 3
|
||
|
|
|
||
|
|
readinessProbe:
|
||
|
|
httpGet:
|
||
|
|
path: /ready
|
||
|
|
port: http
|
||
|
|
initialDelaySeconds: 10
|
||
|
|
periodSeconds: 5
|
||
|
|
timeoutSeconds: 3
|
||
|
|
failureThreshold: 2
|
||
|
|
|
||
|
|
env:
|
||
|
|
- name: ENVIRONMENT
|
||
|
|
value: production
|
||
|
|
- name: LOG_LEVEL
|
||
|
|
value: info
|
||
|
|
|
||
|
|
envFrom: []
|
||
|
|
|
||
|
|
volumeMounts: []
|
||
|
|
volumes: []
|
||
|
|
|
||
|
|
# PostgreSQL dependency
|
||
|
|
postgresql:
|
||
|
|
enabled: true
|
||
|
|
auth:
|
||
|
|
username: myapp
|
||
|
|
password: "" # Set via --set or separate secret
|
||
|
|
database: myapp
|
||
|
|
primary:
|
||
|
|
persistence:
|
||
|
|
enabled: true
|
||
|
|
size: 10Gi
|
||
|
|
|
||
|
|
# Redis dependency
|
||
|
|
redis:
|
||
|
|
enabled: true
|
||
|
|
architecture: standalone
|
||
|
|
auth:
|
||
|
|
enabled: true
|
||
|
|
password: ""
|
||
|
|
master:
|
||
|
|
persistence:
|
||
|
|
enabled: true
|
||
|
|
size: 5Gi
|
||
|
|
```
|
||
|
|
|
||
|
|
## templates/_helpers.tpl
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
{{/*
|
||
|
|
Expand the name of the chart.
|
||
|
|
*/}}
|
||
|
|
{{- define "myapp.name" -}}
|
||
|
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||
|
|
{{- end }}
|
||
|
|
|
||
|
|
{{/*
|
||
|
|
Create a default fully qualified app name.
|
||
|
|
*/}}
|
||
|
|
{{- define "myapp.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 "myapp.chart" -}}
|
||
|
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||
|
|
{{- end }}
|
||
|
|
|
||
|
|
{{/*
|
||
|
|
Common labels
|
||
|
|
*/}}
|
||
|
|
{{- define "myapp.labels" -}}
|
||
|
|
helm.sh/chart: {{ include "myapp.chart" . }}
|
||
|
|
{{ include "myapp.selectorLabels" . }}
|
||
|
|
{{- if .Chart.AppVersion }}
|
||
|
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||
|
|
{{- end }}
|
||
|
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||
|
|
{{- end }}
|
||
|
|
|
||
|
|
{{/*
|
||
|
|
Selector labels
|
||
|
|
*/}}
|
||
|
|
{{- define "myapp.selectorLabels" -}}
|
||
|
|
app.kubernetes.io/name: {{ include "myapp.name" . }}
|
||
|
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||
|
|
{{- end }}
|
||
|
|
|
||
|
|
{{/*
|
||
|
|
Create the name of the service account to use
|
||
|
|
*/}}
|
||
|
|
{{- define "myapp.serviceAccountName" -}}
|
||
|
|
{{- if .Values.serviceAccount.create }}
|
||
|
|
{{- default (include "myapp.fullname" .) .Values.serviceAccount.name }}
|
||
|
|
{{- else }}
|
||
|
|
{{- default "default" .Values.serviceAccount.name }}
|
||
|
|
{{- end }}
|
||
|
|
{{- end }}
|
||
|
|
```
|
||
|
|
|
||
|
|
## templates/deployment.yaml
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: apps/v1
|
||
|
|
kind: Deployment
|
||
|
|
metadata:
|
||
|
|
name: {{ include "myapp.fullname" . }}
|
||
|
|
labels:
|
||
|
|
{{- include "myapp.labels" . | nindent 4 }}
|
||
|
|
spec:
|
||
|
|
{{- if not .Values.autoscaling.enabled }}
|
||
|
|
replicas: {{ .Values.replicaCount }}
|
||
|
|
{{- end }}
|
||
|
|
selector:
|
||
|
|
matchLabels:
|
||
|
|
{{- include "myapp.selectorLabels" . | nindent 6 }}
|
||
|
|
template:
|
||
|
|
metadata:
|
||
|
|
annotations:
|
||
|
|
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||
|
|
{{- with .Values.podAnnotations }}
|
||
|
|
{{- toYaml . | nindent 8 }}
|
||
|
|
{{- end }}
|
||
|
|
labels:
|
||
|
|
{{- include "myapp.selectorLabels" . | nindent 8 }}
|
||
|
|
spec:
|
||
|
|
{{- with .Values.imagePullSecrets }}
|
||
|
|
imagePullSecrets:
|
||
|
|
{{- toYaml . | nindent 8 }}
|
||
|
|
{{- end }}
|
||
|
|
serviceAccountName: {{ include "myapp.serviceAccountName" . }}
|
||
|
|
securityContext:
|
||
|
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||
|
|
containers:
|
||
|
|
- name: {{ .Chart.Name }}
|
||
|
|
securityContext:
|
||
|
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||
|
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||
|
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||
|
|
ports:
|
||
|
|
- name: http
|
||
|
|
containerPort: {{ .Values.service.targetPort }}
|
||
|
|
protocol: TCP
|
||
|
|
{{- with .Values.env }}
|
||
|
|
env:
|
||
|
|
{{- toYaml . | nindent 12 }}
|
||
|
|
{{- end }}
|
||
|
|
{{- with .Values.envFrom }}
|
||
|
|
envFrom:
|
||
|
|
{{- toYaml . | nindent 12 }}
|
||
|
|
{{- end }}
|
||
|
|
livenessProbe:
|
||
|
|
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||
|
|
readinessProbe:
|
||
|
|
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||
|
|
resources:
|
||
|
|
{{- toYaml .Values.resources | nindent 12 }}
|
||
|
|
{{- with .Values.volumeMounts }}
|
||
|
|
volumeMounts:
|
||
|
|
{{- toYaml . | nindent 12 }}
|
||
|
|
{{- end }}
|
||
|
|
{{- with .Values.volumes }}
|
||
|
|
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 }}
|
||
|
|
```
|
||
|
|
|
||
|
|
## templates/hpa.yaml
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
{{- if .Values.autoscaling.enabled }}
|
||
|
|
apiVersion: autoscaling/v2
|
||
|
|
kind: HorizontalPodAutoscaler
|
||
|
|
metadata:
|
||
|
|
name: {{ include "myapp.fullname" . }}
|
||
|
|
labels:
|
||
|
|
{{- include "myapp.labels" . | nindent 4 }}
|
||
|
|
spec:
|
||
|
|
scaleTargetRef:
|
||
|
|
apiVersion: apps/v1
|
||
|
|
kind: Deployment
|
||
|
|
name: {{ include "myapp.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 }}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Helm Hooks
|
||
|
|
|
||
|
|
### Pre-Install Hook (Database Migration)
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: batch/v1
|
||
|
|
kind: Job
|
||
|
|
metadata:
|
||
|
|
name: {{ include "myapp.fullname" . }}-migration
|
||
|
|
labels:
|
||
|
|
{{- include "myapp.labels" . | nindent 4 }}
|
||
|
|
annotations:
|
||
|
|
"helm.sh/hook": pre-install,pre-upgrade
|
||
|
|
"helm.sh/hook-weight": "0"
|
||
|
|
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||
|
|
spec:
|
||
|
|
backoffLimit: 3
|
||
|
|
template:
|
||
|
|
metadata:
|
||
|
|
labels:
|
||
|
|
app: migration
|
||
|
|
spec:
|
||
|
|
restartPolicy: Never
|
||
|
|
containers:
|
||
|
|
- name: migrate
|
||
|
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||
|
|
command: ["/app/migrate", "up"]
|
||
|
|
env:
|
||
|
|
- name: DATABASE_URL
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: {{ include "myapp.fullname" . }}-secrets
|
||
|
|
key: database-url
|
||
|
|
```
|
||
|
|
|
||
|
|
### Post-Install Hook (Test)
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: v1
|
||
|
|
kind: Pod
|
||
|
|
metadata:
|
||
|
|
name: {{ include "myapp.fullname" . }}-test
|
||
|
|
labels:
|
||
|
|
{{- include "myapp.labels" . | nindent 4 }}
|
||
|
|
annotations:
|
||
|
|
"helm.sh/hook": test
|
||
|
|
"helm.sh/hook-weight": "0"
|
||
|
|
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||
|
|
spec:
|
||
|
|
restartPolicy: Never
|
||
|
|
containers:
|
||
|
|
- name: test
|
||
|
|
image: curlimages/curl:latest
|
||
|
|
command: ['sh', '-c']
|
||
|
|
args:
|
||
|
|
- |
|
||
|
|
curl -f http://{{ include "myapp.fullname" . }}:{{ .Values.service.port }}/health || exit 1
|
||
|
|
```
|
||
|
|
|
||
|
|
## Helm Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Create new chart
|
||
|
|
helm create myapp
|
||
|
|
|
||
|
|
# Lint chart
|
||
|
|
helm lint myapp/
|
||
|
|
|
||
|
|
# Template rendering (dry-run)
|
||
|
|
helm template myapp ./myapp -f values-prod.yaml
|
||
|
|
|
||
|
|
# Install chart
|
||
|
|
helm install myapp ./myapp \
|
||
|
|
--namespace production \
|
||
|
|
--create-namespace \
|
||
|
|
--values values-prod.yaml \
|
||
|
|
--set image.tag=v1.2.0
|
||
|
|
|
||
|
|
# Upgrade chart
|
||
|
|
helm upgrade myapp ./myapp \
|
||
|
|
--namespace production \
|
||
|
|
--values values-prod.yaml \
|
||
|
|
--set image.tag=v1.3.0 \
|
||
|
|
--atomic \
|
||
|
|
--timeout 5m
|
||
|
|
|
||
|
|
# Rollback
|
||
|
|
helm rollback myapp 1 --namespace production
|
||
|
|
|
||
|
|
# List releases
|
||
|
|
helm list --namespace production
|
||
|
|
|
||
|
|
# Get values
|
||
|
|
helm get values myapp --namespace production
|
||
|
|
|
||
|
|
# Get manifest
|
||
|
|
helm get manifest myapp --namespace production
|
||
|
|
|
||
|
|
# Uninstall
|
||
|
|
helm uninstall myapp --namespace production
|
||
|
|
|
||
|
|
# Test
|
||
|
|
helm test myapp --namespace production
|
||
|
|
|
||
|
|
# Package chart
|
||
|
|
helm package myapp/ --version 1.2.0
|
||
|
|
|
||
|
|
# Dependency update
|
||
|
|
helm dependency update myapp/
|
||
|
|
```
|
||
|
|
|
||
|
|
## values-prod.yaml (Environment Override)
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
replicaCount: 5
|
||
|
|
|
||
|
|
image:
|
||
|
|
tag: v1.2.0
|
||
|
|
|
||
|
|
resources:
|
||
|
|
limits:
|
||
|
|
cpu: 1000m
|
||
|
|
memory: 1Gi
|
||
|
|
requests:
|
||
|
|
cpu: 250m
|
||
|
|
memory: 256Mi
|
||
|
|
|
||
|
|
autoscaling:
|
||
|
|
enabled: true
|
||
|
|
minReplicas: 5
|
||
|
|
maxReplicas: 20
|
||
|
|
|
||
|
|
ingress:
|
||
|
|
hosts:
|
||
|
|
- host: app.production.example.com
|
||
|
|
paths:
|
||
|
|
- path: /
|
||
|
|
pathType: Prefix
|
||
|
|
|
||
|
|
postgresql:
|
||
|
|
enabled: true
|
||
|
|
primary:
|
||
|
|
persistence:
|
||
|
|
size: 100Gi
|
||
|
|
resources:
|
||
|
|
limits:
|
||
|
|
cpu: 2000m
|
||
|
|
memory: 4Gi
|
||
|
|
requests:
|
||
|
|
cpu: 500m
|
||
|
|
memory: 1Gi
|
||
|
|
|
||
|
|
redis:
|
||
|
|
enabled: true
|
||
|
|
master:
|
||
|
|
persistence:
|
||
|
|
size: 20Gi
|
||
|
|
```
|
||
|
|
|
||
|
|
## Chart Testing
|
||
|
|
|
||
|
|
### Helm Test Command
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Run chart tests after installation
|
||
|
|
helm test myapp --namespace production
|
||
|
|
|
||
|
|
# Run tests with logs
|
||
|
|
helm test myapp --namespace production --logs
|
||
|
|
|
||
|
|
# Run tests with timeout
|
||
|
|
helm test myapp --namespace production --timeout 5m
|
||
|
|
```
|
||
|
|
|
||
|
|
### Chart Testing Tool (ct)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install chart-testing
|
||
|
|
brew install chart-testing
|
||
|
|
|
||
|
|
# Lint charts
|
||
|
|
ct lint --config ct.yaml
|
||
|
|
|
||
|
|
# Lint and install (CI/CD)
|
||
|
|
ct lint-and-install --config ct.yaml
|
||
|
|
|
||
|
|
# Test changed charts only
|
||
|
|
ct lint-and-install --target-branch main --config ct.yaml
|
||
|
|
```
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# ct.yaml - Chart Testing configuration
|
||
|
|
remote: origin
|
||
|
|
target-branch: main
|
||
|
|
chart-dirs:
|
||
|
|
- charts
|
||
|
|
chart-repos:
|
||
|
|
- bitnami=https://charts.bitnami.com/bitnami
|
||
|
|
helm-extra-args: --timeout 600s
|
||
|
|
validate-maintainers: true
|
||
|
|
check-version-increment: true
|
||
|
|
```
|
||
|
|
|
||
|
|
### Unit Testing with helm-unittest
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install plugin
|
||
|
|
helm plugin install https://github.com/helm-unittest/helm-unittest
|
||
|
|
|
||
|
|
# Run tests
|
||
|
|
helm unittest ./mychart
|
||
|
|
```
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# tests/deployment_test.yaml
|
||
|
|
suite: deployment tests
|
||
|
|
templates:
|
||
|
|
- templates/deployment.yaml
|
||
|
|
tests:
|
||
|
|
- it: should create deployment with correct replicas
|
||
|
|
set:
|
||
|
|
replicaCount: 5
|
||
|
|
asserts:
|
||
|
|
- isKind:
|
||
|
|
of: Deployment
|
||
|
|
- equal:
|
||
|
|
path: spec.replicas
|
||
|
|
value: 5
|
||
|
|
|
||
|
|
- it: should set resource limits
|
||
|
|
set:
|
||
|
|
resources:
|
||
|
|
limits:
|
||
|
|
cpu: 500m
|
||
|
|
memory: 256Mi
|
||
|
|
asserts:
|
||
|
|
- equal:
|
||
|
|
path: spec.template.spec.containers[0].resources.limits.cpu
|
||
|
|
value: 500m
|
||
|
|
|
||
|
|
- it: should not create HPA when autoscaling disabled
|
||
|
|
set:
|
||
|
|
autoscaling:
|
||
|
|
enabled: false
|
||
|
|
template: templates/hpa.yaml
|
||
|
|
asserts:
|
||
|
|
- hasDocuments:
|
||
|
|
count: 0
|
||
|
|
```
|
||
|
|
|
||
|
|
## Values Schema Validation
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"$schema": "https://json-schema.org/draft-07/schema#",
|
||
|
|
"type": "object",
|
||
|
|
"required": ["image", "service"],
|
||
|
|
"properties": {
|
||
|
|
"replicaCount": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 1,
|
||
|
|
"maximum": 100,
|
||
|
|
"default": 1
|
||
|
|
},
|
||
|
|
"image": {
|
||
|
|
"type": "object",
|
||
|
|
"required": ["repository"],
|
||
|
|
"properties": {
|
||
|
|
"repository": {
|
||
|
|
"type": "string",
|
||
|
|
"pattern": "^[a-z0-9.-/]+$"
|
||
|
|
},
|
||
|
|
"tag": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"pullPolicy": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": ["Always", "IfNotPresent", "Never"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"service": {
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"type": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": ["ClusterIP", "NodePort", "LoadBalancer"]
|
||
|
|
},
|
||
|
|
"port": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 1,
|
||
|
|
"maximum": 65535
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"resources": {
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"limits": {
|
||
|
|
"$ref": "#/definitions/resourceRequirements"
|
||
|
|
},
|
||
|
|
"requests": {
|
||
|
|
"$ref": "#/definitions/resourceRequirements"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"definitions": {
|
||
|
|
"resourceRequirements": {
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"cpu": {
|
||
|
|
"type": "string",
|
||
|
|
"pattern": "^[0-9]+m?$"
|
||
|
|
},
|
||
|
|
"memory": {
|
||
|
|
"type": "string",
|
||
|
|
"pattern": "^[0-9]+(Mi|Gi)$"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Chart Repository
|
||
|
|
|
||
|
|
### Create Repository
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Package chart
|
||
|
|
helm package mychart/ --version 1.2.0 --destination ./repo
|
||
|
|
|
||
|
|
# Generate index
|
||
|
|
helm repo index ./repo --url https://charts.example.com
|
||
|
|
|
||
|
|
# Update index with new chart
|
||
|
|
helm repo index ./repo --url https://charts.example.com --merge ./repo/index.yaml
|
||
|
|
```
|
||
|
|
|
||
|
|
### GitHub Pages Repository
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# .github/workflows/release.yaml
|
||
|
|
name: Release Charts
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
branches: [main]
|
||
|
|
paths: ['charts/**']
|
||
|
|
jobs:
|
||
|
|
release:
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
steps:
|
||
|
|
- uses: actions/checkout@v4
|
||
|
|
with:
|
||
|
|
fetch-depth: 0
|
||
|
|
- name: Configure Git
|
||
|
|
run: |
|
||
|
|
git config user.name "$GITHUB_ACTOR"
|
||
|
|
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||
|
|
- name: Install Helm
|
||
|
|
uses: azure/setup-helm@v3
|
||
|
|
- name: Run chart-releaser
|
||
|
|
uses: helm/chart-releaser-action@v1.6.0
|
||
|
|
env:
|
||
|
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||
|
|
```
|
||
|
|
|
||
|
|
### OCI Registry
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Login to registry
|
||
|
|
helm registry login myregistry.io -u user -p token
|
||
|
|
|
||
|
|
# Push chart to OCI registry
|
||
|
|
helm push mychart-1.2.0.tgz oci://myregistry.io/charts
|
||
|
|
|
||
|
|
# Pull from OCI
|
||
|
|
helm pull oci://myregistry.io/charts/mychart --version 1.2.0
|
||
|
|
|
||
|
|
# Install from OCI
|
||
|
|
helm install myapp oci://myregistry.io/charts/mychart --version 1.2.0
|
||
|
|
```
|
||
|
|
|
||
|
|
## Helm Plugins
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# helm-diff - preview upgrades
|
||
|
|
helm plugin install https://github.com/databus23/helm-diff
|
||
|
|
helm diff upgrade myapp ./mychart -f values-prod.yaml
|
||
|
|
|
||
|
|
# helm-secrets - manage encrypted secrets
|
||
|
|
helm plugin install https://github.com/jkroepke/helm-secrets
|
||
|
|
helm secrets encrypt secrets.yaml
|
||
|
|
helm secrets decrypt secrets.yaml.enc
|
||
|
|
helm secrets install myapp ./mychart -f secrets.yaml.enc
|
||
|
|
|
||
|
|
# helm-git - use git repos as chart sources
|
||
|
|
helm plugin install https://github.com/aslafy-z/helm-git
|
||
|
|
helm repo add mycharts git+https://github.com/myorg/charts@charts?ref=main
|
||
|
|
|
||
|
|
# helm-s3 - S3 as chart repository
|
||
|
|
helm plugin install https://github.com/hypnoglow/helm-s3
|
||
|
|
helm s3 init s3://my-bucket/charts
|
||
|
|
helm s3 push mychart-1.2.0.tgz my-s3-repo
|
||
|
|
```
|
||
|
|
|
||
|
|
## Complex Upgrade/Rollback
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Upgrade with atomic (rollback on failure)
|
||
|
|
helm upgrade myapp ./mychart \
|
||
|
|
--namespace production \
|
||
|
|
--atomic \
|
||
|
|
--timeout 10m \
|
||
|
|
--wait
|
||
|
|
|
||
|
|
# Upgrade with cleanup on failure
|
||
|
|
helm upgrade myapp ./mychart \
|
||
|
|
--namespace production \
|
||
|
|
--cleanup-on-fail
|
||
|
|
|
||
|
|
# Force resource update (recreate)
|
||
|
|
helm upgrade myapp ./mychart \
|
||
|
|
--namespace production \
|
||
|
|
--force
|
||
|
|
|
||
|
|
# Dry run before upgrade
|
||
|
|
helm upgrade myapp ./mychart \
|
||
|
|
--namespace production \
|
||
|
|
--dry-run \
|
||
|
|
--debug
|
||
|
|
|
||
|
|
# Compare current vs new
|
||
|
|
helm get manifest myapp -n production > current.yaml
|
||
|
|
helm template myapp ./mychart -f values-prod.yaml > new.yaml
|
||
|
|
diff current.yaml new.yaml
|
||
|
|
|
||
|
|
# Rollback to specific revision
|
||
|
|
helm rollback myapp 3 --namespace production
|
||
|
|
|
||
|
|
# Rollback with wait
|
||
|
|
helm rollback myapp 3 --namespace production --wait --timeout 5m
|
||
|
|
|
||
|
|
# View revision history
|
||
|
|
helm history myapp --namespace production
|
||
|
|
```
|
||
|
|
|
||
|
|
## Library Charts
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Chart.yaml for library chart
|
||
|
|
apiVersion: v2
|
||
|
|
name: mylib
|
||
|
|
type: library
|
||
|
|
version: 1.0.0
|
||
|
|
```
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# templates/_deployment.tpl in library
|
||
|
|
{{- define "mylib.deployment" -}}
|
||
|
|
apiVersion: apps/v1
|
||
|
|
kind: Deployment
|
||
|
|
metadata:
|
||
|
|
name: {{ include "mylib.fullname" . }}
|
||
|
|
labels:
|
||
|
|
{{- include "mylib.labels" . | nindent 4 }}
|
||
|
|
spec:
|
||
|
|
replicas: {{ .Values.replicaCount }}
|
||
|
|
selector:
|
||
|
|
matchLabels:
|
||
|
|
{{- include "mylib.selectorLabels" . | nindent 6 }}
|
||
|
|
template:
|
||
|
|
metadata:
|
||
|
|
labels:
|
||
|
|
{{- include "mylib.selectorLabels" . | nindent 8 }}
|
||
|
|
spec:
|
||
|
|
containers:
|
||
|
|
- name: {{ .Chart.Name }}
|
||
|
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||
|
|
{{- end }}
|
||
|
|
```
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Using library chart
|
||
|
|
# Chart.yaml
|
||
|
|
dependencies:
|
||
|
|
- name: mylib
|
||
|
|
version: "1.x.x"
|
||
|
|
repository: https://charts.example.com
|
||
|
|
|
||
|
|
# templates/deployment.yaml
|
||
|
|
{{- include "mylib.deployment" . }}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
1. **Versioning**: Follow semantic versioning for charts
|
||
|
|
2. **Values**: Provide sensible defaults, allow overrides
|
||
|
|
3. **Documentation**: Document all values in README
|
||
|
|
4. **Testing**: Include tests in templates/tests/
|
||
|
|
5. **Helpers**: Use _helpers.tpl for reusable templates
|
||
|
|
6. **Labels**: Include standard Kubernetes labels
|
||
|
|
7. **Annotations**: Use annotations for metadata and tools
|
||
|
|
8. **Hooks**: Use hooks for migrations, cleanup
|
||
|
|
9. **Dependencies**: Pin dependency versions
|
||
|
|
10. **Schema**: Validate values with values.schema.json
|
||
|
|
11. **Use ct** for comprehensive chart testing in CI
|
||
|
|
12. **Use helm-diff** before production upgrades
|
||
|
|
13. **Encrypt secrets** with helm-secrets or sealed-secrets
|
||
|
|
14. **Use library charts** for shared patterns
|
||
|
|
15. **Push to OCI registries** for better artifact management
|