```
-42sh$ unshare -m ./cmpns $$ self
+42sh# unshare -m ./cmpns $$ self
- cgroup: same
- ipc: same
- mnt: differ
diff --git a/tutorial/5/mount.md b/tutorial/5/mount.md
index 6742d51..ef22fb3 100644
--- a/tutorial/5/mount.md
+++ b/tutorial/5/mount.md
@@ -10,8 +10,8 @@ Petite parenthèse avant de parler des *namespaces* ...
Au premier abord, les points de montage dans l'arborescence d'un système de
fichiers n'ont pas l'air d'être remplis de notions complexes : un répertoire
peut être le point d'entrée d'un montage vers la partition d'un disque
-physique... ou d'une partition virtuelle, comme nous l'avons vu dans la partie
-précédente.
+physique... ou d'une partition virtuelle, comme nous l'avons vu dans le TP
+précédent.
Mais avez-vous déjà essayé de monter la même partition d'un disque physique à
deux endroits différents de votre arborescence ?
@@ -214,7 +214,7 @@ mount --make-private mountpoint
### non-attachable -- *unbindable mount*
-Ce mode interdira tout tentative d'attache à un autre endroit.
+Ce mode interdira toute tentative d'attache à un autre endroit.
```bash
@@ -254,8 +254,9 @@ Il n'est pas nécessaire que le point d'accroche que l'on cherche à dupliquer
pointe sur un point de montage (c'est-à-dire, dans la plupart des cas : une
partition ou un système de fichiers virtuel). Il peut parfaitement pointer sur
un dossier, et même sur un simple fichier, à la manière d'un *hardlink*, mais
-que l'on pourrait faire entre plusieurs partitions et qui ne persisterait pas au
-redémarrage.
+que l'on pourrait faire entre plusieurs partitions et qui ne persisterait pas
+au redémarrage (le *hardlink* persiste au redémarrage, mais doit se faire au
+sein d'une même partition).
Nous verrons dans la partie [*namespace* réseau](#net-ns), une utilisation
d'attache sur un fichier.
@@ -269,7 +270,7 @@ faire même si un fichier est en cours d'utilisation. Il faut cependant veiller
à ce que les programmes susceptibles d'aller chercher un fichier à l'ancien
emplacement soient prévenus du changement.
-On utilise pour cela l'option `--move` de `mount(8)` :
+Pour déplacer un point de montage, on utilise l'option `--move` de `mount(8)` :
```bash
@@ -285,9 +286,9 @@ mount --move /dev /newroot/dev
```
-Il est courant de faire appel à cette option lorsque l'on souhaite changer la
-racine de notre système de fichiers : par exemple pour passer de l'*initramfs*
-au système démarré, de notre système hôte au système d'un conteneur, ...
+Cette possibilité s'emploie notamment lorsque l'on souhaite changer la racine
+de notre système de fichiers : par exemple pour passer de l'*initramfs* au
+système démarré, de notre système hôte au système d'un conteneur, ...
## Aller plus loin {-}
diff --git a/tutorial/5/namespaces.md b/tutorial/5/namespaces.md
index 69a6696..0e3c34f 100644
--- a/tutorial/5/namespaces.md
+++ b/tutorial/5/namespaces.md
@@ -10,8 +10,8 @@ dupliquer certaines structures, habituellement considérées uniques
pour le noyau, dans le but de les isoler d'un groupe de processus à un
autre.
-On en dénombre sept depuis Linux 4.6 : `cgroup`, `IPC`, `network`,
-`mount`, `PID`, `user` et `UTS`.
+On en dénombre sept (le dernier ayant été ajouté dans Linux 4.6) : `cgroup`,
+`IPC`, `network`, `mount`, `PID`, `user` et `UTS`.
La notion d'espace de noms est relativement nouvelle et a été intégrée
progressivement au sein du noyau Linux. Aussi, toutes les structures
@@ -27,12 +27,12 @@ Depuis Linux 2.4.19.
Cet espace de noms isole la liste des points de montage.
-Chaque processus appartenant à un *namespace* différent peut monter, démonter
-et réorganiser à sa guise les points de montage, sans que cela n'ait d'impact
-sur les processus hors de cet espace de noms. Une partition ne sera donc pas
-nécessairement démontée après un appel à `umount(2)`, elle le sera lorsqu'elle
-aura effectivement été démontée de chaque *namespace* dans lequel elle était
-montée.
+Chaque processus appartenant à un *namespace mount* différent peut monter,
+démonter et réorganiser à sa guise les points de montage, sans que cela n'ait
+d'impact sur les processus hors de cet espace de noms. Une partition ne sera
+donc pas nécessairement démontée après un appel à `umount(2)`, elle le sera
+lorsqu'elle aura effectivement été démontée de chaque *namespace mount* dans
+lequel elle était montée.
Attention il convient cependant de prendre garde aux types de liaison existant
entre vos points de montage (voir la partie sur
@@ -198,20 +198,23 @@ similaire à :
```c
#include
-#define STACKSIZE (1024*1024)
+#define STACKSIZE (1024 * 1024)
static char child_stack[STACKSIZE];
int clone_flags = CLONE_CGROUP | CLONE_NEWNET | SIGCHLD;
-pid_t pid = clone(do_execvp,
- child_stack + STACKSIZE,
- clone_flags,
- &args);
+pid_t pid = clone(do_execvp, // First function executed by child
+ child_stack + STACKSIZE, // Assume stack grows downward
+ clone_flags, // clone specials flags
+ args); // Arguments to pass to do_execvp
```
-Le premier argument est un pointeur sur fonction. Il s'agit de la fonction qui
-sera appelée par le nouveau processus.
+Dans cet exemple, le processus fils créé disposera d'un nouvel espace de noms
+pour les *CGroups* et disposera d'une nouvelle pile réseau.
+
+Un exemple complet d'utilisation de `clone(2)` et du *namespace* `UTS` est
+donné dans le `man` de l'appel système.
## Rejoindre un *namespace*
@@ -310,6 +313,6 @@ les *namespaces*](https://lwn.net/Articles/531114/) est excellente ! Auquel il
faut ajouter [le petit dernier sur le `cgroup`
*namespace*](https://lwn.net/Articles/621006/).
-[Cet article de Michael Crosby montrant l'utilisation de clone(2)](http://crosbymichael.com/creating-containers-part-1.html)
+[Cet article de Michael Crosby montrant l'utilisation de clone(2)](https://web.archive.org/web/20190206073558/http://crosbymichael.com/creating-containers-part-1.html)
est également des plus intéressants, pour ce qui concerne la programmation
plus bas-niveau.
diff --git a/tutorial/5/networkns.md b/tutorial/5/networkns.md
index 3cfbe58..45b8a66 100644
--- a/tutorial/5/networkns.md
+++ b/tutorial/5/networkns.md
@@ -235,3 +235,7 @@ Pour construire une nouvelle interface de ce type :
Pour approfondir les différentes techniques de routage, je vous
recommande cet article :
[Linux Containers and Networking](https://blog.flameeyes.eu/2010/09/linux-containers-and-networking).
+
+Appliqué à Docker, vous apprécirez cet article : [Understanding Docker
+Networking Drivers and their use
+cases](https://www.docker.com/blog/understanding-docker-networking-drivers-use-cases/).
diff --git a/tutorial/5/pidns.md b/tutorial/5/pidns.md
index bc165d5..cead56f 100644
--- a/tutorial/5/pidns.md
+++ b/tutorial/5/pidns.md
@@ -41,8 +41,8 @@ contenu de `/proc`. D'ailleurs, si l'on affiche le PID du processus courant
En l'état, beaucoup d'informations sont divulguées. Mais il n'est pas possible
de monter le bon `/proc` car il serait également monté pour les processus de
-notre système initial. Pour s'en sortir, il est nécessaire de s'isoler du
-*namespace* `mount`.
+notre système initial. Pour s'en sortir, il est nécessaire de s'isoler dans un
+*namespace* `mount` séparé.
### Double isolation : ajout du *namespace* `mount`
diff --git a/tutorial/5/project-rendu.md b/tutorial/5/project-rendu.md
index 3af9fc5..c4a57d8 100644
--- a/tutorial/5/project-rendu.md
+++ b/tutorial/5/project-rendu.md
@@ -15,6 +15,9 @@ sera pas pris en compte.
Pour différencier le rendu du TP, du rendu du projet, ajoutez une balise
`[PROJET]` au sujet de votre courriel, afin qu'il soit traité comme tel.
+N'hésitez pas à indiquer dans le corps du courriel votre
+ressenti et vos difficultés ou bien alors écrivez votre meilleure histoire
+drôle si vous n'avez rien à dire.
Tarball
-------
diff --git a/tutorial/5/rendu.md b/tutorial/5/rendu.md
index fc7ed47..74557ea 100644
--- a/tutorial/5/rendu.md
+++ b/tutorial/5/rendu.md
@@ -23,8 +23,13 @@ et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne
sera pas pris en compte.
+Afin d'orienter correctement votre rendu, ajoutez une balise `[TP5]` au sujet
+de votre courriel. N'hésitez pas à indiquer dans le corps du courriel votre
+ressenti et vos difficultés ou bien alors écrivez votre meilleure histoire
+drôle si vous n'avez rien à dire.
+
Par ailleurs, n'oubliez pas de répondre à
-[l'évaluation du cours](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=309).
+[l'évaluation du cours](https://virli.nemunai.re/quiz/7).
Tarball
@@ -37,8 +42,8 @@ Voici une arborescence type :
```
-login_x-TP4/cmpns.sh
-login_x-TP4/mydocker_exec.sh
-login_x-TP4/myswitch_root.sh
+login_x-TP5/cmpns.sh
+login_x-TP5/mydocker_exec.sh
+login_x-TP5/myswitch_root.sh
```
diff --git a/tutorial/5/tutorial.md b/tutorial/5/tutorial.md
index d119d99..64f2ae0 100644
--- a/tutorial/5/tutorial.md
+++ b/tutorial/5/tutorial.md
@@ -1,9 +1,9 @@
---
-title: Virtualisation légère -- TP n^o^ 4
+title: Virtualisation légère -- TP n^o^ 5
subtitle: Linux Internals partie 2
author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps}
institute: EPITA
-date: Mercredi 6 novembre 2019
+date: Jeudi 12 novembre 2020
abstract: |
Le but de ce second TP sur les mécanismes internes du noyau va nous
permettre d'utiliser les commandes et les appels systèmes relatifs
@@ -13,12 +13,11 @@ abstract: |
\vspace{1em}
Tous les exercices de ce TP sont à rendre à
au
- plus tard le mercredi 20 novembre 2017 à 13 h 42.
+ plus tard le jeudi 19 novembre 2020 à 12 h 42.
- En tant que personnes sensibilisées à la sécurité des échanges
- électroniques, vous devrez m'envoyer vos rendus signés avec votre
- clef PGP. Pensez à
- [me](https://keys.openpgp.org/search?q=nemunaire%40nemunai.re)
- faire signer votre clef et n'hésitez pas à [faire signer la
+ En tant que personnes sensibilisées à la sécurité des échanges électroniques,
+ vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à
+ [me](https://keys.openpgp.org/search?q=nemunaire%40nemunai.re) faire signer
+ votre clef et n'hésitez pas à [faire signer la
votre](https://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
...
diff --git a/tutorial/devops/tools.md b/tutorial/devops/tools.md
index fe1d7f7..1ce654b 100644
--- a/tutorial/devops/tools.md
+++ b/tutorial/devops/tools.md
@@ -43,7 +43,9 @@ Aller c'est parti ! première chose à faire : installer et configurer
[gitlab](https://gitlab.org/) ou une autre plate-forme, mais la suite du TP
sera moins guidée pour eux).
-Nous allons utiliser l'image : .
+Nous allons utiliser l'image :
+[`gitea/gitea`](https://hub.docker.com/r/gitea/gitea) (ou
+[`gitlab/gitlab-ce`](https://hub.docker.com/r/gitlab/gitlab-ce)).
Votre playbook resemblera à quelque chose comme ça :
diff --git a/tutorial/k8s/Makefile b/tutorial/k8s/Makefile
index 9fc4a76..70a0630 100644
--- a/tutorial/k8s/Makefile
+++ b/tutorial/k8s/Makefile
@@ -1,6 +1,6 @@
include ../pandoc-opts.mk
-SOURCES_TUTO = tutorial.md setup.md intro.md overview.md discover.md run.md scaling.md rendu.md
+SOURCES_TUTO = tutorial-el.md setup.md intro.md overview.md discover.md run.md scaling.md rendu.md
all: tutorial.pdf
diff --git a/tutorial/k8s/discover.md b/tutorial/k8s/discover.md
index 800ffe4..c226988 100644
--- a/tutorial/k8s/discover.md
+++ b/tutorial/k8s/discover.md
@@ -84,13 +84,13 @@ kubectl -n kube-system get pods
```
Eh oui ! De nombreux services de base pour Kubernetes tournent dans des
-conteneurs, géré par lui-même... notamment :
+conteneurs, gérés par lui-même... notamment :
- `etcd` : notre base de données clef/valeur,
- `kube-apiserver` : l'API REST avec qui communique `kubectl`,
- `kube-controller-manager` et `kube-scheduler`, deux autres composants
indispensables,
-- `coredns` : un composant additionnel pour gérer la résolution de noms interne
+- `coredns` : un composant additionnel pour gérer la résolution de noms internes
(pour pas avoir à s'embêter avec les IP),
- `kube-proxy` : 1 par nœud, pour gérer l'ouverture des ports notamment,
- `kindnet`, `weave` : 1 par nœud, le plugin réseau.
@@ -110,87 +110,98 @@ Nous devons lancer un *pod* (qui ne contiendra qu'un seul conteneur).
kubectl run pingpong --image alpine ping 1.1.1.1
```
-Outre un avertissement, `kubectl` doit indiquer nous qu'une tâche de
-déploiement a été créée.
+`kubectl` doit nous indiquer nous qu'un *pod* a été créée.
Si l'on affiche la liste des pods, vous devriez avoir quelque chose qui
ressemble à cela :
```
$ kubectl get pods
-NAME READY STATUS RESTARTS AGE
-pingpong-7d49d9bc9-k8fpg 1/1 Running 0 123s
+NAME READY STATUS RESTARTS AGE
+pingpong 1/1 Running 0 123s
```
-#### Déploiement³
+#### Sortie d'un conteneur
-Si l'on affiche davantage d'informations, on obtient :
+Allons maintenant regarder si nous recevons bien nos PONG.
+
+Pour cela, nous allons utiliser la commande `kubectl logs`. Cette commande
+s'utilise d'une manière similaire à `docker logs` :
+
+```bash
+kubectl logs pingpong
+```
+
+ou bien :
+
+```bash
+kubectl logs -f pingpong
+```
+
+Notez ici l'option -f qui permet de suivre les logs en direct.
+
+
+Notre premier test ayant réussi, nous pouvons arrêter de DDos Cloudflare :
+
+```bash
+kubectl delete deploy/pingpong
+```
+
+
+### Déploiement³
+
+Bien ... maintenant que nous savons nous débrouiller avec `kubectl`, attaquons
+les choses sérieuses : en temps normal avec Kubernetes, nous ne déploierons pas
+de *pod* directement, car cela reviendrait à utiliser Docker, mais des tâches
+de déploiement.
+
+Essayons sans plus attendre de lancer nos `ping` à travers une tâche de déploiement :
+
+```bash
+kubectl create deployment pingpong --image=alpine -- ping 1.1.1.1
+```
+
+Si l'on regarde maintenant la sortie de `kubectl get all`, on obtient :
```
-$ kubectl get all
-NAME READY STATUS RESTARTS AGE
-pod/pingpong-7d49d9bc9-k8fpg 1/1 Running 0 123s
+NAME READY STATUS RESTARTS AGE
+pod/pingpong-98f6d5899-5wsrm 0/1 ContainerCreating 0 123s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
-service/kubernetes ClusterIP 10.96.0.1 443/TCP 2m3s
+service/kubernetes ClusterIP 10.96.0.1 443/TCP 123h
NAME READY UP-TO-DATE AVAILABLE AGE
-deployment.apps/pingpong 1/1 1 1 123s
+deployment.apps/pingpong 0/1 1 0 123s
NAME DESIRED CURRENT READY AGE
-replicaset.apps/pingpong-7d49d9bc9 1 1 1 123s
+replicaset.apps/pingpong-98f6d5899 1 1 0 123s
```
+Pas de panique, on peut très facilement le décortiquer :
+
Les tâches de déploiements (*deployment.apps*) sont des ressources de
-haut-niveau et sont là pour assurer que les migrations se font en douceur :
+haut-niveau et sont là pour s'assurer que les migrations se font en douceur :
elles vont permettre de basculer progressivement les pods d'une version X à une
version Y (par exemple si l'on change notre ping d'alpine vers debian), mais
éventuellement de revenir sur la version X si besoin, en cours de migration.
Elles délèguent aux *replicatsets* la gestion des pods.
Le *replicatset* est là pour indiquer le nombre de pods que l'on désire, et
-s'assure que le nombre de pods actuellement lancé est bien en adéquation avec
-le nombre de pods attendus.
+s'assurer que le nombre de pods actuellement lancé est bien en adéquation avec
+le nombre de pods attendu.
-Pour résumer : `kubectl run` a créé une tâche de déploiement
+Pour résumer : `kubectl` a créé une tâche de déploiement
`deploy/pingpong`. Cette tâche de déploiement a créé elle-même un *replicatset*
`rs/pingpong-xxxx`. Ce *replicatset* a créé un *pod* `po/pingpong-yyyy`.
-#### Sortie d'un conteneur
+### Passage à l'échelle : facile ?
-Bref ... allons maintenant regarder si nous recevons bien nos PONG.
-
-Pour cela, nous allons utiliser la commande `kubectl logs`. Cette commande
-s'utilise d'une manière similaire à `docker logs` :
+Pour lancer 3 ping en parallèle, modifions la tâche de déploiement comme suit :
```bash
-kubectl logs deploy/pingpong
-```
-
-Cette ligne est la plus simple, et nous affichera la sortie du premier pod de
-la tâche de déploiement.
-
-Pour afficher un pod en particulier, il faut indiquer son nom en entier, en
-remplaçant `yyyy` par son identifiant :
-
-```bash
-kubectl logs -f pingpong-yyyy
-```
-
-Notez ici l'option -f qui permet de suivre les logs en direct.
-
-
-#### Mise à l'échelle : facile ?
-
-Bien ... maintenant que nous savons nous débrouiller avec `kubectl`, attaquons
-les choses sérieuses.
-
-Pour lancer 8 ping en parallèle, modifions la tâche de déploiement comme suit :
-
-```bash
-kubectl scale deploy/pingpong --replicas 8
+kubectl scale deploy/pingpong --replicas 3
```
À ce stade, comme nous ne modifions que le nombre de replicats, Kubernetes va
@@ -199,55 +210,18 @@ tout simplement propager ce nombre au *replicatset* existant. Puis, le
de pods en cours d'exécution, il va en lancer de nouveaux, afin de répondre à
la demande.
-Et que se passe-t-il alors, si l'on tue un pod ?
+Et que se passe-t-il alors, si l'on tue un *pod* ?
```bash
kubectl delete pod pingpong-yyyy
```
+Cela supprime bien un *pod*, mais un autre est relancé instantannément car le
+*replicatset* constate une différence dans le nombre attendu.
-#### Autres usages de `run`
-
-Si l'on veut des tâche qui ne redémarrent pas systématiquement, on peut
-utiliser : `kubectl run --restart=OnFailure` ou `kubectl run --restart=Never`,
-... au lieu de créer des tâches de déploiement, cela va créer des *jobs* ou des
-*pods*. On peut même créer l'équivalent de tâches cron avec : `kubectl run
---schedule=...`.
-
-Comme nous venons de le voir, actuellement `kubectl run` sert un peu à tout et
-n'importe quoi, la ressource créée n'est pas évidente, c'est pour cela que
-l'avertissement nous recommande d'utiliser `kubectl create` :
-
-- `kubectl create deployment` pour créer une tâche de déploiement,
-- `kubectl create job` pour créer un *job*.
-
-Dans le futur, `kubectl run` servira à lancer un *pod* à usage unique, sans
-tâche de déploiement ni réplicat.
-
-
-En fait, `kubectl run` génère une nouvelle spécification, qu'il envoie à l'API
-de Kubernetes. On peut voir le fichier généré avec la ligne de commande
-suivante :
-
-```bash
-kubectl run --dry-run -o yaml pingpong --image alpine ping 1.1.1.1
-```
-
-Le fichier YAML récupéré peut s'utiliser comme suit :
-
-```bash
-kubectl apply -f my-specs.yml
-```
-
-
-#### Arrêter de flooder 1.1.1.1
-
-Ok, on s'est bien amusé à ping Cloudflare, pour ne pas être trop méchants, nous
-pouvons maintenant arrêter nos pods.
-
-Comme nous l'avons vu juste avant, il ne s'agit pas de tuer chacun des pods un
-par un, car de nouveaux seraient créés par le *replicatset*. Si l'on supprime
-le *replicatset*, la tâche de déploiement en rećréera un similaire.
+Si nous voulons arrêter de DDoS Cloudflare, il ne s'agit pas de tuer chacun des
+pods un par un, car de nouveaux seraient créés par le *replicatset*. Si l'on
+supprime le *replicatset*, la tâche de déploiement en rećréera un similaire.
Pour arrêter nos conteneurs, il convient donc de supprimer la tâche de
déploiement :
@@ -276,13 +250,13 @@ Il y a différents types de services :
- `ClusterIP` (par défaut) : une adresse IP virtuelle est allouée pour le
service, elle n'est accessible que depuis le réseau interne (par les pods et
- les nœuds). Il n'y a pas de translation de port a effectuer.
+ les nœuds). Il n'y a pas de translation de port à effectuer.
- `NodePort` : un port est alloué pour le service, sur tous les nœuds le
cluster, et n'importe qui peut alors s'y connecter. Le port est choisi
aléatoirement.
- `LoadBalancer` : lorsque l'infrastructure sous-jacente fourni un
load-balancer (typiquement AWS, GCE, Azure, ...), un service `NodePort` est
- créé pour utiiser ce load-balancer externe.
+ créé pour utiliser ce load-balancer externe.
- `ExternalName` : une entrée DNS est créée pour avoir un alias.
@@ -309,3 +283,86 @@ youp0m ClusterIP 10.102.129.233 8080/TCP 42s
Depuis un nœud du cluster, on peut donc venir interroger cette IP. Si l'on
essaie avec plusieurs nœuds, on voit alors que les requêtes sont balancées sur
différents nœuds.
+
+Si vous passez par `kind`, vous pouvez constater le bon fonctionnement grâce à :
+
+```bash
+docker exec -it kind-control-plane curl 10.96.179.154:8080
+```
+
+
+Kubernetes dashboard
+--------------------
+
+L'équipe de Kubernetes propose un tableau de bord assez pratique, qui permet de
+voir toutes les *resources*, comme nous l'avons fait avec `kubectl`, mais dans
+une interface web.
+
+Ils mettent à disposition un fichier décrivant l'état d'un cluster ayant une
+telle application. Nous pouvons demander à ce que notre cluster converge vers
+la configuration nécessaire :
+
+```bash
+kubectl create -f https://virli.nemunai.re/insecure-dashboard.yaml
+```
+
+Notez que le dashboard, avec cette configuration, va s'exécuter sans les
+prérequis minimum de sécurité : pas de certificat TLS, ni
+d'authentification. Ceci est juste pour jouer avec l'interface, en production,
+on n'utilisera pas cette recette.
+
+Regardons où nous pouvons contacter notre dashboard :
+
+```bash
+$ kubectl get svc
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+dashboard NodePort 10.96.78.69 80:31505/TCP 3m10s
+kubernetes ClusterIP 10.96.0.1 443/TCP 6m51s
+```
+
+Regardons si cela répond :
+
+```bash
+$ docker exec -it kind-control-plane curl 127.0.0.1:31505
+ You are using an outdated browser.
+```
+
+Pas très sympa... il faudrait que l'on puisse le voir dans un navigateur plus
+... moderne alors.
+
+Étant donné que notre cluster ne se trouve pas directement sur notre machine,
+mais dans différents conteneurs Docker, nous ne pouvons pas accéder à
+`127.0.0.1`. Heureusement, au moment de la création de notre cluster, nous
+avons renseigné plusieurs ports redirigés au sein de notre configuration. Il va
+donc falloir indiquer à Kubernetes que l'on désire utiliser un port spécifique
+pour exposer le tableau de bord.
+
+Pour ce faire, éditons le fichier `insecure-dashboard.yaml`, pour ajouter, dans
+la partie `Service` un *node port* plus spécifique :
+
+```yaml
+ - port: 80
+ protocol: TCP
+ targetPort: 80
+ nodePort: 30002
+```
+
+Maintenant, nous n'allons pas recréer un nouveau dashboard : nous allons
+simplement « appliquer » la nouvelle configuration :
+
+```bash
+kubectl apply -f my-insecure-dashboard.yaml
+```
+
+En voyant la divergence entre la réalité et la configuration demandée,
+Kubernetes va tout mettre en œuvre pour se conformer à nos directives. En
+l'occurrence, il s'agit de changer le port qui expose le service au sein du
+cluster.
+
+En fait, on pourra faire exactement la même chose lors d'un changement de
+version. Kubernetes verra la différence et appliquera une politique de
+migration déterminée.
+
+Une fois que c'est fait, nous pouvons fièrement utiliser notre navigateur pour
+aller sur (vous pouvez *Skip* l'authentification,
+dans cette configuration d'exemple, elle n'est pas nécessaire).
diff --git a/tutorial/k8s/intro.md b/tutorial/k8s/intro.md
index b3248c7..3c792bd 100644
--- a/tutorial/k8s/intro.md
+++ b/tutorial/k8s/intro.md
@@ -7,8 +7,10 @@ Notre application du jour
-------------------------
Aujourd'hui, nous allons travailler avec un mineur de pépites ... de chocolat !
-De véritables cookies sont à gagner pour celles et ceux qui auront amassés le
-plus de pépites avant la pause !
+Alors, on se fait un bon thé, on prend sa boîte de gâteaux pour tenir le coup,
+et c'est parti !
+
![ChocoMiner](dockercoins-diagram.svg)
@@ -32,7 +34,7 @@ Obtenir l'application
```shell
-git clone https://git.nemunai.re/chocominer.git
+git clone https://gitea.nemunai.re/srs/chocominer.git
```
@@ -62,8 +64,8 @@ de chronograf pour voir l'avancement de recherche de pépites :
-Monté en puissance
-------------------
+Montée en puissance
+-------------------
```bash
docker-compose up -d --scale worker=2
@@ -88,6 +90,6 @@ la cause des ralentissements :
- Testons `rng` : `httping -c 3 localhost:8001`,
- puis testons `hasher` : `httping -c 3 localhost:8002`.
-Il semblerait que notre application `rng` nécessite d'être exécuté en parallèle
+Il semblerait que notre application `rng` nécessite d'être exécutée en parallèle
! Mais on ne peut pas faire de répartition de charge facilement avec
`docker-compose` !
diff --git a/tutorial/k8s/nuggets-graph.png b/tutorial/k8s/nuggets-graph.png
new file mode 100644
index 0000000..ded7eb0
Binary files /dev/null and b/tutorial/k8s/nuggets-graph.png differ
diff --git a/tutorial/k8s/overview.md b/tutorial/k8s/overview.md
index 1a28954..9aaa23a 100644
--- a/tutorial/k8s/overview.md
+++ b/tutorial/k8s/overview.md
@@ -9,13 +9,13 @@ spécifications qu'on lui aura demandé.
Ce projet est l'aboutissement de plus d'une dizaine d'années d'expérience de
gestion de conteneurs applicatifs chez Google (rappelons que c'est eux qui ont
-poussés de nombreuses technologies dans le noyau Linux, notamment les
+poussé de nombreuses technologies dans le noyau Linux, notamment les
*cgroups*, ...).
Dans Kubernetes, il n'est pas question d'indiquer comment lancer ses
conteneurs, ni même quels cgroups utiliser. On va fournir à l'orchestrateur des
informations, des *spécifications* qui vont altérer l'état du cluster. Et c'est
-en cherchant à être constamment dans l'état que l'on lui a décrit, qu'il va
+en cherchant à être constamment dans l'état qu'on lui a décrit, qu'il va
s'adapter pour répondre aux besoins.
Par exemple, on ne va pas lui expliquer comment lancer des conteneurs ou
@@ -94,7 +94,7 @@ node
pod
: un groupe de conteneurs travaillant ensemble. Il s'agit de la ressource que
l'on déploie sur un *node*. Les conteneurs au sein d'un *pod* ne peuvent pas
- être séparés pour travailler sur deux *nodes* différent.
+ être séparés pour travailler sur deux *nodes* différents.
service
: c'est un point de terminaison (*endpoint*), stable dans le temps, sur lequel
@@ -128,8 +128,8 @@ compléter ce schéma...
Chaque plugin implémente la [spécification
CNI](https://github.com/containernetworking/cni/blob/master/SPEC.md#network-configuration)
-(Container Network Interface). On trouve donc autant de plugin qu'il n'y a de
-besoin en terme de réseau.
+(Container Network Interface). On trouve donc autant de plugin qu'il y a de
+besoins en terme de réseau.
Ainsi, à la création d'un conteneur, Kubernetes va laisser aux plugins CNI le
loisir d'allouer l'adresse IP, d'ajouter les interfaces réseaux adéquates, de
diff --git a/tutorial/k8s/rendu.md b/tutorial/k8s/rendu.md
index b1574c5..e7fe6de 100644
--- a/tutorial/k8s/rendu.md
+++ b/tutorial/k8s/rendu.md
@@ -3,8 +3,41 @@
Rendu
=====
-Il n'y a rien à rendre pour ce TP. Profitez-en pour avancer sur le projet !
+Modalités de rendu
+------------------
-Mais n'oubliez pas de répondre au
-[sondage](https://www.epitaf.fr/moodle/mod/feedback/view.php?id=305) pour me
-permettre d'améliorer ce cours.
+Un service automatique s'occupe de réceptionner vos rendus, de faire des
+vérifications élémentaires et de vous envoyer un accusé de réception (ou de
+rejet).
+
+Ce service écoute sur l'adresse <ανδροππήςρε@nemunai.re>. C'est donc à cette adresse
+et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
+envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne
+sera pas pris en compte.
+
+Afin d'orienter correctement votre rendu, ajoutez une balise `[TP6]` au sujet
+de votre courriel. N'hésitez pas à indiquer dans le corps du courriel votre
+ressenti et vos difficultés ou bien alors écrivez votre meilleure histoire
+drôle si vous n'avez rien à dire.
+
+Mais n'oubliez pas de répondre au [sondage](https://virli.nemunai.re/quiz/8)
+pour me permettre d'améliorer ce cours.
+
+
+Tarball
+-------
+
+Tous les exercices de ce TP sont à placer dans une tarball (pas d'archive ZIP,
+RAR, ...).
+
+Voici une arborescence type :
+
+
+```
+login_x-TP6/my-cluster.yml
+login_x-TP6/my-dashboard.yaml
+login_x-TP6/influxdb.yml # values.yml avec Helm ou le fichier passé à kubectl -f
+login_x-TP6/chronograph.yml
+login_x-TP6/daemonset-rng.yml
+```
+
diff --git a/tutorial/k8s/run.md b/tutorial/k8s/run.md
index cdb0700..875cf07 100644
--- a/tutorial/k8s/run.md
+++ b/tutorial/k8s/run.md
@@ -7,94 +7,87 @@ Maintenant que nous en savons un peu plus sur Kubernetes, nous allons commencer
à déployer notre application ChocoMiner dans notre cluster. Pour cela, nous
allons devoir :
-- construire les images de notre application ;
-- publier ces images dans un registre ;
- lancer des déploiements de ces images ;
- exposer avec un ClusterIP les services qui ont besoin de communiquer
entre-eux ;
- exposer avec un NodePort l'interface graphique de contrôle.
-Rappels sur les registres
--------------------------
+Lancement des *pods*
+--------------------
-Pour rappel, lorsque l'on exécute un `docker run alpine`, Docker va étendre le
-nom de l'image en `library/alpine` puis `index.docker.io/library/alpine`. Pour
-interroger `index.docker.io` au sujet d'une image `library/alpine:latest`.
+### Via Helm
-Si l'on souhaite utiliser un autre registre que le Docker Hub, il faut alors
-préciser le domaine à utiliser : `registry.mycompany.io:5000/myimage:awesome`,
-...
+[Helm](https://helm.sh/) est l'équivalent d'un gestionnaire de paquets, mais
+pour Kubernetes. Nous avons pu voir dans la section précédente qu'il faut
+parfois écrire des fichiers de description YAML assez volumineux (et encore,
+celui du tableau de bord est tout petit !) afin de se faire comprendre de
+Kubernetes.
+Helm se veut donc, notamment, être un moyen de packager une application, pour
+que ce soit plus simple de l'ajouter à son cluster k8s. L'[artifact
+hub](https://artifacthub.io/) est une agrégation de différents dépôts,
+permettant de trouver facilement son bonheur. On va y trouver
+[`influxdb`](https://artifacthub.io/packages/helm/influxdata/influxdb) dont on
+va avoir besoin pour la suite.
-Sur la route du registre auto-hébergé
--------------------------------------
+Mais d'abord, il va nous falloir [installer
+helm](https://helm.sh/docs/intro/install/). Il utilisera la même configuration
+que `kubectl`, il n'y a rien de plus à configurer.
-Profitons d'avoir notre cluster Kubernetes pour y installer un registre Docker
-!
-
-Nous allons déployer l'image officielle de registre Docker : `registry` et
-faire en sorte que celle-ci stocke les couches des images en local.
-
-La subtilité à ne pas oublier, est que Docker nécessite d'avoir une connexion
-chiffrée avec ses registres, à moins qu'il ne soit lancé avec le flag
-`--insecure-registry`, ou que le registre soit sur `127.0.0.0/8`.
-
-L'idée sera donc de publier notre registre via un `NodePort`, afin qu'il soit
-disponible sur un port `127.0.0.1:xxxxx` de chaque nœud :
+Une fois `helm` installé, et le dépôt `influxdata` ajouté, comme précisé dans
+la documentation du *chart* d'InfluxDB, nous pouvons le déployer dans notre
+cluster :
```bash
-kubectl create deployment registry --image=registry
-kubectl expose deploy/registry --port=5000 --type=NodePort
+helm install influxdata/influxdb --generate-name
```
-Pour tester, vous pouvez :
+Les valeurs de configuration indiquées dans le `README` du *chart* se modifient
+ainsi :
```bash
-NODEPORT=$(kubectl get svc/registry -o json | jq .spec.ports[0].nodePort)
-REGISTRY=127.0.0.1:$NODEPORT
-curl $REGISTRY/v2/_catalog
+helm upgrade -f values.yml your-influx-name influxdata/influxdb
```
-```bash
-docker pull busybox
-docker tag busybox $REGISTRY/busybox
-docker push $REGISTRY/busybox
-```
+Il vous sera entre-autre nécessaire d'ajouter un administrateur afin de pouvoir
+utiliser la base de données.
+
+Nous pouvons ensuite faire de même avec
+[Chronograf](https://artifacthub.io/packages/helm/influxdata/chronograf) ou
+mixer avec la méthode ci-dessous (en adaptant certaines valeurs).
-Utiliser les images du Docker Hub
----------------------------------
+### Via `kubectl`
-Si vous n'avez pas réussi à déployer votre propre registre (ou si on n'est pas
-en avance pour avoir le temps de regarder ça !), utilisez les images mises à
-disposition sur le Docker Hub :
-
-- `nemunaire/worker:v0.1`
-- `nemunaire/rng:v0.1`
-- `nemunaire/hasher:v0.1`
-
-
-Lançons les images standards
-----------------------------
-
-#### `influxdb` et `chronograf`
+Si vous ne souhaitez pas utiliser `helm`, vous pouvez vous rabattre sur les
+YAML que l'on a utilisé jusqu'à maintenant, et utiliser `kubectl`. Commençons
+par lancer `influxdb` :
```bash
-kubectl create deployment chronograf --image=chronograf
kubectl apply -f https://virli.nemunai.re/influxdb.yaml
```
-#### Notre application
+Pour chronograf, la commande suivante fonctionnerait, mais prenons exemple sur
+le fichier YAML d'InfluxDB pour Chronograf :
```bash
-TAG=v0.1
+kubectl create deployment chronograf --image=chronograf -- chronograf \
+ --influxdb-url=http://influxdb:8086 \
+ --influxdb-username=chronograf \
+ --influxdb-password=eBoo8geingie8ziejeeg8bein6Yai1a
+```
+
+### Notre application
+
+```bash
+TAG=0.1
for SERVICE in hasher rng worker; do
kubectl create deployment $SERVICE --image=nemunaire/$SERVICE:$TAG
done
```
-#### Exposer les ports
+### Exposer les ports
Pour trois des applications, des ClusterIP font l'affaire, car ils n'ont pas
besoin d'être exposés en dehors du cluster.
@@ -112,3 +105,19 @@ kubectl create service nodeport chronograf --tcp=8888 --node-port=30001
```
À ce stade, nous devrions pouvoir accéder à l'interface de Chronograf !
+
+Le port 30001 est exposé par `kind` (cela faisait partie des ports redirigés par
+Docker entre le nœud *master* et votre machine !), nous devrions donc pouvoir
+nous rendre sur : pour y voir Chronograf.
+
+Pour afficher un graphique intéressant, on se rend dans *Explore*, on choisit
+la base `chocominer.autogen`, puis la table `hashes` et enfin on sélectionne
+l'élément `value`. Pour être tout à fait juste, il faut choisir la fonction
+`sum`, car nous voulons afficher le nombre total de condensat générés. Un
+second graphique intéressant est celui du nombre de pépites trouvées : il faut
+compter (`count`), le nombre d'éléments dans la table `chunks`.
+
+![Montée en charge progressive dans Chronograph](nuggets-graph.png)
+
+Vous n'avez pas la même courbe de progression ? continuons le TP alors, pour
+augmenter la puissance de notre *rig* !
diff --git a/tutorial/k8s/scaling.md b/tutorial/k8s/scaling.md
index c06e26d..82c4d12 100644
--- a/tutorial/k8s/scaling.md
+++ b/tutorial/k8s/scaling.md
@@ -1,7 +1,5 @@
-\newpage
-
Montée en charge
-================
+----------------
Commençons facilement, en augmentant le nombre de `workers` :
@@ -22,8 +20,7 @@ Par contre, ce ne sera pas aussi simple d'augmenter le nombre de `rng`. En
effet, il nous faut répartir les services entre plusieurs machines.
-Daemon sets
------------
+### Daemon sets
Une ressource *daemon sets* va s'assurer que tous les nœuds (ou une partie)
vont exécuter une instance d'un *pod*. Ainsi, si un nouveau nœud rejoint le
@@ -91,13 +88,13 @@ Pour plus d'informations, consultez [la
documentation](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/).
-### *DaemonSet* `rng`
+#### *DaemonSet* `rng`
Pour réaliser le *DaemonSet* de notre pod `rng`, le plus simple est de partir
d'un export de la ressource existante :
```bash
-kubectl get deploy/rng -o yaml --export > rng.yml
+kubectl get deploy/rng -o yaml > rng.yml
```
La première chose que l'on peut faire, c'est changer le type décrit dans le
@@ -108,13 +105,13 @@ kind: DaemonSet
```
Vous pouvez essayer d'appliquer cette spec, pour voir et essayer de tâtonner
-grâce aux erreurs renvoyés par l'API.
+grâce aux erreurs renvoyées par l'API.
Il vous faudra également retirer le champ `replicas` (qui n'a pas de sens ici,
-vu que la réplication est basé sur les nœuds), les champs `strategy`,
+vu que la réplication est basée sur les nœuds), les champs `strategy`,
`progressDeadlineSeconds`, ainsi que la ligne `status: {}`.
-#### Force !
+##### Force ! {-}
En fait, plutôt que de corriger ces erreurs, on aurait aussi très bien pu
désactiver la validation comme ceci :
@@ -124,14 +121,14 @@ kubectl apply -f rng.yml --validate=false
```
-### Trop de *pods* `rng`
+#### Trop de *pods* `rng` {-}
Après avoir appliqué la nouvelle spec, on constate qu'il y a beaucoup de *pods*
`rng`. En effet, l'ancien *pod* déployé avec la resource *deployment* est
toujours là.
-### Bootleneck résolu ?
+#### Bootleneck résolu ? {-}
Admirez maintenant dans Chronograf si vous avez réussi à augmenter votre nombre
de pépites !
diff --git a/tutorial/k8s/setup.md b/tutorial/k8s/setup.md
index c29551a..397e87f 100644
--- a/tutorial/k8s/setup.md
+++ b/tutorial/k8s/setup.md
@@ -3,12 +3,35 @@
Mise en place
=============
-La mise en place d'un cluster Kubernetes est une opération très longue, car
-elle nécessite l'installation et la configuration de nombreux composants.
-Cette opération n'étant pas très palpitante, nous ne la verrons pas
-aujourd'hui.
+La mise en place d'un cluster Kubernetes ([prononcé
+Ku-ber-né-tèce](https://github.com/kubernetes/kubernetes/issues/44308) en grec
+ancien) est une opération qui peut s'avérer très longue et complexe, car elle
+nécessite l'installation et la configuration de nombreux composants avant de
+pouvoir être utilisé sereinement.
+
+Cette opération n'étant pas très palpitante (c'est beaucoup de lecture de
+documentations et d'heures passées à essayer de faire tomber en marche tous les
+composants d'un seul coup), nous ne la verrons pas aujourd'hui.
+
+D'ailleurs, dans le milieu professionnel, il est plutôt rare de voir des
+entreprises investir dans la gestion de leur propre cluster Kubernetes. La
+plupart des entreprises qui choisissent d'utiliser Kubernetes pour gérer leurs
+infrastructures, choisissent de passer par un prestataire. L'entreprise délègue
+donc la gestion de leur cluster à une autre entreprise, dont c'est la cœur de
+métier. La plupart du temps, il va s'agir d'Amazon (via [Elastic Kubernetes
+Service](https://aws.amazon.com/fr/eks/)), d'Azur ([Kubernetes
+Service](https://azure.microsoft.com/fr-fr/services/kubernetes-service/)) ou
+Google ([Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)), mais
+d'autres acteurs plus petits existent aussi
+([OVHcloud](https://www.ovhcloud.com/fr/public-cloud/kubernetes/), ...).
+
+
+Pour jouer aujourd'hui, deux solutions s'offrent à nous pour commencer à
+utiliser Kubernetes facilement :
+
+- Kubernetes in Docker (kind) : pour tenter l'aventure sur votre machine,
+- Play With Kubernetes : si vous ne vous en sortez pas avec `kind`.
-Pour jouer aujourd'hui, deux solutions s'offrent à nous :
Kubernetes in Docker (kind)
---------------------------
@@ -17,11 +40,11 @@ Kubernetes in Docker (kind)
Docker.
Pour commencer, il nous faudra télécharger le binaire (go, donc statique)
-suivant :
+suivant (il existe pour Linux, macOS et Windows) :
```bash
-curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/v0.6.0/kind-$(uname)-amd64
+curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/v0.9.0/kind-$(uname)-amd64
chmod +x kind
```
@@ -35,9 +58,16 @@ master et 2 workers. Avant de lancer leur création.
```bash
cat > my-cluster.yml <
```bash
-curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.16.3/bin/linux/amd64/kubectl
+curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.19.4/bin/linux/amd64/kubectl
chmod +x kubectl
```
@@ -64,11 +94,15 @@ Une fois que tout sera opérationnel, nous devrions obtenir :
```
42sh$ kubectl version
-Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"archive", BuildDate:"2019-11-26T02:51:51Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
-Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"clean", BuildDate:"2019-11-16T01:01:59Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
+Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.3", GitCommit:"1e11e4a2108024935ecfcb2912226cedeafd99df", GitTreeState:"archive", BuildDate:"2020-11-18T12:02:06Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
+Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.1", GitCommit:"206bcadf021e76c27513500ca24182692aabd17e", GitTreeState:"clean", BuildDate:"2020-09-14T07:30:52Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}
```
+Par défaut, `kubectl` va tenter de contacter le port local 2375, `kind` aura
+pris soin de l'exposer pour vous au moment de la création du cluster.
+
+Passez ensuite à la section 2 si vous avez réussi à mettre en place `kind`.
Play With Kubernetes
--------------------
@@ -135,12 +169,12 @@ kubectl apply -n kube-system -f \
Minikube, Docker for Mac/Windows, MicroK8s, ...
-----------------------------------------------
-Si les solutions précédentes ne sont pas adaptés à votre usage, de nombreuses
+Si les solutions précédentes ne sont pas adaptées à votre usage, de nombreuses
autres applications permettent de mettre en place un cluster plus ou moins
complet, facilement.
Vous pouvez tenter d'utiliser
[minikube](https://kubernetes.io/docs/setup/learning-environment/minikube/),
[microk8s](https://microk8s.io/), ... Notez également que *Docker for Mac* et
-*Docker for Windows* intègrent également Kubernetes depuis quelques versions ;
+*Docker for Windows* intègrent aussi Kubernetes depuis quelques versions ;
il convient de l'activer dans les préférences de l'application.
diff --git a/tutorial/k8s/tutorial-el.md b/tutorial/k8s/tutorial-el.md
new file mode 100644
index 0000000..a67f6fb
--- /dev/null
+++ b/tutorial/k8s/tutorial-el.md
@@ -0,0 +1,15 @@
+---
+title: Ελαφριά εικονικοποίηση -- Πρακτική δουλειά αριθμός 6
+subtitle: κυβερνήτης
+author: Πιέρ-Ολιβιέ *νεμυναιρε* [ῥαφοπώλης]{.smallcaps}
+institute: ΣΠκΠΤ
+date: Πέμπτη 19 Νοεμβρίου 2020
+abstract: |
+ Ο στόχος αυτού του τελευταίου εργαστηρίου είναι να κατανοήσει το κυβερνήτης και την ενορχήστρωση εμπορευματοκιβωτίων.
+
+ \vspace{1em}
+
+ Οι ασκήσεις για αυτό το πρακτικό έργο μπορούν να επιστραφούν στη διεύθυνση <ανδροππήςρε@nemunai.re> το αργότερο την Πέμπτη 26 Νοεμβρίου 2020 στις 11:42 μ.μ., οι ερωτήσεις των μαθημάτων πρέπει επίσης να ολοκληρωθούν πριν από αυτήν την ημερομηνία. Δείτε το τελευταίο μέρος αυτού του εργαστηρίου για λεπτομέρειες.
+
+ Καθώς οι άνθρωποι γνωρίζουν την ασφάλεια των ηλεκτρονικών ανταλλαγών, πρέπει να μου στείλετε τις υπογεγραμμένες αποδόσεις σας με το κλειδί PGP. Θυμηθείτε να [με](https://keys.openpgp.org/search?q=nemunaire%40nemunai.re) υπογράψετε το κλειδί σας και μην διστάσετε [να υπογράψετε το δικό σας](https://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
+...
diff --git a/tutorial/k8s/tutorial.md b/tutorial/k8s/tutorial.md
index 502bddc..cfeef85 100644
--- a/tutorial/k8s/tutorial.md
+++ b/tutorial/k8s/tutorial.md
@@ -1,24 +1,23 @@
---
-title: Virtualisation légère -- TP n^o^ 5
+title: Virtualisation légère -- TP n^o^ 6
subtitle: Kubernetes
author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps}
institute: EPITA
-date: Mercredi 27 novembre 2019
+date: Jeudi 19 novembre 2020
abstract: |
Le but de ce dernier TP est d'appréhender Kubernetes et l'orchestration de
conteneurs.
\vspace{1em}
- Les exercices de ce TP peuvent être rendus à