Introduction
Lorsque vous construisez votre pile d'application pour fonctionner sur Kubernetes, généralement la configuration de base du pod est généralement effectuée en définissant différentes variables d'environnement. Si par exemple si vous souhaitez créer un pod utilisant l'image wordpress, vous aurez alors besoin de surcharger certaines variables d'environnement, tels que l'adresse, l'utilisateur et le mot de passe de la base de données. Par ailleurs vous souhaiteriez peut-être aussi protéger le mot de passe de la base de données en évitant de l'afficher en clair. De plus, parfois, vous voudriez aussi définir tout un ensemble de variables d'environnement pouvant être partagées par plusieurs conteneurs de votre pods.
Afin de répondre à tous ces besoins, nous verrons donc, dans cet article, les différentes façons d'injecter des variables d'environnement dans les applications Kubernetes.
Les types de variables d'environnement
Le mode basique
Lorsque vous créez un pod, vous pouvez définir des variables d'environnement pour les conteneurs qui s'exécutent dans le pod en incluant le champ env dans le fichier de configuration du pod.
Dans cet exercice, nous créerons un pod qui exécute un conteneur basé sur l'image alpine contenant une variable d'environnement nommé DEMO_ENV et sa valeur "Coucou". Voici donc à quoi va ressembler le fichier de configuration du pod :
apiVersion: v1
kind: Pod
metadata:
name: alpine
spec:
containers:
- name: alpine
image: alpine
env:
- name: DEMO_ENV
value: "Coucou"
command: ["/bin/sh", "-c"]
args:
- while true; do
sleep 1;
done
Créez ensuite le pod basé sur le fichier de configuration YAML:
kubectl apply -f pod.yaml
Par la suite, nous allons exécuter la commande printenv afin de répertorier les variables d'environnement disponible dans le conteneur de notre pod :
kubectl exec -it alpine -- printenv | grep DEMO_ENV
Résultat :
DEMO_ENV=Coucou
ConfigMap
Dans le cas où vous utilisez les mêmes variables d'environnement dans plusieurs pods, il est plus intéressant de songer à utiliser l'objet ConfigMap.
Comment ça marche ? Premièrement vous définissez vos clés et vos valeurs votre objet ConfigMap et deuxièmement vous les réutilisez dans le champ env de vos Pods. Dès lors, quand vous souhaiteriez modifier la valeur votre variable d'environnement, vous n'aurez plus qu'à modifier sa valeur que dans votre ConfigMap. À l'inverse du mode basique, où vous auriez rectifié la valeur de votre variable d'environnement pour chaque pod.
Pour créer une ConfigMap, soit vous utilisez soit la commande kubectl soit un fichier yaml.
Depuis la commande kubectl
Voici à quoi ressemble le prototype de la commande de création de ConfigMap avec l'outil kubectl :
kubectl create configmap <confiMap-name> --from-literal=<key>=<value>
Dans cet exemple nous allons créer une ConfigMap nommée maconfigmap avec comme clé db-name et comme valeur test, soit :
kubectl create configmap maconfigmap --from-literal=db-name=test
Voici la commande qui permet de lister les ConfigMaps disponibles dans votre cluster :
kubectl get configmaps
Résultat :
NAME DATA AGE
maconfigmap 1 2m26s
Si vous désirez divulguer la valeur de votre configMap, on exécutera la commande ci-dessous :
kubectl describe configmap maconfigmap
Résultat :
...
Data
====
db-name:
----
test
Events: <none>
Si vous avez plusieurs clés et valeurs à déclarer, il serait plus intéressant de rajouter toutes les données dans un fichier. Pour ce faire, créez un fichier values.txt et complétez les données suivantes :
cle-un:valeur-un
cle-deux:valeur-deux
Pour utiliser notre fichier, nous utiliserons l'argument --from-file, comme suit :
kubectl create configmap maconfigmap2 --from-file=values.txt
Vérifions les données de notre configmap2 :
kubectl describe configmaps maconfigmap2
Résultat :
...
Data
====
values.txt:
----
cle-un:valeur-un
cle-deux:valeur-deux
Events: <none>
Utilisation d'un fichier YAML
L'autre manière de créer votre configMap, est d'utiliser un fichier YAML. Dans cet exemple, nous créerons une ConfigMap avec comme clé ma-cle et comme valeur ma-valeur.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
ma-cle: ma-valeur
Créez ensuite votre objet ConfigMap avec la commande suivante :
kubectl create -f configmapc.yaml
Enfin, vérifions si notre nouveau ConfigMap est bien disponible dans notre cluster :
kubectl get configmaps
Résultat :
NAME DATA AGE
maconfigmap 1 20m
maconfigmap2 1 13m
test-config 1 3m26s
Utiliser votre ConfigMap dans une variable d'environnement
Dans le fichier YAML de notre pod, nous aurons besoin du champ valueFrom afin de récolter une valeur depuis un objet Kubernetes et le champ configMapKeyRef pour indiquer à notre master qu'on souhaite récolter cette valeur depuis un objet de type ConfigMap, ce qui nous donne le fichier YAML suivant :
apiVersion: v1
kind: Pod
metadata:
name: alpine
spec:
containers:
- name: alpine
image: alpine
env:
- name: DB-NAME
valueFrom:
configMapKeyRef:
name: maconfigmap
key: db-name
command: ["/bin/sh", "-c"]
args:
- while true; do
sleep 1;
done
Créez votre pod à l'aide de la commande suivante :
kubectl create -f pod.yaml
Affichons dorénavant, la valeur de notre variable d'environnement DB-NAME en exécutant la commande printenv :
kubectl exec -it alpine -- printenv | grep DB-NAME
Résultat :
DB-NAME=test
les Secrets
Les Secret sont des objets Kubernetes qui vous permettent de stocker et de gérer des informations sensibles, telles que des mots de passe, des jetons OAuth et des clés ssh.
Le fonctionnement est similaire à l'objet ConfigMap, vous déclarez vos données dans un objet de type Secret et par la suite vous les réutilisez dans votre Pod.
Depuis la commande kubectl
Comme pour l'objet ConfigMap, vous pouvez utiliser la commande kubectl de façon à créer votre objet Secret.
Voici le prototype de la commande kubectl :
kubectl create secret generic <secret-name> --from-literal=<key>=<value>
Dans cet exemple nous allons créer un secret nommé monsecret avec comme clé db-password et comme valeur passw0rd, soit :
kubectl create secret generic monsecret --from-literal=db-password=passw0rd
Voici la commande qui permet de lister les secrets disponibles dans votre cluster est la suivant :
kubectl get secrets
Résultat :
NAME TYPE DATA AGE
default-token-5hwrq kubernetes.io/service-account-token 3 12m
monsecret Opaque 1 9s
Pour information, les données secrètes sont codées en base64, vous pouvez récupérer le hash depuis la commande suivante :
kubectl get secrets monsecret -o yaml
Résultat :
apiVersion: v1
data:
db-password: cGFzc3cwcmQ=
...
Une fois le hash récupéré (ici cGFzc3cwcmQ=), décodez votre secret avec la commande suivante :
echo "cGFzc3cwcmQ=" | base64 -d
Résultat :
passw0rd
Information
Le codage base64 n’est pas une méthode de chiffrement et est considéré comme identique au texte brut.
Comme pour les ConfigMaps, vous pouvez utiliser un fichier pour rajouter toutes vos données secrètes. Dans cet exemple nous allons créer un fichier nommé login.txt contenant les données suivantes :
db-user:admin
db-password:p@ssw0rd
Pour créer notre objet secret depuis un fichier, nous utiliserons cette fois-ci l'argument --from-file, comme suit :
kubectl create secret generic loginsecret --from-file=login.txt
Récupérons la liste des secrets de notre cluster :
kubectl get secrets
Notre nouveau secret est bien présent :
NAME TYPE DATA AGE
default-token-5hwrq kubernetes.io/service-account-token 3 53m
loginsecret Opaque 1 2m59s
monsecret Opaque 1 41m
Utilisation d'un fichier YAML
Dans cet exemple, nous créerons à l'aide d'un fichier yaml, un objet secret nommé db-login avec deux clés, soit la clé db-user avec la valeur root et la clé db-password avec la valeur p@ssw0rd.
Avant toute chose, nous devons d'abord convertir nos valeurs en base64, soit :
echo "root" | base64 && echo "p@ssw0rd" | base64
Résultat :
cm9vdAo=
cEBzc3cwcmQK
Une fois le hash obtenu, on peut commencer par la création de notre template YAML :
apiVersion: v1
kind: Secret
metadata:
name: db-login
data:
db-user: cm9vdAo=
db-password: cEBzc3cwcmQK
Créez ensuite votre secret à l'aide la commande suivante :
kubectl create -f secret.yaml
Avant d'utiliser notre objet secret dans un pod, vérifions d'abord si il est bien disponible dans notre cluster :
kubectl get secrets
c'est bien le cas :
NAME TYPE DATA AGE
default-token-5hwrq kubernetes.io/service-account-token 3 53m
loginsecret Opaque 1 2m59s
monsecret Opaque 1 45m
db-login Opaque 2 53s
Utiliser votre secret dans une variable d'environnement
apiVersion: v1
kind: Pod
metadata:
name: alpine
spec:
containers:
- name: alpine
image: alpine
env:
- name: DB-USER
valueFrom:
secretKeyRef:
name: db-login
key: db-user
- name: DB-PASSWORD
valueFrom:
secretKeyRef:
name: db-login
key: db-password
command: ["/bin/sh", "-c"]
args:
- while true; do
sleep 1;
done
Créez votre pod :
kubectl create -f pod.yaml
Dorénavant, affichons les valeurs de nos variables d'environnement DB-USER et DB-PASSWORD en exécutant la commande printenv :
kubectl exec -it alpine -- printenv | grep 'DB-USER\|DB-PASSWORD'
Résultat :
DB-USER=root
DB-PASSWORD=p@ssw0rd
Conclusion
Kubernetes nous offre selon nos besoins différentes façon de gérer et manipuler nos variables d'environnement. Pour vous exercez essayez de créer un Pod basé sur l'image wordpress.
Voici un aide-mémoire des commandes liées aux variables d'environnement de Kubernetes :
################# ConfigMap ################
# Créer d'une ConfigMap
kubectl create configmap <confiMap-name> --from-literal=<key>=<value>
# Créer d'un ConfigMap depuis un fichier
kubectl create configmap <confiMap-name> --from-file=<filaname>
# Lister les ConfigMaps
kubectl get configmaps
# Récupérer la valeur d'une ConfigMap
kubectl describe configmap <confiMap-name>
################# Secrets ################
# Créer d'un Secret
kubectl create secret generic <secret-name> --from-literal=<key>=<value>
# Créer d'un Secret depuis un fichier
kubectl create secret generic <secret-name> --from-file=<filaname>
# Lister les Secrets
kubectl get secrets
# Récupérer la valeur d'un Secret
kubectl get secrets <confiMap-name> -o yaml
echo "<hash>" | base64 -d
# Convertir une valeur en base64
echo "<value>" | base64
Espace commentaire
Écrire un commentaire
Rejoignez la discussion
Vous devez être connecté pour poster un message.
27 commentaires
Si quelqu'un galère encore avec le mapping, vérifiez que le nom de l'objet dans
secretKeyRefpointe bien vers lemetadata.namedu secret créé, pas le nom du fichier utilisé lors de la création.Non, pas directement. Tu dois passer par un
subPathdans la définition du volume pour renommer le fichier monté à partir du secret.Est-ce qu'on peut utiliser une valeur de secret comme nom de fichier ?
La version BSD de
base64sur Mac est plus tatillonne. Utilisebase64 -Davec un grand D au lieu de-d.J'ai une erreur de syntaxe quand je fais mon
base64 -dsur Mac. C'est quoi la différence avec Linux ?Le YAML est préférable pour la versionning (GitOps). Ne laisse jamais traîner des secrets en clair dans tes fichiers de config, utilise SealedSecrets pour les chiffrer dans ton repo.
Sympa l'article. Pour les secrets, vous conseillez de les créer en YAML ou en ligne de commande ?
Oui, tu peux inspecter le manifeste du pod directement :
kubectl get pod ton-pod -o yamlet regarde la sectionenv.Existe-t-il une commande pour lister toutes les variables d'un pod sans entrer dedans ?
Vérifie que ton processus principal n'écrase pas les variables. Si tu as un script d'entrée, il peut très bien supprimer les variables d'environnement.
Je n'arrive pas à voir les variables dans mon conteneur avec
printenv, pourtant elles sont bien déclarées dans le YAML.La limite est de 1Mo. Si ton fichier dépasse, ne mets pas ça dans une ConfigMap, utilise un volume persistant ou un objet spécifique (CRD).
C'est quoi la limite de taille pour une ConfigMap ? Je voulais injecter un gros fichier de conf JSON dedans.
Si ton pod utilise un service account spécifique, il faut vérifier qu'il a bien les permissions
getsur les secrets via unRoleBinding.J'ai une erreur
Forbiddenquand je tente d'accéder aux secrets depuis mon pod. C'est un souci de droits ?Oui, utilise
envFromau lieu deenv. Ça charge tout d'un coup.Merci pour le tuto, c'est clair. Par contre, est-ce qu'on peut injecter toutes les clés d'une ConfigMap d'un coup au lieu de les lister une par une ?
Ah, le classique
echosur PowerShell ajoute souvent un retour à la ligne automatique qui corrompt le hash.Essaie ça :
J'ai essayé de créer un secret avec
echomais ça me sort une erreur de base64 invalide. Je suis sous Windows/PowerShell.C'est normal, les variables d'environnement sont injectées au démarrage du conteneur. Elles ne sont pas dynamiques.
Si tu veux du rafraîchissement à chaud, il faut monter la ConfigMap en tant que
volumedans ton pod, pas en variable d'environnement.Comment je fais pour mettre à jour une ConfigMap sans redémarrer le pod ? Les variables ne semblent pas se rafraîchir à chaud.
Probablement un souci de format dans ton fichier. Vérifie qu'il n'y a pas de caractères invisibles ou de retours à la ligne en trop.
Utilise
cat -A ton-fichier.txtpour voir les caractères spéciaux.J'ai une erreur bizarre lors de l'injection d'un fichier via
--from-file, mon app ne récupère pas les bonnes valeurs. Ça vient de quoi ?Le base64 n'est pas du chiffrement, comme précisé dans l'article. En prod, à ne jamais faire sans chiffrement au repos.
Regarde du côté de SealedSecrets ou Hashicorp Vault pour gérer ça proprement.
Super article. Par contre, pour les Secrets, le base64 c'est vraiment pas ouf niveau sécurité si quelqu'un a accès au cluster. Vous utilisez quoi en prod pour chiffrer ça ?