Aller au contenu
Conteneurs & Orchestration medium

Qualité des charts Helm : schema, documentation et linting

20 min de lecture

logo helm

Un chart Helm mal configuré peut générer des manifestes Kubernetes invalides, des déploiements qui échouent silencieusement, ou des failles de sécurité. Ce guide vous montre comment détecter ces problèmes avant qu’ils n’atteignent la production, en mettant en place une chaîne de validation automatisée.

Concrètement, vous allez apprendre à :

  • Valider les values fournies par les utilisateurs (types, limites, valeurs autorisées)
  • Générer automatiquement la documentation de votre chart
  • Vérifier que les manifestes générés sont conformes aux standards Kubernetes
  • Détecter les mauvaises pratiques de sécurité (containers root, absence de limits…)

Avant de commencer, assurez-vous d’avoir :

  • Un chart Helm existant (ou helm create mon-chart)
  • Helm v3.8+ installé
  • Les outils de qualité installés :
Fenêtre de terminal
# Avec mise (recommandé)
mise use helm-docs kubeconform kube-linter helm-ct
pipx install yamale # Requis par ct
# Vérification
helm-docs --version # v1.14.2
kubeconform -v # v0.7.0
kube-linter version # 0.8.1
ct version # v3.14.0
SectionConceptDurée
values.schema.jsonValidation JSON Schema native10 min
helm-docsGénération automatique du README8 min
helm lintValidation syntaxique de base5 min
kubeconformValidation des manifestes K8s8 min
kube-linterBonnes pratiques sécurité8 min
ct lintValidation complète CI/CD10 min
ConventionsLabels, nommage, versioning6 min

Helm supporte nativement la validation des values via un fichier JSON Schema. Quand ce fichier existe, Helm refuse tout install, upgrade ou template si les values ne respectent pas le schema.

Placez values.schema.json à la racine du chart :

values.schema.json
{
"\$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["replicaCount", "image"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"description": "Nombre de réplicas (1-10)"
},
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": {
"type": "string",
"description": "Image Docker à déployer"
},
"tag": {
"type": "string",
"default": "latest",
"description": "Tag de l'image"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"],
"default": "IfNotPresent"
}
}
},
"service": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["ClusterIP", "NodePort", "LoadBalancer"],
"default": "ClusterIP"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 80
}
}
}
}
}
Fenêtre de terminal
# Values valides → succès
helm template test mon-chart/
Fenêtre de terminal
# replicaCount hors limites → erreur
helm template test mon-chart/ --set replicaCount=15
Error: values don't meet the specifications of the schema(s) in the following chart(s):
mon-chart:
- at '/replicaCount': maximum: got 15, want 10
Fenêtre de terminal
# Valeur enum invalide → erreur
helm template test mon-chart/ --set service.type=Invalid
Error: values don't meet the specifications of the schema(s) in the following chart(s):
mon-chart:
- at '/service/type': value must be one of 'ClusterIP', 'NodePort', 'LoadBalancer'
TypeUsageExemple
stringTexte"nginx"
integerNombre entier3
numberNombre décimal0.5
booleanVrai/fauxtrue
arrayListe["a", "b"]
objectObjet imbriqué{"key": "value"}
{
"replicaCount": {
"type": "integer",
"minimum": 1, // Valeur minimale
"maximum": 100 // Valeur maximale
},
"environment": {
"type": "string",
"enum": ["dev", "staging", "prod"] // Liste de valeurs autorisées
},
"image": {
"type": "string",
"pattern": "^[a-z0-9.-]+/[a-z0-9.-]+:[a-z0-9.-]+$" // Regex
},
"resources": {
"type": "object",
"additionalProperties": false // Interdit les clés non déclarées
}
}

helm-docs génère automatiquement un README.md à partir des commentaires dans values.yaml et du Chart.yaml.

Les commentaires commençant par # -- sont extraits pour la documentation :

