Kubernetes Kubernetes

Configuration : ConfigMaps et Secrets

Gardez vos paramètres et identifiants hors de vos images, injectez-les dans les Pods et évitez les pièges courants.

14 min Intermédiaire

Si une URL de base de données ou un niveau de log est figé dans l’image de votre conteneur, le modifier oblige à reconstruire, publier et redéployer l’image. Kubernetes sépare la configuration des images grâce à deux objets : les ConfigMaps pour les paramètres ordinaires et les Secrets pour les valeurs sensibles. Ce guide montre comment créer les deux, comment les faire parvenir à un Pod, et ce que les Secrets protègent (ou non).

Créer un ConfigMap

Un ConfigMap est un ensemble de paires clé-valeur. Les valeurs peuvent être de courtes chaînes ou des fichiers entiers :

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  LOG_LEVEL: info
  API_URL: http://api.internal:8080
  motd.txt: |
    Welcome to the demo environment

Vous pouvez aussi en créer un depuis la ligne de commande, ce qui est pratique pour les fichiers :

kubectl create configmap web-config \
  --from-literal=LOG_LEVEL=info \
  --from-file=motd.txt
kubectl describe configmap web-config

Les ConfigMaps sont destinés à de petites données non sensibles (la limite est de 1 Mio) et appartiennent à un namespace. Un Pod ne peut utiliser que les ConfigMaps de son propre namespace.

L’utiliser comme variable d’environnement ou comme fichier

Un même Pod peut consommer un ConfigMap des deux manières :

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:1.25
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: web-config
              key: LOG_LEVEL
      volumeMounts:
        - name: config
          mountPath: /usr/share/nginx/html/motd.txt
          subPath: motd.txt
  volumes:
    - name: config
      configMap:
        name: web-config

Deux choses se passent ici. L’entrée env copie une clé dans une variable d’environnement. (envFrom permet d’importer toutes les clés d’un coup, mais les clés qui ne sont pas des noms de variables valides sont ignorées.) Les entrées volumes et volumeMounts montent le ConfigMap sous forme de fichiers, un fichier par clé.

Si vous montez le ConfigMap entier dans un répertoire comme ci-dessous, le répertoire est remplacé par les fichiers montés :

      volumeMounts:
        - name: config
          mountPath: /etc/web

C’est sans problème pour un répertoire qui vous appartient, mais cela masque tout ce qui se trouvait déjà à cet emplacement. Monter par-dessus un répertoire de configuration existant, comme /etc/nginx, peut casser l’application, et le conteneur peut entrer en boucle de plantage avec une erreur « file not found ». C’est le rôle de subPath, comme dans le premier exemple : il monte une seule clé comme un seul fichier et laisse le reste du répertoire intact. La contrepartie est que les fichiers montés avec subPath ne reçoivent pas les mises à jour ultérieures du ConfigMap.

Vérifiez ce qui est arrivé dans le conteneur :

kubectl exec web -- ls /usr/share/nginx/html
kubectl exec web -- env | grep LOG_LEVEL

Stocker des identifiants dans un Secret

Un Secret ressemble à un ConfigMap, mais il est destiné aux mots de passe, jetons et clés :

kubectl create secret generic db-credentials \
  --from-literal=username=app \
  --from-literal=password='S3cr3t-value'

En YAML, utilisez stringData pour écrire en clair et laisser Kubernetes l’encoder :

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: app
  password: S3cr3t-value

Si vous écrivez vous-même le champ encodé data, encodez avec echo -n 'value' | base64. Sans -n, le saut de ligne final fait partie du mot de passe et l’authentification échoue de manière déroutante. Un Secret se consomme comme un ConfigMap :

      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

Évitez de commiter dans Git des manifestes de Secrets contenant de vraies valeurs. Des outils comme Sealed Secrets, External Secrets Operator et SOPS existent pour que vous puissiez conserver des secrets chiffrés ou référencés dans un dépôt.

Savoir ce que les Secrets protègent, ou non

Base64 est un encodage, pas un chiffrement. Quiconque peut lire le Secret via l’API peut le décoder :

kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

Par défaut, les Secrets sont aussi stockés non chiffrés dans etcd. Pour les protéger réellement :

  • Restreignez qui peut lire les Secrets avec RBAC, et n’oubliez pas que quiconque peut créer un Pod dans un namespace peut monter ses Secrets.
  • Activez le chiffrement au repos d’etcd, ou utilisez la gestion de clés d’un fournisseur cloud.
  • Envisagez un gestionnaire de secrets externe si vos exigences sont strictes.

Les variables d’environnement sont pratiques mais fuient facilement : elles apparaissent dans kubectl exec ... env, dans les core dumps et parfois dans les logs. Monter un Secret sous forme de fichier est souvent un meilleur choix pour les valeurs sensibles.

Mettre à jour la configuration de façon maîtrisée

Les variables d’environnement ne sont lues qu’une fois, au démarrage du conteneur : un ConfigMap ou un Secret modifié n’atteint donc pas les Pods en cours d’exécution. Redémarrez-les pour prendre en compte le changement :

kubectl rollout restart deployment/web

Les fichiers issus d’un montage de répertoire entier finissent par se rafraîchir, mais beaucoup d’applications ne lisent leur configuration qu’au démarrage. Pour les déploiements automatisés, faites en sorte que le template du Pod change chaque fois que la configuration change. C’est à cela que servent les techniques de hash de configuration décrites dans le guide Argo CD avec Helm et Kustomize.

Guides populaires