12 KiB
\newpage
Découverte de kubectl
kubectl
(prononcé
'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
Commençons par apprivoiser kubectl
en prenant quelques
renseignements et surtout en apprennant comment obtenir de l'aide :
Les type
s que vous pouvez découvrir sont ceux que l'on a vu à la
sections précédentes : node
, pod
, ...
La commande describe
permet d'afficher l'état tel qu'il est attendu
et tel qu'il est actuellement (cela permet de se rendre lorsque les
deux divergent).
get
Une autre manière, moins verbeuse, de récupérer des informations est
d'utiliser get
:
kubectl get node
On peut ajouter des options pour avoir plus d'infos :
kubectl get nodes -o wide
... ou rendre la sortie lisible par une machine :
kubectl get no -o yaml
kubectl get no -o json
On aimera utiliser jq(1)
avec la sortie -o json
:
kubectl get no -o json | \
jq ".items[] | {name:.metadata.name} + .status.capacity"
Services
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 :
kubectl get pods
Regardons maintenant les namespaces
:
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 :
kubectl -n kube-system get pods
Eh oui ! De nombreux services de base pour Kubernetes tournent dans des conteneurs, gérés par lui-même... notamment :
etcd
: notre base de données clef/valeur,kube-apiserver
: l'API REST avec qui communiquekubectl
,kube-controller-manager
etkube-scheduler
, deux autres composants indispensables,coredns
: un composant additionnel pour gérer la résolution de noms internes (pour ne 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
kubectl run pingpong --image alpine ping 1.1.1.1
kubectl
doit nous indiquer nous qu'un pod a été créé.
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 1/1 Running 0 123s
Sortie d'un conteneur \
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
:
kubectl logs pingpong
ou bien :
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 :
kubectl delete pods 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. Nous allons
plutôt créer des tâches de déploiement.
Essayons sans plus attendre de lancer nos ping
à travers une tâche de déploiement :
kubectl create deployment pingpong --image=alpine -- ping 8.8.8.8
Si l'on regarde maintenant la sortie de kubectl get all
, on obtient :
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 <none> 443/TCP 123h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/pingpong 0/1 1 0 123s
NAME DESIRED CURRENT READY AGE
replicaset.apps/pingpong-98f6d5899 1 1 0 123s
Oula, on a vraiment lancé tout ça ?!
Pas de panique, on peut très facilement le décortiquer :
Les tâches de déploiement (deployment.apps) sont des ressources de 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 3.14 vers alpine edge), mais éventuellement de revenir sur la version X si besoin, en cours de migration. Elles délèguent ensuite aux replicatsets la gestion des pods.
Le replicatset est là pour indiquer le nombre de pods que l'on désire et s'assurer que le nombre de pods actuellement lancé est bien en adéquation avec le nombre de pods attendu. \
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
.
Pasage à l'échelle : facile ?
Pour lancer 3 ping
s en parallèle, modifions la tâche de déploiement comme suit :
kubectl scale deploy/pingpong --replicas 3
À 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 ?
kubectl delete pod pingpong-yyyy
Cela supprime bien un pod, mais un autre est relancé instantanément car le replicatset constate une différence dans le nombre attendu.
Si nous voulons arrêter de DDoS Google/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 recréera un similaire (avec de nouveaux pods).
Pour arrêter nos conteneurs, il convient donc de supprimer la tâche de déploiement :
kubectl delete deploy pingpong
Exposer son conteneur
Exposer un conteneur revient à créer un nouveau service (une ressource 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 à 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 fournit un load-balancer (typiquement AWS, GCE, Azure, ...), un serviceNodePort
est créé pour utiliser ce load-balancer externe.ExternalName
: une entrée DNS est créée pour avoir un alias.
Le retour de youp0m
\
Déployons maintenant l'image youp0m
pour voir comment utiliser les services :
kubectl create deployment youp0m --image=nemunaire/youp0m
Commençons par créer un service ClusterIP
:
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.
Si vous passez par kind
, vous pouvez constater le bon fonctionnement grâce à :
docker exec -it kind-control-plane curl 10.102.129.233: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 :
kubectl create -f https://virli.nemunai.re/insecure-dashboard.yaml
::::: {.warning}
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 :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard NodePort 10.96.78.69 <none> 80:31505/TCP 3m10s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6m51s
Regardons si cela répond :
$ docker exec -it kind-control-plane curl 10.96.78.69:80
<p class="browsehappy">You are using an <strong>outdated</strong> 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 avec
kind
, 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 :
- 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 :
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 http://localhost:30002/ (vous pouvez Skip l'authentification, dans cette configuration d'exemple, elle n'est pas nécessaire).