Start working on tuto 5
This commit is contained in:
parent
50132f3a8f
commit
4fe5c78d9b
12
tutorial/k8s/Makefile
Normal file
12
tutorial/k8s/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
include ../pandoc-opts.mk
|
||||||
|
|
||||||
|
SOURCES_TUTO = tutorial.md setup.md intro.md overview.md discover.md run.md rendu.md
|
||||||
|
|
||||||
|
|
||||||
|
all: tutorial.pdf
|
||||||
|
|
||||||
|
tutorial.pdf: ${SOURCES_TUTO}
|
||||||
|
pandoc ${PANDOCOPTS} -o $@ $+
|
||||||
|
|
||||||
|
clean::
|
||||||
|
rm tutorial.pdf
|
311
tutorial/k8s/discover.md
Normal file
311
tutorial/k8s/discover.md
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Découverte de `kubectl`
|
||||||
|
=======================
|
||||||
|
|
||||||
|
`kubectl`
|
||||||
|
([prononcé](https://www.reddit.com/r/kubernetes/comments/5qthoc/how_should_i_pronounce_kubectl/)
|
||||||
|
'cube C T L', 'cube cuttle', 'kyoob cuddle', 'cube control', ...) est le principal
|
||||||
|
programme que l'on utilise pour interagir avec notre cluster.
|
||||||
|
|
||||||
|
Étant donné qu'il s'agit d'un programme client, qui ne fait rien de plus que
|
||||||
|
discuter avec une API REST HTTP, on peut le considérer comme un gros wrapper au
|
||||||
|
dessus de `curl`.
|
||||||
|
|
||||||
|
Obtenir de l'aide
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl describe type/name
|
||||||
|
kubectl describe type name
|
||||||
|
kubectl explain type
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`get`
|
||||||
|
-----
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get node
|
||||||
|
```
|
||||||
|
|
||||||
|
Plus d'infos :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get nodes -o wide
|
||||||
|
```
|
||||||
|
|
||||||
|
Lisible par une machine :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get no -o yaml
|
||||||
|
kubectl get no -o json
|
||||||
|
```
|
||||||
|
|
||||||
|
On aimera utiliser `jq(1)` avec la sortie `-o json` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get no -o json | \
|
||||||
|
jq ".items[] | {name:.metadata.name} + .status.capacity"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get services
|
||||||
|
kubectl get svc
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour le moment, nous n'avons qu'un seul service, il s'agit de l'API Kubernetes.
|
||||||
|
|
||||||
|
`ClusterIP` désigne l'IP d'un service accessible en interne, pour le cluster.
|
||||||
|
|
||||||
|
|
||||||
|
### Conteneurs actifs
|
||||||
|
|
||||||
|
Jetons un œil aux conteneurs actifs :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods
|
||||||
|
```
|
||||||
|
|
||||||
|
Regardons maintenant les `namespaces` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get namespaces
|
||||||
|
```
|
||||||
|
|
||||||
|
On l'a vu, les *namespaces* ici désignent des espaces de noms qui n'ont rien à
|
||||||
|
voir avec les *namespaces* de Linux. Regardons par exemple les conteneurs d'un
|
||||||
|
autre espace de noms :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 :
|
||||||
|
|
||||||
|
- `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
|
||||||
|
(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.
|
||||||
|
|
||||||
|
|
||||||
|
Mon premier conteneur
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Prêt à lancer notre premier conteneur ?!
|
||||||
|
|
||||||
|
Pas si vite ! En fait ... Kubernetes ne permet pas de lancer de conteneur...
|
||||||
|
Nous devons lancer un *pod*, qui ne contiendra qu'un seul conteneur.
|
||||||
|
|
||||||
|
### Mon premier pod
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl run pingpong --image alpine ping 1.1.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Outre un avertissement, `kubectl` doit indiquer qu'une tâche de déploiement 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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Déploiement³
|
||||||
|
|
||||||
|
Si l'on affiche davantage d'informations, on obtient :
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get all
|
||||||
|
NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/pingpong-7d49d9bc9-k8fpg 1/1 Running 0 123s
|
||||||
|
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2m3s
|
||||||
|
|
||||||
|
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||||
|
deployment.apps/pingpong 1/1 1 1 123s
|
||||||
|
|
||||||
|
NAME DESIRED CURRENT READY AGE
|
||||||
|
replicaset.apps/pingpong-7d49d9bc9 1 1 1 123s
|
||||||
|
```
|
||||||
|
|
||||||
|
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 :
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Pour résumer : `kubectl run` 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
|
||||||
|
|
||||||
|
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` :
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
À ce stade, comme nous ne modifions que le nombre de replicats, Kubernetes va
|
||||||
|
tout simplement propager ce nombre au *replicatset* existant. Puis, le
|
||||||
|
*replicatset* voyant un décalage entre le nombre de pods attendus et le nombre
|
||||||
|
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 ?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl delete pod pingpong-yyyy
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
Pour arrêter nos conteneurs, il convient donc de supprimer la tâche de
|
||||||
|
déploiement :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl delete deploy/pingpong
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Exposer son conteneur
|
||||||
|
|
||||||
|
Exposer un conteneur revient à créer un nouveau service (une *resource*
|
||||||
|
service). Un service est une adresse IP que l'on peut considérer comme stable
|
||||||
|
pour un *pod* ou un groupe de *pods*.
|
||||||
|
|
||||||
|
Il est nécessaire de créer un service si l'on veut pouvoir se connecter à un
|
||||||
|
*pod*.
|
||||||
|
|
||||||
|
Une fois le service créé, le serveur DNS interne va permettre de résoudre le
|
||||||
|
nom du *pod* depuis les autres conteneurs.
|
||||||
|
|
||||||
|
|
||||||
|
#### Types de services
|
||||||
|
|
||||||
|
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.
|
||||||
|
- `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.
|
||||||
|
- `ExternalName` : une entrée DNS est créée pour avoir un alias.
|
||||||
|
|
||||||
|
|
||||||
|
#### Le retour de `youp0m`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create deployment youp0m --image=nemunaire/youp0m
|
||||||
|
```
|
||||||
|
|
||||||
|
Commençons par créer un service `ClusterIP` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl expose deployment youp0m --port 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce qui donne :
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get service
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
youp0m ClusterIP 10.102.129.233 <none> 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.
|
2
tutorial/k8s/dockercoins-diagram.svg
Normal file
2
tutorial/k8s/dockercoins-diagram.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
87
tutorial/k8s/intro.md
Normal file
87
tutorial/k8s/intro.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
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 !
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Fier de respecter le paradigme des micro-services, notre ChocoMiner fonctionne
|
||||||
|
ainsi :
|
||||||
|
|
||||||
|
* le `worker` demande à `rng` de générer un grand nombre aléatoire,
|
||||||
|
* le `worker` envoie ce grand nombre au `hasher`, qui lui retourne un hash,
|
||||||
|
* si le condensat respecte les contraintes pour obtenir une pépite, on est content,
|
||||||
|
* et on recommence, ainsi de suite, pour avoir le maximum de pépites.
|
||||||
|
|
||||||
|
Chaque seconde, le `worker` envoie à `influxdb` le nombre de hashs et de
|
||||||
|
pépites qu'il a ainsi pu obtenir.
|
||||||
|
|
||||||
|
Une interface graphique (`chronograf`) permet d'interroger la base de données
|
||||||
|
pour afficher des statistiques.
|
||||||
|
|
||||||
|
|
||||||
|
Obtenir l'application
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```shell
|
||||||
|
git clone git://git.nemunai.re/chocominer.git
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
Rappels sur la découverte de services
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
Dans Docker, nous avions vu que nous n'avions pas besoin de connaître les IP
|
||||||
|
des conteneurs : un serveur DNS nous permettait de se connecter aux différents
|
||||||
|
services à partir de leurs noms.
|
||||||
|
|
||||||
|
Dans Kubernetes, le même principe s'applique : dans aucun cas, nous ne devrions
|
||||||
|
coder en dur des adresses IP. Il convient d'utiliser au maximum le système de
|
||||||
|
DNS, car les IP sont susceptibles de changer !
|
||||||
|
|
||||||
|
|
||||||
|
Tester avec `docker-compose`
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
`docker-compose up`
|
||||||
|
|
||||||
|
Se connecter à chronograf sur le port qui va bien
|
||||||
|
|
||||||
|
|
||||||
|
Monté en puissance
|
||||||
|
------------------
|
||||||
|
|
||||||
|
`docker-compose up -d --scale worker=2`
|
||||||
|
|
||||||
|
Ok :-)
|
||||||
|
|
||||||
|
`docker-compose up -d --scale worker=10`
|
||||||
|
|
||||||
|
Argh :-(
|
||||||
|
|
||||||
|
|
||||||
|
Identification du goulot d'étranglement
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
De nombreux outils existent pour réaliser des tests de performance, essayons
|
||||||
|
`httping` sur nos différents services pour voir si un service ne serait pas
|
||||||
|
la cause des ralentissements.
|
||||||
|
|
||||||
|
`rng`
|
||||||
|
: `httping -c 3 localhost:8001`
|
||||||
|
|
||||||
|
`hasher`
|
||||||
|
: `httping -c 3 localhost:8002`
|
||||||
|
|
||||||
|
Il semblerait que notre application `rng` nécessite d'être exécuté en parallèle
|
||||||
|
! Mais on ne peut pas faire de répartition de charge facilement avec
|
||||||
|
`docker-compose` !
|
BIN
tutorial/k8s/k8s-archi.png
Normal file
BIN
tutorial/k8s/k8s-archi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
143
tutorial/k8s/overview.md
Normal file
143
tutorial/k8s/overview.md
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Vue d'ensemble de Kubernetes
|
||||||
|
============================
|
||||||
|
|
||||||
|
*Kubernetes* est un système open source d'orchestration et de gestion de
|
||||||
|
conteneurs. C'est-à-dire qu'il se charge de coller constamment aux
|
||||||
|
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
|
||||||
|
*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
|
||||||
|
s'adapter pour répondre aux besoins.
|
||||||
|
|
||||||
|
Par exemple, on ne va pas lui expliquer comment lancer des conteneurs ou
|
||||||
|
récupérer des images ; mais on va lui demander d'avoir 5 conteneurs youp0m
|
||||||
|
lancés, de placer ces conteneurs derrière un load-balancer ; on pourra
|
||||||
|
également lui demander d'adapter la charge pour absorber les pics de trafic
|
||||||
|
(par exemple lors du Black Friday sur une boutique), mais également, il pourra
|
||||||
|
gérer les mises à jour des conteneurs selon différentes méthodes, ...
|
||||||
|
|
||||||
|
|
||||||
|
Architecture de Kubernetes
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Un cluster Kubernetes est composé d'un (ou plusieurs) nœuds *master*, et d'une
|
||||||
|
série de *workers*.
|
||||||
|
|
||||||
|
Sur le master, on retrouve les composants suivants :
|
||||||
|
|
||||||
|
API HTTP
|
||||||
|
: On distingue plusieurs API, elles sont toutes utilisées pour communiquer avec
|
||||||
|
le cluster, pour son administration.
|
||||||
|
|
||||||
|
L'ordonnanceur
|
||||||
|
: Il a la responsabilité de monitorer les ressources utilisées sur chaque nœud
|
||||||
|
et de répartir les conteneurs en fonction des ressources disponibles.
|
||||||
|
|
||||||
|
Le contrôleur
|
||||||
|
: Il va contrôler l'état des applications déployées au sein du cluster, pour
|
||||||
|
s'assurer d'être dans l'état désiré.
|
||||||
|
|
||||||
|
**`etcd`**
|
||||||
|
: Il s'agit d'une base de données clef/valeur, supportant la
|
||||||
|
haute-disponibilité, que Kubernetes emploie comme système de stockage
|
||||||
|
persistant pour les objets d'API.
|
||||||
|
|
||||||
|
|
||||||
|
Chaque nœud[^minion] (généralement, le nœud *master* est également *worker*) est utilisé
|
||||||
|
via deux composants :
|
||||||
|
|
||||||
|
[^minion]: historiquement, avant de parler de *node*, on parlait de
|
||||||
|
*minion*. Vous êtes susceptibles de rencontrer encore ce terme dans
|
||||||
|
certaines documentations.
|
||||||
|
|
||||||
|
`kubelet`
|
||||||
|
: C'est l'agent qui va se charger de créer les conteneurs et les manager, afin
|
||||||
|
de répondre aux spécifications.
|
||||||
|
|
||||||
|
`kube-proxy`
|
||||||
|
: Ce programme va servir de load-balancer pour se connecter aux pods.
|
||||||
|
|
||||||
|
Sans oublier le moteur de conteneurs (généralement Docker), qui va
|
||||||
|
effectivement se charger de lancer les conteneurs demandés par `kubelet`.
|
||||||
|
|
||||||
|
|
||||||
|
Évidemment, chaque élément de l'architecture est malléable à souhait, c'est la
|
||||||
|
raison pour laquelle il peut être très difficile de mettre en place une
|
||||||
|
architecture Kubernetes : avec ou sans haute-disponibilité, un nœud master
|
||||||
|
dédié au contrôle, avec un moteur de conteneur exotique (`rkt`, `ctr`, ...).
|
||||||
|
|
||||||
|
|
||||||
|
*Resources*
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Avec Docker, nous avons eu l'habitude de travailler avec des objets (images,
|
||||||
|
containers, networks, volumes, secrets, ...). Au sein de Kubernetes, cela
|
||||||
|
s'appelle des *resources* et elles sont très nombreuses.
|
||||||
|
|
||||||
|
Parmi les plus courantes, citons les types (désignés *Kind* dans l'API)
|
||||||
|
suivants :
|
||||||
|
|
||||||
|
node
|
||||||
|
: il s'agit d'une machine physique ou virtuelle, de notre cluster.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
service
|
||||||
|
: c'est un point de terminaison (*endpoint*), stable dans le temps, sur lequel
|
||||||
|
on peut se connecter pour accéder à un ou plusieurs
|
||||||
|
conteneurs. Historiquement, appelés portails/*portals*, on les retrouve
|
||||||
|
encore quelques fois désignés ainsi dans de vieux articles.
|
||||||
|
|
||||||
|
namespace
|
||||||
|
: à ne pas confondre avec les *namespaces* Linux. Ici il s'agit d'espaces de
|
||||||
|
noms divers, pour Kubernetes.
|
||||||
|
|
||||||
|
secret
|
||||||
|
: comme `docker secret`, il s'agit d'un moyen de passer des données sensibles à
|
||||||
|
un conteneur.
|
||||||
|
|
||||||
|
Pour voir la liste complète des *resources*, on utilise : `kubectl
|
||||||
|
api-resources`.
|
||||||
|
|
||||||
|
|
||||||
|
Modèle réseau
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Pour Kubernetes, il n'y a qu'un seul gros réseau au sein duquel se retrouve
|
||||||
|
tous les conteneurs. Il ne doit pas y avoir de NAT, que ce soit entre les
|
||||||
|
*pods* ou les *nodes*, chacun doit pouvoir contacter n'importe quel autre
|
||||||
|
élément, sans qu'il y ait de routage.
|
||||||
|
|
||||||
|
C'est un modèle assez simpliste au premier abord, mais en raison de la
|
||||||
|
nécessité de faire un minimum de filtrage, de nombreuses extensions viennent
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
configurer les routes, les règles de pare-feu, ...
|
||||||
|
|
||||||
|
|
||||||
|
Pour aller plus loin
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* [Kubernetes Documentation](https://kubernetes.io/docs/)
|
||||||
|
* [A Reference Architecture for Deploying WSO2 Middleware on Kubernetes](https://medium.com/containermind/a-reference-architecture-for-deploying-wso2-middleware-on-kubernetes-d4dee7601e8e)
|
36
tutorial/k8s/rendu.md
Normal file
36
tutorial/k8s/rendu.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Rendu
|
||||||
|
=====
|
||||||
|
|
||||||
|
Modalités de rendu
|
||||||
|
------------------
|
||||||
|
|
||||||
|
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 <virli@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.
|
||||||
|
|
||||||
|
Par ailleurs, n'oubliez pas de répondre à
|
||||||
|
[l'évaluation du cours](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=309).
|
||||||
|
|
||||||
|
|
||||||
|
Tarball
|
||||||
|
-------
|
||||||
|
|
||||||
|
Tous les exercices de ce TP sont à placer dans une tarball (pas d'archive ZIP,
|
||||||
|
RAR, ...).
|
||||||
|
|
||||||
|
Voici une arborescence type :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
login_x-TP5/cmpns.sh
|
||||||
|
login_x-TP5/mydocker_exec.sh
|
||||||
|
login_x-TP5/myswitch_root.sh
|
||||||
|
```
|
||||||
|
</div>
|
107
tutorial/k8s/run.md
Normal file
107
tutorial/k8s/run.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Cookies dans Kube
|
||||||
|
=================
|
||||||
|
|
||||||
|
Maintenant que nous en savons un peu plus sur Kubernetes, nous allons commencer
|
||||||
|
à déployer notre application ChochMiner 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
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
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`,
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Sur la route du registre auto-hébergé
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create deployment registry --image=registry
|
||||||
|
kubectl expose deploy/registry --port=5000 --type=NodePort
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour tester, vous pouvez :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NODEPORT=$(kubectl get svc/registry -o json | jq .spec.ports[0].nodePort)
|
||||||
|
REGISTRY=127.0.0.1:$NODEPORT
|
||||||
|
curl $REGISTRY/v2/_catalog
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull busybox
|
||||||
|
docker tag busybox $REGISTRY/busybox
|
||||||
|
docker push $REGISTRY/busybox
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Utiliser les images du Docker Hub
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
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`
|
||||||
|
- etc.
|
||||||
|
|
||||||
|
|
||||||
|
Lançons les images standards
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
#### `influxdb` et `chronograf`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create deployment influxdb --image=influxdb
|
||||||
|
kubectl create deployment chronograf --image=chronograf
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Notre application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TAG=v0.1
|
||||||
|
for SERVICE in hasher rng worker; do
|
||||||
|
kubectl create deployment $SERVICE --image=nemunaire/$SERVICE:$TAG
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exposer les ports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl expose deployment influxdb --port 8088
|
||||||
|
kubectl expose deployment rng --port 80
|
||||||
|
kubectl expose deployment hasher --port 80
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create service nodeport chronograf --tcp=8888 --node-port=30001
|
||||||
|
```
|
146
tutorial/k8s/setup.md
Normal file
146
tutorial/k8s/setup.md
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Pour jouer aujourd'hui, deux solutions s'offrent à nous :
|
||||||
|
|
||||||
|
Kubernetes in Docker (kind)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
`kind` est un projet permettant de lancer un cluster kubernetes directement via
|
||||||
|
Docker.
|
||||||
|
|
||||||
|
Pour commencer, il nous faudra télécharger le binaire (go, donc statique)
|
||||||
|
suivant :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```bash
|
||||||
|
curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/v0.6.0/kind-$(uname)-amd64
|
||||||
|
chmod +x kind
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Placez-le dans un endroit où il sera accessible de votre `$PATH`.
|
||||||
|
|
||||||
|
Notre prochaine étape est de décrire le cluster que l'on souhaite avoir : 1
|
||||||
|
master et 2 workers. Avant de lancer leur création.
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```bash
|
||||||
|
cat > my-cluster.yml <<EOF
|
||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.sigs.k8s.io/v1alpha3
|
||||||
|
nodes:
|
||||||
|
- role: control-plane
|
||||||
|
- role: worker
|
||||||
|
- role: worker
|
||||||
|
EOF
|
||||||
|
kind create cluster --config my-cluster.yml
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
La création du cluster peut prendre quelques minutes.
|
||||||
|
|
||||||
|
Profitons-en pour télécharger `kubectl` :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```bash
|
||||||
|
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.16.3/bin/linux/amd64/kubectl
|
||||||
|
chmod +x kubectl
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
C'est via cette commande que nous interagirons principalement avec l'API de
|
||||||
|
Kubernetes.
|
||||||
|
|
||||||
|
Une fois que tout sera opérationnel, nous devrions obtenir :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
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"}
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
Play With Kubernetes
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
De la même manière que pour les TP utilisant Docker, si vous avez des
|
||||||
|
difficultés pour réaliser les exercices sur vos machines, vous pouvez utiliser
|
||||||
|
le projet [Play With K8s](https://play-with-k8s.com/) qui vous donnera accès à
|
||||||
|
un bac à sable avec lequel vous pourrez réaliser tous les exercices de ce TP.
|
||||||
|
|
||||||
|
Il nous faut créer plusieurs instances, disons 3 : parmi elles, 1 instance sera
|
||||||
|
la master, nous l'utiliserons principalement, les deux autres ne feront
|
||||||
|
qu'exécuter des conteneurs, nous pourrons les oublier dès qu'on les aura
|
||||||
|
connectées au master.
|
||||||
|
|
||||||
|
Pour initialiser notre cluster Kubernetes, nous allons devoir créer notre
|
||||||
|
master. Pour cela, dans notre première instance, nous allons taper :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```bash
|
||||||
|
kubeadm init --apiserver-advertise-address $(hostname -i)
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Cette action peut prendre quelques minutes, et devrait se finir, si tout se
|
||||||
|
passe bien, par :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
Your Kubernetes master has initialized successfully!
|
||||||
|
|
||||||
|
To start using your cluster, you need to run (as a regular user):
|
||||||
|
|
||||||
|
mkdir -p $HOME/.kube
|
||||||
|
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
|
||||||
|
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
||||||
|
|
||||||
|
You should now deploy a pod network to the cluster.
|
||||||
|
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
|
||||||
|
http://kubernetes.io/docs/admin/addons/
|
||||||
|
|
||||||
|
You can now join any number of machines by running the following on each node
|
||||||
|
as root:
|
||||||
|
|
||||||
|
kubeadm join --token SOMETOKEN SOMEIPADDRESS --discovery-token-ca-cert-hash SOMESHAHASH
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Recopions ensuite la commande `kubeadm join ...` donnée dans le terminal, dans
|
||||||
|
nos deux autres instances. Cela permettra aux machines de se connecter au
|
||||||
|
master.
|
||||||
|
|
||||||
|
Dernière étape pour la mise en place de notre cluster, il s'agit de définir un
|
||||||
|
profil de politique réseau, sur le master (nous n'exécuterons plus de commande
|
||||||
|
sur les autres workers) :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```bash
|
||||||
|
kubectl apply -n kube-system -f \
|
||||||
|
"https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 |tr -d '\n')"
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
Minikube, Docker for Mac/Windows, MicroK8s, ...
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Si les solutions précédentes ne sont pas adaptés à 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 ;
|
||||||
|
il convient de l'activer dans les préférences de l'application.
|
24
tutorial/k8s/tutorial.md
Normal file
24
tutorial/k8s/tutorial.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: Virtualisation légère -- TP n^o^ 5
|
||||||
|
subtitle: Kubernetes
|
||||||
|
author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps}
|
||||||
|
institute: EPITA
|
||||||
|
date: Mercredi 27 novembre 2019
|
||||||
|
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 à <virli@nemunai.re> au
|
||||||
|
plus tard le mercredi 11 décembre 2019 à 23 h 42, des questions de
|
||||||
|
cours sont également à compléter avant cette date sur
|
||||||
|
Epitaf. Consultez la dernière partie de ce TP pour les modalités.
|
||||||
|
|
||||||
|
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/).
|
||||||
|
...
|
Loading…
x
Reference in New Issue
Block a user