Configuration
Variables d’environnement
Quand nous avons besoin de lancer une application en container sur Kubernetes, il est rare d’avoir toute la configuration écrite de manière statique dans l’image du container. On pourrait. Mais ça veut dire que dès qu’on veut lancer l’application en changeant un de ses paramètres, par exemple : si elle tourne sur un cluster de production ou hors production où on n’aura pas le même affichage d’une page web quels sont les identifiants qu’elle doit utiliser pour se connecter à une base de données Il faudrait reconstruire toute l’image avec ce paramètre écrit de manière statique. Et donc on aurait besoin d’autant d’images de container que de combinaisons de paramètres de l’application. Ce qui est très lourd à maintenir.
Au lieu de ça, on préfère externaliser la configuration et ne laisser dans l’image du container que la partie traitement de l’application. Une manière de lui passer sa configuration est d’utiliser des variables d’environnement. lien vers le répo github lié à cette section
Essayons de créer un pod avec une image busybox standard et de lui passer une variable d’environnement qui s’appelle “PLANET” et qui a comme valeur “blue”. Le YAML est le suivant :
apiVersion: v1
kind: Pod
metadata:
labels:
run: envvar
name: envvar
spec:
containers:
- image: busybox:latest
name: envvar
args:
- sleep
- "3600"
env:
- name: PLANET
value: "blue"
On lance le pod dans notre cluster :
$ kubectl apply -f envvar.yaml
Et pour exécuter une commande à l’intérieur du pod, on utilise la commande exec :
$ kubectl exec envvar -- env | grep PLANET
PLANET=blue
Si on veut changer la variable d’environnement pour qu’elle ait comme valeur red, on pourrait changer la valeur dans le YAML de départ, et relancer le pod. Il n’y a pas besoin de changer l’image.
ConfigMap
Dans certaines petites applications, on a juste besoin de passer quelques variables d’environnement à l’application pour qu’elle fasse ce qu’on veut. Mais dans des applications plus complexes, on peut avoir beaucoup de variables d’environnement et plusieurs fichiers de configuration à passer.
Les ConfigMaps sont les objets Kubernetes qui nous permettent d’injecter toute la configuration nécessaire à l’application pour qu’elle tourne comme on veut.
Prenons un exemple simple. On veut passer deux variables d’environnement à l’application via une ConfigMap. On commence par créer la ConfigMap qui aura pour nom space :
$ kubectl create configmap space --from-literal=planet=blue --from-literal=moon=white
configmap/space created
On peut également la créer en tant que fichier YAML puis faire un apply :
apiVersion: v1
kind: ConfigMap
metadata:
name: space
data:
planet: blue
moon: white
Ensuite, on va créer un pod à qui on va passer des variables d’environnement à partir de cette ConfigMap :
apiVersion: v1
kind: Pod
metadata:
labels:
run: configenvvar
name: configenvvar
spec:
containers:
- image: busybox:latest
name: configenvvar
args:
- sleep
- "3600"
env:
- name: PLANET
valueFrom:
configMapKeyRef:
name: space
key: planet
- name: MOON
valueFrom:
configMapKeyRef:
name: space
key: moon
On lance ce pod et on regarde s’il a bien reçu ses variables d’environnement :
$ kubectl apply -f configenvvar.yaml
$ kubectl exec configenvvar -- env | grep -E "PLANET|MOON"
PLANET=blue
MOON=white
Secret
Une partie de la configuration des applications peuvent être sensibles, comme des mots de passe pour accéder à une base de données, ou une clé d’API pour accéder à un service externe. Ce genre de configuration ne passe pas dans les ConfigMap mais les objets Secret de k8s, qui ont besoin de droits de lecture et d’écriture spécifiques pour y accéder.
Nous allons créer un Secret qui contient un username et un password :
$ echo -n 'admin' > username
$ echo -n 'admin-pass' > password
$ kubectl create secret generic admin-creds --from-file=username --from-file=password
Il y a plusieurs types de secret dont la liste est ici : secret types
Dans notre cas, nous avons utilisé le type de Secret standard, par défaut, sans contraintes qui s’appelle Opaque.
Si nous essayons de le lire avec nos droits d’administrateur, nous allons voir ça :
apiVersion: v1
kind: Secret
metadata:
name: admin-creds
type: Opaque
data:
username: YWRtaW4=
password: YWRtaW4tcGFzcw==
Les valeurs des secrets sont encodées en base64 (admin en base64 donne YWRtaW4=). L’encodage ne constitue pas un moyen de protection des secrets. Pour empêcher les utilisateurs de voir les secrets, il ne faut pas qu’ils aient les droits nécessaires, qui ne devraient être attribués qu’aux administrateurs et aux utilisateurs privilégiés.
Maintenant que le Secret est créé, on peut créer un pod qui va charger ses valeurs en tant que variables d’environnement :
apiVersion: v1
kind: Pod
metadata:
labels:
run: secretenv
name: secretenv
spec:
containers:
- image: busybox:latest
name: secretenv
args:
- sleep
- "3600"
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: admin-creds
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: admin-creds
key: password
On lance le pod et on s’assure qu’il détient bien les secrets :
$ kubectl apply -f secretenv.yaml
$ kubectl exec secretenv -- env | grep -E "USERNAME|PASSWORD"
USERNAME=admin
PASSWORD=admin-pass
Conclusion
Les variables d’environnement sont un moyen très usuel de passer une partie de la configuration des applications. Nous pouvons le faire directement dans la description YAML du pod (ou du deployment ou autre workload), ou nous pouvons le faire à partir d’une ConfigMap, ou d’un Secret quand il s’agit de données sensibles.
Il se trouve que beaucoup d’applications ne se suffisent pas de variables d’environnement, mais ont besoin de longs fichiers de configuration pour avoir l’ensemble de leurs paramètres. Nous utilisons toujours des ConfigMap et des Secrets pour les construire, mais nous ne pouvons pas les passer en tant que variables d’environnements. Nous avons besoin de les monter en tant que fichier dans l’arborescence du pod pour que l’application puisse les lire. Pour cela, nous avons besoin de comprendre la notion de volumes, ce qui sera l’objet du prochain article.