Skip to content

Niveau 3 : Orchestration de conteneurs avec Rancher et Kubernetes.

Auteurs

© CNRS 2020

Assemblé et rédigé par Martin Souchal, d'après les travaux de Remi Cailletaud.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Introduction

Kubernetes

Déployer un cluster Kubernetes n'est pas une chose aisée. Rancher est un produit indépendant de la CNCF qui permet de déployer simplement un cluster Kubernetes avec une surcouche propre à Rancher, tout en assurant la compatibilité avec les commandes Kubernetes traditionnelles. Rancher est un produit OpenSource maintenu par la société Rancher Labs.

Ecosystème

CNCFlandscape

Version interactive

Déploiement d'un cluster Kubernetes avec Rancher

Un peu de vocabulaire

  • Node: Serveur physique ou virtuel.
  • Cluster: Groupe de noeuds.
  • Control plane: Noeud qui contrôle les autres noeuds.
  • Persistent Volumes: Volumes disponibles.
  • Persistent Volume Claims: Volume necessaire.
  • Pods: Une description d'un ensemble de conteneurs et les volumes qu'ils utilisent.
  • Deployment: Un ensemble de pods.
  • Service: Une description du réseau utilisé par les déploiements.

Kubernetes fait tourner des applications en les placant dans des conteneurs rassemblés dans des Pods qui eux mêmes tournent sur des noeuds. Un noeud peut être une machine virtuelle ou physique, et un cluster peut contenir les deux types de noeuds. Le control plane est un noeud chargé de gérer le cluster. En général un cluster Kubernetes comprends plusieurs noeuds pour être efficace. Dans un environnement de test ou de formation, on peut créer un cluster Kubernetes eavec une seule machine.

Un noeud comprends le kubelet, une runtime de conteneur et un kube-proxy.

Rancher a besoin d'un rancher server (pour la gestion du cluster Rancher) et d'un service etcd (base de donnée simple clé valeur) en plus du control plane et de workers kubernetes.

1. Rancher Server

Pour déployer le Rancher Server, il faut utiliser la commande suivante :

$ sudo docker run -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher

2. Working nodes

Pour déployer les working nodes Kubernetes sur un cluster de machine virtuelles il faut utiliser l'UI de Rancher : alller dans Modifier le cluster et dans la partie Customize Node Run Command choisir les rôles à assigner et faire un copier coller de la commande générée. Par exemple :

sudo docker run -d --privileged --restart=unless-stopped --net=host -v /etc/kubernetes:/etc/kubernetes -v /var/run:/var/run rancher/rancher-agent:v2.4.5 --server https://apcrancher.in2p3.fr --token grxjmgqd84pq766v9qqvcfhn6gm7s9pz9zxgt6fzhrbpvkjhh2986b --ca-checksum e32baedc4d9b69d3f5ba997bf49e6ac6943bbc248fa7dd27792fcbe68cd1c147 --worker

Avec au moins un worker, un etcd et un control plane on a une infrastructure Rancher fonctionnelle, sur laquelle on peut déployer des applications.

Déploiement d'une application

1. Installation et configuration de kubectl

2. Premiers pas

Afin de bien comprendre le fonctionnement, tous les participants sont administrateurs du cluster. En revanche, chacun se voit attribuer un Namespace afin de bien isoler les différentes ressources.

Afin de vérifier que vous accédez au cluster, lancez :

$ kubectl version

3. Namespaces

Listez les différents namespaces :

$ kubectl get namespaces

Kubernetes supporte plusieurs clusters virtuels au sein d'un même cluster physique. Ces clusters virtuels sont appellés namespaces. Les namespaces sont prévus pour être utilisés dans des environnements avec beaucoup d'utilisateurs et de projets différents.

Ici l'intêret d'utiliser des namespaces différents est de pouvoir avoir des noms de pods identiques, ce qui n'est pas possible dans un même namespace.

Kubernetes démarre avec 4 namespaces initiaux :

  • default : Le namespace par défaut pour les objets crées sans namespaces particuliers
  • kube-system : Namespace pour les objets crées par le système Kubernetes
  • kube-public : Ce namespace est créé automatiquement et est lisible par tous les utilisateurs (y compris ceux qui ne sont pas authentifiés). Ce namespace est principalement réservé à l'utilisation du cluster, au cas où certaines ressources devraient être visibles et lisibles publiquement dans l'ensemble du cluster. L'aspect public de ce namespace n'est qu'une convention, pas une exigence.
  • kube-node-lease : Ce namespace pour les objets de location associés à chaque nœud améliore la performance du heartbeat des nœuds au fur et à mesure que la grappe s'étend.

Listez les pods dans le namespace actuel puis dans tous les namespaces :

$ kubectl get pods
$ kubectl get pods --all-namespaces

Maintenant créez votre propre namespace :

$ kubectl create namespace <namespace name>

Pour configurer le namespace par défaut que vous allez utiliser, modifiez la configuration :

kubectl config set-context --current --namespace=<insert-namespace-name-here>

4. Monitoring

Vérifiez l'usage des ressources par pod :

$ kubectl top pods --all-namespaces

5. Déploiement d'un conteneur

Enfin, lancez un pod, depuis une image debian, et lancez-y un shell :

