TP - Déployer une application multiconteneurs
Une application d’exemple en 3 parties
Section titled “Une application d’exemple en 3 parties”Récupérez le projet de base en clonant le dépot global de la formation :
git clone https://github.com/e-lie/formation-kube-unified.git et en copian le dossier 128_tp_monsterstack_base sur le bureau en tp3
Ce TP va consister à créer des objets Kubernetes pour déployer une application microservices (plutôt simple) : monsterstack.
Elle est composée :
- d’un frontend en Flask (Python),
- d’un service de backend qui génère des images (un avatar de monstre correspondant à une chaîne de caractères)
- et d’un datastore
redisservant de cache pour les images de l’application
Etudions le code et testons avec docker-compose
Section titled “Etudions le code et testons avec docker-compose”- Le frontend est une application web python (flask) qui propose un petit formulaire et lance une requete sur le backend pour chercher une image et l’afficher.
- Il est construit à partir du
Dockerfileprésent dans le dossiertp3. - Le fichier
docker-compose.ymlest utile pour faire tourner les trois services de l’application dans docker rapidement (plus simple que kubernetes)
Pour lancer l’application il suffit d’exécuter par exemple: docker compose up -d
Passons maintenant à Kubernetes.
Déploiements pour le backend d’image (imagebackend) et le datastore redis
Section titled “Déploiements pour le backend d’image (imagebackend) et le datastore redis”En vous inspirant du TP précédent créez des deployments pour imagebackend et redis sachant que:
- l’image docker pour
imagebackendestamouat/dnmonster:1.0qui fonctionne sur le port 8080. - l’image officielle redis est une image connue et bien documentée du docker hud (hub.docker.com). Lancez ici simplement l’image avec une configuration minimale comme dans le docker-compose.yml. Nous discuterons plus tard de comment déployer ce type d’application stateful de façon plus fiable et robuste.
- Combien de réplicats mettre pour l’imagebackend et le redis ?
Correction
- Complétez
imagebackend.yamldans le dossierk8s:
imagebackend.yaml :
apiVersion: apps/v1kind: Deploymentmetadata: name: imagebackend labels: app: monsterstackspec: selector: matchLabels: app: monsterstack partie: imagebackend strategy: type: Recreate replicas: 5 template: metadata: labels: app: monsterstack partie: imagebackend spec: containers: - image: amouat/dnmonster:1.0 name: imagebackend ports: - containerPort: 8080 name: imagebackend- Ensuite, configurons le deuxième deployment
redis.yaml:
apiVersion: apps/v1kind: Deploymentmetadata: name: redis labels: app: monsterstackspec: selector: matchLabels: app: monsterstack partie: redis strategy: type: Recreate replicas: 1 template: metadata: labels: app: monsterstack partie: redis spec: containers: - image: redis:latest name: redis ports: - containerPort: 6379 name: redis- Appliquez ces ressources avec
kubectlet vérifiez dansLensque les 5 + 1 réplicats sont bien lancés.
Déploiement du frontend manuellement
Section titled “Déploiement du frontend manuellement”Comment déployer dans le cluster une image buildée en local, comme c’est le cas pour notre frontend ? docker build ... ne rendra en effet pas directement l’image disponible dans le cluster car ce dernier n’a rien à voir avec le démon Docker présent sur notre machine.
La façon simple et manuelle de déployer notre image est de la pousser manuellement sur un registry (serveur d’images conteneur) par exemple Docker hub mais ça pourrait étre gitlab, quay ou un registry auto-hébergé.
(cf TP développer dans Kubernetes pour de meilleures méthodes de déploiement)
- Créez un compte (gratuit et plutôt pratique) sur le Docker Hub si vous n’en avez pas encore.
- Buildez l’image
frontendavec la ligne de commandedocker build -t frontend .dans le dossier de projet.docker image listpour voir le résultat
Pour accéder à l’image dans le cluster nous allons la poussez sur le registry Docker Hub (une solution basique parmis plein d’autres) pour cela:
- Lancez la commande
docker login docker.ioet utilisez votre compte précédemment créé (ou simplementdocker logincar docker se loggue automatiquement à sa plateforme par défaut). - Tagguez l’image
frontendavecdocker tag ...avec un nouveau repository:tag précisant le serveur et l’utilisateur par exempledocker tag frontend docker.io/myuser/frontend:1.0 - Poussez l’image sur le registry avec
docker push <repository:tag> - Créez un déploiement pour le frontend en réutilisant ce repository:tag dans la section image.
- déployez avec
kubectl applypour tester.
Correction:
Ajoutez au fichier frontend.yml du dossier k8s le code suivant:
apiVersion: apps/v1kind: Deploymentmetadata: name: frontend labels: app: monsterstackspec: selector: matchLabels: app: monsterstack partie: frontend strategy: type: Recreate replicas: 3 template: metadata: labels: app: monsterstack partie: frontend spec: containers: - name: frontend image: docker.io/<votreuserdockerhhubacompleter>/frontend:1.0 ports: - containerPort: 5000- Appliquez ce fichier avec
kubectlet vérifiez que le déploiement frontend est bien créé.
Gérer la communication réseau de notre application avec des Services
Section titled “Gérer la communication réseau de notre application avec des Services”Les services K8s sont des endpoints réseaux qui envoient le trafic automatiquement vers un ensemble de pods désignés par certains labels. Ils sont un peu la pierre angulaire des applications microservices qui sont composées de plusieurs sous parties elles même répliquées.
Pour créer un objet Service, utilisons le code suivant, à compléter :
apiVersion: v1kind: Servicemetadata: name: <nom_service> labels: app: monsterstackspec: ports: - port: <port> # port exposé en entrée par le service targetPort: <target-port> # port coté conteneur selector: app: <app_selector> partie: <tier_selector> type: <type>---Ajoutez le code précédent au début de chaque fichier déploiement. Complétez pour chaque partie de notre application :
- le nom du service et le nom de la
partiepar le nom de notre programme (frontend,imagebackendetredis) - le port par le port du service
- les selectors
appetpartiepar ceux du groupe de pods correspondant.
Le type sera : ClusterIP pour imagebackend et redis, car ce sont des services qui n’ont à être accédés qu’en interne, et LoadBalancer pour frontend.
- Appliquez à nouveau.
- Listez les services avec
kubectl get services. - Visitez votre application dans le navigateur avec
minikube service frontend.
Exposer notre application à l’extérieur avec un Ingress (~ reverse proxy)
Section titled “Exposer notre application à l’extérieur avec un Ingress (~ reverse proxy)”-
Pour Minikube : Installons le contrôleur Ingress Nginx avec
minikube addons enable ingress. -
Pour k3s : Installer l’ingress nginx avec la commande:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yamlPuis vérifiez l’installation aveckubectl get svc -n ingress-nginx ingress-nginx-controller: le serviceingress-nginx-controllerdevrait avoir une IP externe. -
Pour les autres types de cluster (cloud ou manuel), lire la documentation sur les prérequis pour les objets Ingress et installez l’ingress controller appelé
ingress-nginx: https://kubernetes.io/docs/concepts/services-networking/ingress/#prerequisites. -
Si vous êtes dans k3s, avant de continuer, vérifiez l’installation du contrôleur Ingress Nginx avec
kubectl get svc -n ingress-nginx ingress-nginx-controller: le serviceingress-nginx-controllerdevrait avoir une IP externe.
Utilisation de l’ingress
Section titled “Utilisation de l’ingress”Un contrôleur Ingress (Ingress controller) est une implémentation de reverse proxy dynamique (car ciblant et s’adaptant directement aux objets services k8s) configurée pour s’interfacer avec un cluster k8s.
Une ressource Ingress est le fichier de configuration pour paramétrer le reverse proxy nativement dans Kubernetes.
-
Repassez le service
frontenden modeClusterIP. Le service n’est plus accessible sur un port. Nous allons utiliser l’ingress à la place pour afficher la page. -
Ajoutez également l’objet
Ingresssuivant dans le fichiermonsterstack-ingress.yaml:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: monsterstack annotations: nginx.ingress.kubernetes.io/rewrite-target: / kubernetes.io/ingress.class: nginxspec: rules: - host: monsterstack.local # à changer si envie/besoin http: paths: - path: / pathType: Prefix backend: service: name: frontend port: number: 5000-
Récupérez l’ip de votre cluster (minikube avec
minikube ip, pour k3 en allant observer l’objetIngressdansLensdans la sectionNetworking. Sur cette ligne, récupérez l’ip). -
Ajoutez la ligne
<ip-cluster> monsterstack.localau fichier/etc/hostsavecsudo nano /etc/hostspuis CRTL+S et CTRL+X pour sauver et quitter. -
Visitez la page
http://monsterstack.localpour constater que notre Ingress (reverse proxy) est bien fonctionnel.
Paramétrer notre déploiement
Section titled “Paramétrer notre déploiement”Configuration de l’application avec des variables d’environnement simples
Section titled “Configuration de l’application avec des variables d’environnement simples”- Notre application frontend peut être configurée en mode DEV ou PROD. Pour cela elle attend une variable d’environnement
CONTEXTpour lui indiquer si elle doit se lancer en modePRODou en modeDEV. Ici nous mettons l’environnementDEVen ajoutant (aligné à la hauteur du i de image):
env: - name: CONTEXT value: DEV-
Généralement la valeur d’une variable est fournie au pod à l’aide d’une ressource de type
ConfigMapouSecretce que nous verrons par la suite. -
Configurez de même les variables
IMAGEBACKEND_DOMAINetREDIS_DOMAINcomme dans le docker-compose et trouvez comment modifier les hostnames associés aux services.
Correction:
Pour modifier le hostname on peut soit changer le nom du service soit utiliser le parametre ClusterIP du service pour y associer un nom de domaine (cela peut impacter les autres applications qui tentent de communiquer avec le service).
Voir la correction github à la fin de la page
Quelques autres paramétrages…
Section titled “Quelques autres paramétrages…”Facultatif: Santé du service avec les Probes
Santé du service avec les Probes
Section titled “Santé du service avec les Probes”- Ajoutons des healthchecks au conteneur dans le pod avec la syntaxe suivante (le mot-clé
livenessProbedoit être à la hauteur duideimage:) :
livenessProbe: tcpSocket: # si le socket est ouvert c'est que l'application est démarrée port: 5000 initialDelaySeconds: 5 # wait before firt probe timeoutSeconds: 1 # timeout for the request periodSeconds: 10 # probe every 10 sec failureThreshold: 3 # fail maximum 3 timesreadinessProbe: httpGet: path: /healthz # si l'application répond positivement sur sa route /healthz c'est qu'elle est prête pour le traffic port: 5000 httpHeaders: - name: Accept value: application/json initialDelaySeconds: 5 timeoutSeconds: 1 periodSeconds: 10 failureThreshold: 3La livenessProbe est un test qui s’assure que l’application est bien en train de tourner. S’il n’est pas rempli le pod est automatiquement redémarré en attendant que le test fonctionne.
Ainsi, k8s sera capable de savoir si notre conteneur applicatif fonctionne bien, quand le redémarrer. C’est une bonne pratique pour que le replicaset Kubernetes sache quand redémarrer un pod et garantir que notre application se répare elle même (self-healing).
Cependant une application peut être en train de tourner mais indisponible pour cause de surcharge ou de mise à jour par exemple. Dans ce cas on voudrait que le pod ne soit pas détruit mais que le traffic évite l’instance indisponible pour être renvoyé vers un autre backend ready.
La readinessProbe est un test qui s’assure que l’application est prête à répondre aux requêtes en train de tourner. S’il n’est pas rempli le pod est marqué comme non prêt à recevoir des requêtes et le service évitera de lui en envoyer.
- on peut tester mettre volontairement port 3000 pour la livenessProbe et constater que k8s redémarre les conteneurs frontend un certain nombre de fois avant d’abandonner. On peut le constater avec
kubectl describe deployment frontenddans la section évènement ou avecLensen bas du panneau latéral droite d’une ressource.
Rollback un rollout en echec : constater la haute disponibilité de notre application
Section titled “Rollback un rollout en echec : constater la haute disponibilité de notre application”- Redéployez le frontend avec 8 replicas et une strategy type
RollingUpdate - Faisons échouer la
readinessProbeen mettant par exemple son port à5001 - Redéployez, et constatez que le deploiement se bloque à mi-chemin (+- 6 pod up de l’ancienne version et 4 nouveau
not ready) - Allez voir les
ReplicatSetspour constater qu’il y en a deux en cours la nouvelle et l’ancienne version (et tout un historique d’anciens à 0 replicas) - Allez voir la resource
Endpointscréé par le service frontend pour constater qu’elle ne liste que les pods ready dans la liste active et qu’elle met les pods not ready à part. Notre application est toujours up car les traffic est renvoyé uniquement vers les pods ready. - Nous pouvons etudier le deploiement (
rollout) en cours aveckubectl rollout status deployment frontend,kubectl rollout history deployment frontend. - Pour effectuer un rollback lancez
kubectl rollout undo deployment frontend.
Documentation sur les rollouts de deployement:
Facultatif: Ajouter des indications de ressources
Ajouter des indications de ressources nécessaires pour garantir la qualité de service
Section titled “Ajouter des indications de ressources nécessaires pour garantir la qualité de service”- Ajoutons aussi des contraintes sur l’usage du CPU et de la RAM, en ajoutant à la même hauteur que
env::
resources: requests: cpu: "100m" # 10% de proc memory: "50Mi" limits: cpu: "300m" # 30% de proc memory: "200Mi"Nos pods auront alors la garantie de disposer d’un dixième de CPU (100/1000) et de 50 mégaoctets de RAM. Ce type d’indications permet de remplir au maximum les ressources de notre cluster tout en garantissant qu’aucune application ne prend toute les ressources à cause d’un fuite mémoire etc.
Documentation : https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/
Correction du TP
Section titled “Correction du TP”La correction se trouve dans le dossier 130_tp_k8s_monsterstack du dépot de la formation (https://github.com/e-lie/formation-kube-unified)