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 !
|
||||
|
||||
![ChocoMiner](dockercoins-diagram.svg)
|
||||
|
||||
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
|
||||
--------------------------
|
||||
|
||||
![Architecture de Kubernetes](k8s-archi.png)
|
||||
|
||||
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…
Reference in New Issue
Block a user