Anatomie d'un Chart
1 - Créer un nouveau Chart
1.1 Génération avec helm create
# Créer un nouveau chart
helm create mon-app
# Structure générée
mon-app/
├── .helmignore # Fichiers à ignorer lors du packaging
├── Chart.yaml # Métadonnées du chart
├── values.yaml # Configuration par défaut
├── charts/ # Dépendances
├── templates/ # Templates Kubernetes
│ ├── NOTES.txt # Notes post-installation
│ ├── _helpers.tpl # Fonctions helpers
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── tests/
│ └── test-connection.yaml
└── README.md
1.2 Structure minimale
minimal-chart/
├── Chart.yaml
├── values.yaml
└── templates/
└── configmap.yaml
# Chart.yaml (minimal)
apiVersion: v2
name: minimal-chart
version: 0.1.0
2 - Fichiers de configuration
2.1 Chart.yaml complet
apiVersion: v2
name: mon-application
version: 1.0.0
appVersion: "2.1.0"
description: |
Une application web complète avec base de données
et cache Redis.
type: application
kubeVersion: ">=1.23.0"
keywords:
- web
- api
- microservice
home: https://github.com/monorg/mon-app
sources:
- https://github.com/monorg/mon-app
- https://github.com/monorg/mon-app-chart
maintainers:
- name: DevOps Team
email: [email protected]
url: https://example.com
icon: https://example.com/icons/app.png
annotations:
artifacthub.io/category: web-application
artifacthub.io/license: Apache-2.0
artifacthub.io/links: |
- name: Documentation
url: https://docs.example.com
- name: Support
url: https://support.example.com
# Dépendances
dependencies:
- name: postgresql
version: "~12.1.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
alias: db
- name: redis
version: "17.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
import-values:
- child: primary
parent: redis
2.2 .helmignore
# .helmignore
# Fichiers à exclure du package
# Patterns communs
.DS_Store
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# IDE
*.swp
*.bak
*.tmp
*.orig
*~
.idea/
*.tmproj
.vscode/
# CI/CD
.travis.yml
.gitlab-ci.yml
.github/
# Tests et développement
OWNERS
SECURITY.md
CONTRIBUTING.md
hack/
Makefile
# Fichiers de build
*.tgz
2.3 values.yaml organisé
# values.yaml
# ===========================================
# Configuration globale
# ===========================================
global:
imageRegistry: ""
imagePullSecrets: []
storageClass: ""
# ===========================================
# Application principale
# ===========================================
replicaCount: 1
image:
repository: nginx
tag: "" # Défaut: appVersion du Chart.yaml
pullPolicy: IfNotPresent
pullSecrets: []
nameOverride: ""
fullnameOverride: ""
# ===========================================
# Service Account
# ===========================================
serviceAccount:
create: true
automount: true
annotations: {}
name: ""
# ===========================================
# Pod
# ===========================================
podAnnotations: {}
podLabels: {}
podSecurityContext:
fsGroup: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# ===========================================
# Service
# ===========================================
service:
type: ClusterIP
port: 80
# ===========================================
# Ingress
# ===========================================
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# ===========================================
# Ressources
# ===========================================
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
# ===========================================
# Probes
# ===========================================
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
# ===========================================
# Autoscaling
# ===========================================
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# ===========================================
# Volumes et Persistence
# ===========================================
persistence:
enabled: false
storageClass: ""
accessModes:
- ReadWriteOnce
size: 8Gi
annotations: {}
existingClaim: ""
# ===========================================
# Configuration additionnelle
# ===========================================
env: []
envFrom: []
volumes: []
volumeMounts: []
nodeSelector: {}
tolerations: []
affinity: {}
# ===========================================
# Dépendances
# ===========================================
postgresql:
enabled: false
auth:
username: app
database: appdb
redis:
enabled: false
architecture: standalone
3 - Dossier templates/
3.1 Structure typique
templates/
├── NOTES.txt # Message post-installation
├── _helpers.tpl # Fonctions partagées
├── deployment.yaml # Deployment principal
├── service.yaml # Service
├── serviceaccount.yaml # ServiceAccount
├── configmap.yaml # ConfigMaps
├── secret.yaml # Secrets
├── ingress.yaml # Ingress
├── hpa.yaml # HorizontalPodAutoscaler
├── pdb.yaml # PodDisruptionBudget
├── pvc.yaml # PersistentVolumeClaim
├── networkpolicy.yaml # NetworkPolicy
├── cronjob.yaml # CronJobs
└── tests/
└── test-connection.yaml
3.2 _helpers.tpl
{{/* templates/_helpers.tpl */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "mon-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "mon-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 "mon-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "mon-app.labels" -}}
helm.sh/chart: {{ include "mon-app.chart" . }}
{{ include "mon-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "mon-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mon-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "mon-app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mon-app.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Return the proper image name
*/}}
{{- define "mon-app.image" -}}
{{- $registryName := .Values.global.imageRegistry | default "" -}}
{{- $repositoryName := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- if $registryName }}
{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
{{- else }}
{{- printf "%s:%s" $repositoryName $tag -}}
{{- end }}
{{- end }}
3.3 deployment.yaml
{{/* templates/deployment.yaml */}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mon-app.fullname" . }}
labels:
{{- include "mon-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "mon-app.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "mon-app.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "mon-app.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "mon-app.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
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 }}
3.4 NOTES.txt
{{/* templates/NOTES.txt */}}
🎉 {{ .Chart.Name }} a été déployé avec succès !
Informations de la release:
- Nom: {{ .Release.Name }}
- Namespace: {{ .Release.Namespace }}
- Révision: {{ .Release.Revision }}
Pour accéder à l'application:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ (first $host.paths).path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "mon-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 contains "LoadBalancer" .Values.service.type }}
NOTE: Le LoadBalancer peut prendre quelques minutes à provisionner.
Exécutez cette commande pour obtenir l'IP:
kubectl get --namespace {{ .Release.Namespace }} svc {{ include "mon-app.fullname" . }} -w
{{- else if contains "ClusterIP" .Values.service.type }}
kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "mon-app.fullname" . }} 8080:{{ .Values.service.port }}
Puis accédez à http://localhost:8080
{{- end }}
Pour voir les logs:
kubectl logs -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} -f
4 - Gestion des dépendances
4.1 Définir les dépendances
# Chart.yaml
dependencies:
- name: postgresql
version: "12.1.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
alias: db
- name: redis
version: "17.3.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
tags:
- cache
- backend
4.2 Gérer les dépendances
# Télécharger les dépendances
helm dependency update ./mon-chart
# Lister les dépendances
helm dependency list ./mon-chart
# Résultat: charts/ contient les .tgz des dépendances
# et Chart.lock est créé/mis à jour
4.3 Chart.lock
# Chart.lock (généré automatiquement)
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 12.1.0
- name: redis
repository: https://charts.bitnami.com/bitnami
version: 17.3.0
digest: sha256:abc123...
generated: "2023-11-13T10:00:00Z"
4.4 Configurer les dépendances
# values.yaml
postgresql:
enabled: true
auth:
username: appuser
password: secretpassword
database: appdb
primary:
resources:
limits:
cpu: 500m
memory: 512Mi
redis:
enabled: true
architecture: standalone
auth:
enabled: false
5 - Packaging et distribution
5.1 Packager un chart
# Packager le chart
helm package ./mon-chart
# Résultat: mon-chart-1.0.0.tgz
# Avec un répertoire de destination
helm package ./mon-chart -d ./packages
# Signer le package (optionnel)
helm package ./mon-chart --sign --key "John Doe" --keyring ~/.gnupg/pubring.gpg
5.2 Valider un chart
# Linting
helm lint ./mon-chart
# Template rendering (dry-run)
helm template mon-release ./mon-chart
# Avec values spécifiques
helm template mon-release ./mon-chart -f production.yaml
# Dry-run contre le cluster
helm install mon-release ./mon-chart --dry-run --debug
Résumé
Dans ce chapitre, nous avons exploré :
- La création d'un nouveau chart
- La structure du Chart.yaml et ses options
- Le fichier .helmignore
- L'organisation des values.yaml
- Les templates et helpers
- La gestion des dépendances
- Le packaging et la distribution
Prochaine étape
Dans le prochain chapitre, nous approfondirons les Templates et Values.
→ Chapitre suivant : Templates et Values