Introduction
Les images de conteneurs doivent pouvoir être réutilisées dans différents environnements. Kubernetes sépare les paramètres non sensibles dans des ConfigMaps et les identifiants dans des Secrets, puis injecte ces objets sous forme de variables d’environnement ou de fichiers.
Vous allez configurer une même application avec ces deux mécanismes, inspecter le résultat en toute sécurité, mettre à jour la configuration et observer pourquoi les variables d’environnement et les volumes montés ne se rafraîchissent pas de la même manière.
L’objectif n’est pas seulement de créer deux objets d’API. Vous suivrez chaque valeur depuis son stockage dans Kubernetes jusqu’au conteneur en cours d’exécution, puis vous la modifierez afin d’observer l’influence du mécanisme de transmission sur le comportement de mise à jour.
Créer la configuration de l’application
Dans cette étape, créez un espace de noms et un ConfigMap à partir de valeurs littérales. Un ConfigMap stocke des clés de type chaîne sous data ; il ne les chiffre pas.
Commencez par vous rendre dans l’espace de travail du laboratoire. L’espace de noms regroupe tous les objets de cet exemple et évite qu’une configuration portant un nom similaire dans un autre laboratoire soit sélectionnée par erreur.
cd /home/labex/project/application-config
kubectl create namespace practice-config
kubectl create configmap app-settings -n practice-config --from-literal=APP_MODE=production --from-literal=LOG_LEVEL=info
kubectl get configmap app-settings -n practice-config -o yaml
Chaque --from-literal=KEY=VALUE ajoute une entrée. La sortie YAML rend les clés stockées visibles et convient aux configurations non sensibles.
Recherchez APP_MODE et LOG_LEVEL sous data:. Kubernetes stocke les valeurs souhaitées, mais rien ne les utilise encore ; la création d’un ConfigMap ne modifie pas un Pod à elle seule.
La partie pertinente de la sortie est volontairement en texte brut :
data:
APP_MODE: production
LOG_LEVEL: info
kind: ConfigMap
Des champs tels que uid, resourceVersion et creationTimestamp sont générés par Kubernetes et varieront. Pour cette étape, ne lisez que data, car il s’agit de la configuration que vous avez fournie.
Créer un Secret en toute sécurité
Dans cette étape, créez un Secret sans placer sa valeur dans un fichier YAML. Les données d’un Secret sont encodées en base64 dans l’API, mais elles ne sont pas chiffrées simplement parce que le type d’objet est Secret ; le contrôle d’accès RBAC et le chiffrement au repos restent essentiels.
generic signifie qu’il s’agit d’un Secret ordinaire composé de paires clé-valeur, et non d’un Secret TLS ou destiné à l’extraction d’images. Les guillemets autour du mot de passe empêchent le shell d’interpréter la ponctuation si la valeur contient ultérieurement des caractères spéciaux.
kubectl create secret generic app-credentials -n practice-config --from-literal=username=appuser --from-literal=password='practice-pass'
kubectl get secret app-credentials -n practice-config
kubectl get secret app-credentials -n practice-config -o jsonpath='{.data.username}' | base64 --decode; echo
Le dernier pipeline décode uniquement le nom d’utilisateur à des fins de démonstration. Évitez d’afficher les mots de passe dans les sorties opérationnelles courantes.
Le caractère | envoie le résultat encodé de JSONPath vers base64 --decode. Le point-virgule démarre un echo final, qui ajoute un saut de ligne après la valeur décodée afin que l’invite du shell suivante reste lisible. L’affichage de appuser montre le fonctionnement de l’encodage ; il rappelle également que base64 ne doit jamais être considéré comme une frontière de sécurité.
La commande intermédiaire affiche les métadonnées sans révéler aucune des deux valeurs :
NAME TYPE DATA AGE
app-credentials Opaque 2 ...
appuser
DATA=2 signifie que le Secret contient deux clés ; il ne s’agit pas d’un nombre d’octets. Opaque est le type d’API utilisé pour un Secret ordinaire composé de paires clé-valeur.
Injecter la configuration dans un Pod
Dans cette étape, utilisez les valeurs du ConfigMap comme variables d’environnement et montez le Secret sous forme de fichiers en lecture seule.
Avant de créer le Pod, distinguez les deux modes de transmission dans le manifeste :
envFromtransforme chaque clé du ConfigMap en variable d’environnement du conteneur.volumesrelie le Secret au Pod, tandis quevolumeMountschoisit l’emplacement où ce volume apparaît dans ce conteneur précis.
La boucle du Pod n’exécute aucun travail applicatif ; elle maintient simplement le conteneur en fonctionnement afin que vous puissiez inspecter les deux modes de transmission.
cd /home/labex/project/application-config
cat <<'EOF' > configured-app.yaml
apiVersion: v1
kind: Pod
metadata:
name: configured-app
namespace: practice-config
spec:
containers:
- name: app
image: busybox:1.36
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "while true; do sleep 30; done"]
envFrom:
- configMapRef:
name: app-settings
volumeMounts:
- name: credentials
mountPath: /etc/app-credentials
readOnly: true
volumes:
- name: credentials
secret:
secretName: app-credentials
EOF
Appliquez le manifeste et attendez que le Pod soit prêt :
kubectl apply -f configured-app.yaml
kubectl wait --for=condition=Ready pod/configured-app -n practice-config --timeout=60s
Inspectez maintenant chaque mode de transmission depuis l’intérieur du conteneur. printenv lit l’environnement du processus, tandis que ls affiche les fichiers projetés par le volume Secret :
kubectl exec configured-app -n practice-config -- printenv APP_MODE LOG_LEVEL
kubectl exec configured-app -n practice-config -- ls -l /etc/app-credentials
envFrom importe toutes les clés du ConfigMap. Le volume Secret crée un fichier par clé et est marqué en lecture seule à l’intérieur du conteneur.
La sortie attendue des variables d’environnement est production, suivie de info. Le répertoire doit contenir des fichiers nommés username et password ; les noms de fichiers correspondent aux clés du Secret.
Dans le conteneur réel, l’affichage ressemble à ceci :
production
info
total 0
lrwxrwxrwx ... password -> ..data/password
lrwxrwxrwx ... username -> ..data/username
Les entrées sont des liens symboliques gérés par Kubernetes. Cette indirection permet à kubelet de basculer le contenu monté vers une nouvelle version sans réécrire un fichier pendant que l’application le lit. Votre application peut néanmoins ouvrir les chemins habituels /etc/app-credentials/username et /etc/app-credentials/password.
Mettre à jour les données du Secret monté
Dans cette étape, mettez à jour le Secret et observez le fichier monté. kubectl create ... --dry-run=client -o yaml | kubectl apply -f - génère localement le YAML souhaité et l’envoie directement à apply ; - signifie l’entrée standard.
Ce modèle est utile pour mettre à jour un objet généré de manière impérative sans le supprimer. La partie gauche affiche un manifeste Secret complet et la partie droite l’envoie au serveur d’API.
kubectl create secret generic app-credentials -n practice-config --from-literal=username=serviceuser --from-literal=password='rotated-pass' --dry-run=client -o yaml | kubectl apply -f -
Comme le Secret a initialement été créé avec kubectl create, la première mise à jour peut afficher l’avertissement suivant avant la ligne de réussite :
Warning: resource secrets/app-credentials is missing the kubectl.kubernetes.io/last-applied-configuration annotation ...
secret/app-credentials configured
Cet avertissement indique que apply ne dispose d’aucun instantané déclaratif précédent auquel comparer la nouvelle configuration. Kubernetes ajoute automatiquement l’annotation lors de cette mise à jour ; configured confirme donc la réussite. Il n’est pas nécessaire de supprimer et de recréer le Secret.
Kubelet met à jour de manière asynchrone le contenu projeté du volume Secret. Effectuez des vérifications répétées jusqu’à l’apparition du nouveau nom d’utilisateur :
current_username=""
for attempt in $(seq 1 24); do
current_username=$(kubectl exec configured-app -n practice-config -- cat /etc/app-credentials/username)
if [ "$current_username" = "serviceuser" ]; then
break
fi
sleep 5
done
echo "$current_username"
test "$current_username" = "serviceuser"
La boucle réessaie pendant deux minutes au maximum, car la projection du volume est finalement cohérente. Dans l’environnement testé, l’objet de l’API a été modifié immédiatement, mais le fichier monté a mis plus d’une minute à se rafraîchir.
$(...) est une substitution de commande : le shell exécute seq 1 24 et utilise les nombres obtenus comme valeurs de boucle. À chaque itération, le contenu actuel du fichier est stocké dans current_username ; break s’arrête dès la première correspondance et sleep 5 évite d’interroger l’API en continu. Le test final renvoie un échec si la valeur n’a jamais changé ; la commande ne peut donc pas continuer silencieusement avec des données obsolètes. La sortie attendue est serviceuser.
Actualiser la configuration d’environnement
Dans cette étape, mettez à jour LOG_LEVEL et comparez sa valeur avec celle du Pod en cours d’exécution. Les variables d’environnement sont définies au démarrage du conteneur ; modifier le ConfigMap ne réécrit donc pas l’environnement du processus actuel.
Le correctif de fusion ne modifie qu’une entrée sous data. La lecture immédiate du processus en cours montre une différence importante par rapport à un volume monté :
kubectl patch configmap app-settings -n practice-config --type=merge -p '{"data":{"LOG_LEVEL":"debug"}}'
kubectl exec configured-app -n practice-config -- printenv LOG_LEVEL
Le Pod affiche toujours info. Recréez-le à partir du manifeste enregistré afin qu’il utilise la nouvelle valeur :
configmap/app-settings patched
info
Ce résultat est attendu et ne signale pas l’échec du correctif : le ConfigMap contient bien debug, mais le processus déjà en cours utilise toujours l’environnement créé au démarrage.
cd /home/labex/project/application-config
kubectl delete pod configured-app -n practice-config --wait=true
kubectl apply -f configured-app.yaml
kubectl wait --for=condition=Ready pod/configured-app -n practice-config --timeout=60s
kubectl exec configured-app -n practice-config -- printenv LOG_LEVEL
Le Pod de remplacement affiche debug. C’est pourquoi les charges de travail de production déclenchent généralement un déploiement après une modification de configuration fondée sur des variables d’environnement.
pod "configured-app" deleted
pod/configured-app created
pod/configured-app condition met
debug
Le manifeste fait toujours référence au ConfigMap par son nom ; aucune valeur n’a été copiée dans configured-app.yaml. La recréation du Pod oblige kubelet à résoudre à nouveau cette référence et à injecter les dernières données.
Résumé
Vous avez séparé les paramètres de l’application et les identifiants de l’image, utilisé les données d’un ConfigMap comme variables d’environnement, monté les données d’un Secret sous forme de fichiers, renouvelé un Secret projeté et recréé un Pod afin d’actualiser une configuration fondée sur des variables d’environnement.


