virli/tutorial/k8s/discover.md

312 lines
8.8 KiB
Markdown
Raw Normal View History

2019-11-26 15:00:39 +00:00
\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...
2019-11-27 12:11:20 +00:00
Nous devons lancer un *pod* (qui ne contiendra qu'un seul conteneur).
2019-11-26 15:00:39 +00:00
### Mon premier pod
```bash
kubectl run pingpong --image alpine ping 1.1.1.1
```
2019-11-27 12:11:20 +00:00
Outre un avertissement, `kubectl` doit indiquer nous qu'une tâche de
déploiement a été créée.
2019-11-26 15:00:39 +00:00
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.