values.yaml
# -- Nombre de réplicas du deployment
replicaCount: 1
image:
# -- Repository de l'image Docker
repository: nginx
# -- Tag de l'image (défaut: appVersion du Chart)
tag: ""
# -- Politique de téléchargement de l'image
# @default -- IfNotPresent
pullPolicy: IfNotPresent
service:
# -- Type de service Kubernetes
type: ClusterIP
# -- Port exposé par le service
port: 80
# -- Configuration des resources (requests/limits)
# @default -- Voir values.yaml
resources: {}
# -- Activation de l'autoscaling HPA
autoscaling:
# -- Activer/désactiver l'autoscaling
enabled: false
# -- Nombre minimum de réplicas
minReplicas: 1
# -- Nombre maximum de réplicas
maxReplicas: 100
Fenêtre de terminal
cd mon-chart/
helm-docs
INFO[2026-02-01T19:27:33+01:00] Found Chart directories [.]
INFO[2026-02-01T19:27:33+01:00] Generating README Documentation for chart .
README.md (extrait)
# mon-chart
![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational)
![Type: application](https://img.shields.io/badge/Type-application-informational)
![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational)
A Helm chart for Kubernetes
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| replicaCount | int | `1` | Nombre de réplicas du deployment |
| image.repository | string | `"nginx"` | Repository de l'image Docker |
| image.tag | string | `""` | Tag de l'image (défaut: appVersion du Chart) |
| image.pullPolicy | string | `"IfNotPresent"` | Politique de téléchargement de l'image |
| service.type | string | `"ClusterIP"` | Type de service Kubernetes |
| service.port | int | `80` | Port exposé par le service |
| resources | object | Voir values.yaml | Configuration des resources (requests/limits) |
| autoscaling.enabled | bool | `false` | Activer/désactiver l'autoscaling |

Créez un fichier README.md.gotmpl pour personnaliser le format :

README.md.gotmpl
# {{ .Name }}
{{ .Description }}
## Installation
\\`\\`\\`bash
helm install {{ .Name }} ./{{ .Name }}
\\`\\`\\`
## Configuration
{{ template "chart.valuesTable" . }}
## Maintainers
{{ template "chart.maintainersTable" . }}

helm lint est la première ligne de défense : il vérifie la syntaxe YAML, les templates Go, et le Chart.yaml.

Fenêtre de terminal
helm lint mon-chart/
==> Linting mon-chart/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed

Le flag --strict fait échouer le lint sur les warnings :

Fenêtre de terminal
helm lint mon-chart/ --strict
Fenêtre de terminal
helm lint mon-chart/ -f production-values.yaml

kubeconform valide que les manifestes générés sont conformes aux schemas Kubernetes.

Fenêtre de terminal
helm template test mon-chart/ | kubeconform -strict -summary
Summary: 4 resources found parsing stdin - Valid: 4, Invalid: 0, Errors: 0, Skipped: 0

Si un champ est mal nommé ou a le mauvais type :

Fenêtre de terminal
# Manifest avec erreur
helm template test mon-chart/ --set service.ports=80 | kubeconform -strict
stdin - Service test-mon-chart is invalid: problem validating schema:
port in body is required
Fenêtre de terminal
helm template test mon-chart/ | kubeconform -kubernetes-version 1.29.0 -strict -summary
OptionDescription
-strictÉchoue sur les propriétés inconnues
-summaryAffiche un résumé à la fin
-output jsonSortie JSON pour la CI
-kubernetes-version X.Y.ZValide pour une version K8s spécifique
-skip Kind1,Kind2Ignore certains types de ressources

kube-linter analyse les manifestes pour détecter les problèmes de sécurité et les mauvaises pratiques.

Fenêtre de terminal
helm template test mon-chart/ | kube-linter lint -
KubeLinter 0.8.1
<stdin>: (object: <no namespace>/test-mon-chart apps/v1, Kind=Deployment)
container "mon-chart" does not have a read-only root file system
(check: no-read-only-root-fs, remediation: Set readOnlyRootFilesystem
to true in the container securityContext.)
<stdin>: (object: <no namespace>/test-mon-chart apps/v1, Kind=Deployment)
container "mon-chart" is not set to runAsNonRoot
(check: run-as-non-root, remediation: Set runAsUser to a non-zero number
and runAsNonRoot to true in your pod or container securityContext.)
<stdin>: (object: <no namespace>/test-mon-chart apps/v1, Kind=Deployment)
container "mon-chart" has cpu request 0
(check: unset-cpu-requirements, remediation: Set CPU requests for your
container based on its requirements.)
Error: found 9 lint errors
CheckDescriptionRemediation
no-read-only-root-fsFilesystem root accessible en écritureAjouter readOnlyRootFilesystem: true
run-as-non-rootContainer s’exécute en rootAjouter runAsNonRoot: true
unset-cpu-requirementsPas de requests/limits CPUDéfinir resources.requests.cpu
unset-memory-requirementsPas de requests/limits memoryDéfinir resources.limits.memory
latest-tagImage utilise le tag latestUtiliser un tag spécifique
privilege-escalation-containerallowPrivilegeEscalation non définiAjouter allowPrivilegeEscalation: false

Créez un fichier .kube-linter.yaml pour ajuster les règles :

.kube-linter.yaml
checks:
# Désactiver certaines règles
exclude:
- "unset-cpu-requirements"
- "no-read-only-root-fs"
# Ou activer uniquement certaines règles
# include:
# - "run-as-non-root"
# - "latest-tag"
Fenêtre de terminal
helm template test mon-chart/ | kube-linter lint --config .kube-linter.yaml -

chart-testing (ct) est l’outil officiel pour valider les charts avant merge. Il combine helm lint, yamale (validation YAML), et vérifie les conventions.

Créez un fichier ct.yaml à la racine du repo :

ct.yaml
# Désactive la vérification des maintainers
validate-maintainers: false
# Désactive la vérification d'incrément de version
check-version-increment: false
# Chemin vers les charts
chart-dirs:
- charts

Téléchargez les schemas officiels :

Fenêtre de terminal
curl -sSL https://raw.githubusercontent.com/helm/chart-testing/main/etc/chart_schema.yaml -o chart_schema.yaml
curl -sSL https://raw.githubusercontent.com/helm/chart-testing/main/etc/lintconf.yaml -o lintconf.yaml
Fenêtre de terminal
ct lint --config ct.yaml --chart-yaml-schema chart_schema.yaml --lint-conf lintconf.yaml --charts mon-chart/
Linting charts...
------------------------------------------------------------------------------------------------------------------------
Charts to be processed:
------------------------------------------------------------------------------------------------------------------------
mon-chart => (version: "0.1.0", path: "mon-chart")
------------------------------------------------------------------------------------------------------------------------
Linting chart "mon-chart => (version: \"0.1.0\", path: \"mon-chart\")"
Validating mon-chart/Chart.yaml...
Validation success! 👍
==> Linting mon-chart
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
------------------------------------------------------------------------------------------------------------------------
✔︎ mon-chart => (version: "0.1.0", path: "mon-chart")
------------------------------------------------------------------------------------------------------------------------
All charts linted successfully

ct lint peut détecter automatiquement les charts modifiés :

Fenêtre de terminal
# Lint uniquement les charts modifiés par rapport à main
ct lint --target-branch main --config ct.yaml

Utilisez les labels standards définis par Kubernetes :

_helpers.tpl
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
LabelDescriptionExemple
app.kubernetes.io/nameNom de l’applicationnginx
app.kubernetes.io/instanceInstance uniquenginx-prod
app.kubernetes.io/versionVersion de l’application1.25.0
app.kubernetes.io/componentComposant dans l’applicationfrontend
app.kubernetes.io/part-ofApplication parentewordpress
app.kubernetes.io/managed-byOutil de gestionHelm
helm.sh/chartNom et version du chartnginx-1.0.0

Pattern recommandé : {{ .Release.Name }}-{{ .Chart.Name }}

{{- define "mychart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- \$name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name \$name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
ChampCe qu’il représenteQuand l’incrémenter
versionVersion du chart HelmModification des templates, values, schema
appVersionVersion de l’applicationMise à jour de l’image Docker
Chart.yaml
apiVersion: v2
name: mon-app
version: 1.2.0 # Chart modifié → +1 minor ou patch
appVersion: "3.5.1" # Nouvelle version de l'app → mettre à jour

  1. Créer un chart avec schema :

    Fenêtre de terminal
    helm create quality-demo
  2. Ajouter un values.schema.json avec au moins :

    • replicaCount : integer, min 1, max 10
    • service.type : enum ClusterIP/NodePort/LoadBalancer
    • image.repository : required
  3. Documenter avec helm-docs :

    • Ajouter les commentaires # -- dans values.yaml
    • Générer le README
  4. Valider avec kubeconform et kube-linter :

    Fenêtre de terminal
    helm template test quality-demo/ | kubeconform -strict -summary
    helm template test quality-demo/ | kube-linter lint -
  5. Corriger les erreurs kube-linter dans les templates

Critères de réussite :

  • Schema valide rejette replicaCount=15
  • README.md généré automatiquement avec tableau des values
  • kubeconform : 0 invalid
  • kube-linter : < 3 erreurs (après corrections)

OutilValideQuand l’utiliser
values.schema.jsonValues utilisateurToujours (natif Helm)
helm lintSyntaxe chartDéveloppement
helm-docsDocumentationAvant commit
kubeconformManifestes K8sCI/CD
kube-linterSécurité, bonnes pratiquesCI/CD
ct lintChart completCI/CD, avant merge

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.