TP Kubernetes
Prise en main
Installation de snap
Tout d'abord, si ce n'est pas déjà fait, installez snap:
sudo apt-get update
sudo apt-get install snapd
Installation et conf de base
Puis installez MicroK8s via snap et configurez-le pour l'utilisateur de la VM:
sudo snap install microk8s --classic
sudo usermod -a -G microk8s $USER
sudo chown -f -R $USER ~/.kube
su - $USER
Vérifiez que le cluster Kubernetes fonctionne correctement :
microk8s status --wait-ready
Vous pouvez récupérer la liste des noeuds (un seul par défaut) :
microk8s kubectl get nodes
Afin de faciliter la suite et de rester vanilla, ajouter l'alias suivant :
alias kubectl='microk8s kubectl'
echo "alias kubectl='microk8s kubectl'" >> ~/.bashrc
Ajoutez la complétion de la commande kubectl
:
echo 'source <(kubectl completion bash)' >>~/.bashrc
bash
Vous pouvez aussi utiliser les alias pratiques définis par kubectl-aliases.
Services de base
MicroK8s est un déploiement minimal de Kubernetes. Vous pouvez lister les pods déployés par défaut :
kubectl get pods -A
Il s'agit uniquement des pods Calico, le plugin CNI (Container Network Interface) qui gère le réseau pour les conteneurs.
Vous allez ajouter deux services importants généralement déployés sur les cluster Kubernetes :
- metrics-server pour l'accès aux métriques de base ;
- CoreDNS pour la resolution DNS interne.
metrics-server
Actuellement, vous n'avez pas accès aux métriques :
kubectl top nodes
kubectl top pods -A
Déployez metrics-server et vérifiez que le pod correspondant tourne bien :
microk8s enable metrics-server
kubectl get pods -A
Après quelques dizaine de secondes, les métriques devraient être disponibles :
kubectl top nodes
kubectl top pods -A
CoreDNS
Pour tester le service de DNS (qui est derrière le mécanisme de Service Discovery), lançez un pod nginx et vérifiez qu'il tourne :
kubectl run --image nginx nginx-pod
kubectl expose pod nginx-pod --port 80
kubectl get pods -o wide
pod_ip=$(kubectl get pod nginx-pod -o jsonpath='{.status.podIP}')
kubectl get service
service_ip=$(kubectl get service nginx-pod -o jsonpath='{.spec.clusterIP}')
Essayez de l'atteindre depuis un autre pod, via son IP, l'IP du service, puis le nom DNS du service :
kubectl run --rm --restart=Never -it --image busybox test -- wget -O- $pod_ip
kubectl run --rm --restart=Never -it --image busybox test -- wget -O- $service_ip
kubectl run --rm --restart=Never -it --image busybox test -- wget -O- nginx-pod
Déployez CoreDNS, vérifiez qu'il tourne bien et testez à nouveau la résolution DNS :
microk8s enable dns
kubectl get pods -A
kubectl run --rm --restart=Never -it --image busybox test -- wget -O- nginx-pod
Nettoyage
kubectl delete service nginx-pod
kubectl delete pod nginx-pod
Vapormap
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.
Mariadb
Premier déploiement
Astuce: pour générer les manifestes yaml facilement, vous pouvez utiliser la commande kubectl
avec les options --dry-run -o yaml
:
kubectl create deployment mariadb --image mariadb --dry-run=client -o yaml > mariadb.yml
Examiner le fichier créé et lancez le déploiement (1 réplicas) :
less mariadb.yml
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 -l app=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 -l app=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 :
kubectl 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 $nom_du_pod_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, déployez un service comme dans la première partie. Utilisez la même astuce du dry-run pour générer le fichier :
kubectl expose deployment mariadb --port=3306 --target-port=3306 --dry-run=client -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
Vapormap
Déploiement de vapormap (à vous de jouer)
Sur l'exemple des manifests mariadb, créez un manifest pour déployer les conteneurs vapormap et nginx, avec 2 replicas pour nginx et 3 pour vapormap.
Vous pouvez déclarer plusieurs objets dans le même fichier en les séparant par la ligne ---
Pensez à :
- Spécifier les images correspondantes, Si vous n'avez pas vos propres images, vous pouvez utiliser les suivantes :
registry.plmlab.math.cnrs.fr/resinfo/anf/2019/ada/vapormap/vapormap:master
registry.plmlab.math.cnrs.fr/resinfo/anf/2019/ada/vapormap/nginx:master - Positionner les variables VAPOR_DBNAME, VAPOR_DBUSER, VAPOR_DBPASS, DJANGO_SETTINGS_MODULE et VAPOR_DBHOST pour le conteneur vapormap ;
- Positionner les variables VAPORMAP_URL et VAPORMAP_PORT pour le conteneur nginx ;
- Spécifier les ports pour chaque conteneur ;
- Créer les services correspondants.
Après avoir appliqué le manifeste, vérifez que vous accédez aux conteneurs avec kubectl port-forward
.
Migration de base
L'application ne marchera pas en l'état, puisqu'il faut lancer les migrations de base avant de lancer l'application Django. Ajoutez un initContainer au déploiement varpomap, dont le rôle sera d'éxecuter la commande python manage.py makemigrations && python manage.py migrate
Verifiez le bon fonctionnement de l'application à l'aide de :
kubectl port-forward svc/nginx 8000:8000
Ingress
Maintenant, nous allons exposer l'application au monde extérieur grâce à l'objet Ingress.
Ajoutons NGINX Ingress Controller, qui est un Ingress Controller très répandu de l'écosystème :
microk8s.enable ingress
kubectl get pods --all-namespaces
Nous pouvons alors définir l'objet Ingress qui va diriger le trafic vers le service nginx :
cat <<EOF > ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vapormap
spec:
ingressClassName: public
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 8000
EOF
kubectl apply -f ingress.yml
Vous n'avez désormais plus besoin de port-forward (qui est un mécanisme prévu pr le debug) pour accéder à votre application :
firefox http://localhost
3. Aller plus loin
Ressources
Afin de limiter l'usage des ressources par les pods 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 vapormap 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 vapormap 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 toute confiance...
Volume
Vous aurez remarqué que les données ne sont pas persistantes. En effet, à chaque redémarrage du pod MariaDB, les données sont perdues. Cette problématique est résolue par Kubernetes grâce aux Volumes. Notez que Kubernetes 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...
Pour MicroK8s, il s'agit par défaut d'un simple stockage fichier dans un répertoire de la machine hôte. Il faut tout d'abord activer le composant qui gère le stockage persitant :
microk8s.enable hostpath-storage
Puis, ajoutez une PersistentVolumeClaim et attachez le volume résultant au pod du déploiement MariaDB (qui stocke ses données dans /var/lib/mysql
). Vérifiez ensuite qu'un redémarrage du pod préserve les données.
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: vapormap
.
Helm
Vous trouverez dans le repertoire helm un chart qui permet de déployer Vapormap à l'aide de Helm, un gestionnaire de paquets pour Kubernetes. Le chart se base lui-même sur le chart MariaDB de Bitnami.
Pour cela, il faut installer la version Helm 3 de MicroK8s :
microk8s.enable helm3
alias helm='microk8s helm3'
echo "alias helm='microk8s helm3'" >> ~/.bashrc
Vous pourrez alors déployer le chart Helm. Les différentes valeurs peuvent être configurées via le fichier values.yaml
.
helm upgrade --install vapormap vapormap -f values.yaml
GitOps
Pour fluidifier les mises en production d'application, nous allons utiliser Argo CD, qui permet de mettre en place des pratiques GitOps. Cet outil va surveiller un dépôt git contenant la définition de votre application et mettre à jour automatiquement à chaque modification.
Installation d'Argo CD :
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm upgrade --install argocd --create-namespace -n argocd argo/argo-cd
Vous pouvez alors déclarer votre application à Argo CD via une Custom Resource Definition :
kubectl apply -f argocd/vapormap.yaml
Après quelques secondes, votre application sera disponible. Une modification du dépôt git spécifié dans le fichier argocd/vapormap.yaml
déclenchera alors une mise à jour sur le cluster.