$ kubectl run --rm --restart=Never -i -t --image debian test /bin/bash

Avec la commande kubectl get pods -o wide, vérifiez sur quel noeud le pod s'éxecute. Avec la commande kubectl get pods -o yaml, récupérez le fichier de configuration du pod.

Application

Nous allons lancer trois pods: un pour Mariadb, un pour l'application Django, et un pour notre frontal NGINX. Ces pods seront contrôlés par des Deployments, ce qui permet de les relancer automatiquement en cas de défaillance.

1. Mariadb

Premier déploiement

Astuce: pour générer les fichier yaml facilement, vous pouvez utiliser la commande kubectl avec les options --dry-run -o yaml :

$ kubectl create deployment mariadb --image mariadb --dry-run -o yaml > mariadb.yml

Lancez le déploiement (1 réplicas) :

$ kubectl apply -f mariadb.yml

Surveillez l'état du pod :

$ kubectl get pods -w

Debug

Le pod mariadb-* produit une erreur. Pour la comprendre :

$ kubectl describe pod mariadb-*

L'erreur est Back-off restarting failed container, ce qui signifie que le pod redémarre sans cesse. Pour examiner plus précisément l'activité d'un conteneur du pod :

$ kubectl logs mariadb-*

On voit qu'il manque des options (variables d'environnement) pour le que conteneur mariadb démarre correctement. Définissez les variables MYSQL_ROOT_PASSWORD, MYSQL_USER, MYSQL_PASSWORD et MYSQL_DATABASE dans le fichier mariadb.yml à l'aide du champ env.

Dans la suite, nous verrons comment éviter les secrets en clair... Si nous avons le temps ;)

Une fois les variables définies, réappliquez les changements et vérifiez que le pod tourne :

$ kubetl apply -f mariadb.yml
$ kubectl get pods -w

Tests

Pour tester facilement que le pod mariadb répond correctement, et pour les futurs debug, vous pouvez utiliser la commande kubectl port-forward :

$ kubectl port-forward mariadb-* 3306:3306
$ telnet localhost 3306

Nous avons vu que les pods et leur configuration réseau sont temporaires. Afin de les exposer de manière pérenne, il faut mettre en place un service. On utilise la même astuce du dry-run pour générer le fichier :

$ kubectl expose deployment mariadb --port=3306 --target-port=3306 --dry-run -o yaml > mariadb-service.yml
$ kubectl apply -f mariadb-service.yml

Ainsi, le nom DNS mariadb pointera vers le pod correspondant. Vous pouvez créer un pod temporaire afin de vérifier la résolution DNS, et le bon fonctionnement du serveur mariadb.

$ kubectl run --rm --restart=Never -i -t --image busybox test /bin/sh
$ telnet mariadb 3306

2. Django

Secret Kubernetes

Afin de déployer l'application directement depuis la registry Gitlab, il faut pouvoir s'identifier sur le registre privé gitlab : pour cela, créer un secret Kubernetes pour stocker vos identifiants gitlab :

$ kubectl create secret docker-registry gitlab --docker-server=gitlab-registry.in2p3.fr --docker-username=user --docker-password=xxxxx --dry-run -o yaml > registry.yml
$ kubectl apply -f registry.yml

Déploiement de django (à vous de jouer)

Sur l'exemple des manifests mariadb, créez un manifest pour déployer le conteneur django.

Vous pouvez déclarer plusieurs objets dans le même fichier en les séparant par la ligne ---

Pensez à : * Spécifier les images correspondantes (!) ; * Spécifier les ports pour chaque conteneur ; * Utiliser le secret pour vous identifier sur gitlab ; * Créer les services correspondants.

Après avoir appliqué le manifeste, vérifez que vous accédez aux conteneurs avec kubectl port-forward :

$ kubectl port-forward django-* 8000:8000

3. Aller plus loin

Ressources

Afin de limiter l'usage des ressources par les pod, et de faciliter le travail du scheduler, il est fortement conseillé de spécifier des Resources. Vous pouvez par exemple configurer le pod nginx avec 2Mo/10Mo, et le pod mariadb avec 40Mo/80Mo.

Secrets

Dans le TP, les secrets sont dans le dépôt, ce qui est évidemment un mauvaise pratique. En pratique, l'API Kubernetes comporte un objet (que l'on a utilisé pour configurer la registry docker) qui permet de séparer les secrets des déploiements : l'objet Secret. Ajouter un secret avec les différentes variables d'environnement nécessaire, et modifier les déploiements mariadb et django afin qu'ils utilisent le secret comme variable d'environnement. Par la suite, on peut utiliser sops pour chiffrer les secrets yaml et les ajouter au dépôt en tout confiance...

Volume

Vous aurez remarqué que nous n'abordons pas la problématique de la persistance, par manque de temps. Cet problématique est résolue par Kubernetes grâce aux Volumes. L'API est capable de communiquer avec le cloud sous-jacent (par exemple cinder, dans le cas d'Openstack), mais aussi de tailler des volumes iSCSI, nfs, etc...

NetworkPolicy

Par défaut, la base mariadb est accessible à tous les pods du cluster. Vous pouvez limiter les pods qui y ont accès à l'aide d'une NetworkPolicy. Utilisez le namespaceSelector qui correspond à votre namespace, et le label app: django.