Vous êtes sur la page 1sur 388

Kubernetes -Guide

pratique

Sébastien Goasguen et Michael Hausenblas


Kubernetes - Guide pratique

Traduction française publiée et vendue avec l’autorisation de


O’Reilly Media, Inc. de Kubernetes Cookbook
ISBN 9781491979686 © 2018 Sébastien Goasguen et Michael
Hausenblas.

© 2019 Éditions First, un département d’Édi8


12, avenue d’Italie
75013 Paris – France
Tél. : 01 44 16 09 00
Fax : 01 44 16 09 01

Courriel : firstinfo@editionsfirst.fr
Site Internet : www.editionsfirst.fr

ISBN : 978-2-412-04964-8
ISBN numérique : 9782412052877
Dépôt légal : décembre 2019

Traduction de l’anglais : Olivier Engler


Mise en page : Pierre Brandeis

Cette œuvre est protégée par le droit d’auteur et strictement


réservée à l’usage privé du client. Toute reproduction ou
diffusion au profit de tiers, à titre gratuit ou onéreux, de
tout ou partie de cette œuvre est strictement interdite et
constitue une contrefaçon prévue par les articles L 335-2 et
suivants du Code de la propriété intellectuelle. L’éditeur se
réserve le droit de poursuivre toute atteinte à ses droits de
propriété intellectuelle devant les juridictions civiles ou
pénales.

Ce livre numérique a été converti initialement au format


EPUB par Isako www.isako.com à partir de l'édition papier
du même ouvrage.
Introduction

Bienvenue à vous dans ce livre, et merci de l’avoir choisi !


Nous avons voulu en l’écrivant vous aider à résoudre des
problèmes concrets concernant Kubernetes. Vous y
trouverez plus de 80 recettes qui couvrent des domaines
aussi différents que la mise en place d’un cluster,
l’administration d’une charge de travail dans des conteneurs
avec des objets API de Kubernetes, l’utilisation des
primitives de stockage, les configurations de sécurité et les
extensions de Kubernetes. Que vous fassiez vos premiers pas
ou que vous soyez déjà acclimaté à Kubernetes, nous
espérons que vous trouverez ici des techniques utiles pour
améliorer votre expérience de votre maîtrise de Kubernetes.
Lectorat principal
Vous êtes peut-être un développeur qui va basculer vers un
travail dans le cloud, ou bien un administrateur système, ou
encore, vous avez été envoyé en éclaireur dans les nouvelles
terres que parcourent les rôles dits « DevOps ». Dans tous
les cas, ce livre va vous aider à tracer votre route dans la
jungle de Kubernetes, du développement à la production. Les
techniques ne sont pas présentées de façon linéaire, en
commençant par les actions les plus élémentaires. Chaque
chapitre utilise les concepts et les primitives API
fondamentales de Kubernetes, parmi d’autres.
Motivation des auteurs
Les deux auteurs du livre utilisent et ont contribué au
développement de Kubernetes depuis plusieurs années. Nous
avons ainsi pu constater quels étaient les problèmes sur
lesquels butaient le plus souvent les débutants, mais aussi
les utilisateurs plus avancés. Nous avons voulu partager les
connaissances réunies en utilisant Kubernetes en
production, ainsi qu’en développement avec Kubernetes.
Nous avons en effet contribué à la base de code du noyau, à
l’écosystème et nous avons bien sûr écrit des applications
fonctionnant sur Kubernetes.
Structure du livre
Ce recueil de techniques comporte 14 chapitres. Chaque
section décrit une technique écrite dans un format
standardisé : Problème – Solution – Discussion. Vous
pouvez lire le livre dans l’ordre des pages, ou passer
directement à un chapitre ou une section qui vous intéresse
sur le moment. Les sections sont indépendantes les unes des
autres. Lorsque des concepts d’une autre section sont
nécessaires, nous fournissons une référence. N’hésitez pas à
profiter de l’index ; certaines techniques sont en effet
l’occasion d’utiliser une commande en particulier, et c’est
grâce à l’index que vous trouverez où cette commande est
mentionnée.
À propos des versions de
Kubernetes
Au moment d’écrire ce livre, la dernière version la plus
stable était la 1.7. C’est la version utilisée tout au long du
livre en tant que référence. Les techniques de ce livre
devraient toutes pouvoir fonctionner avec des versions plus
anciennes, au minimum jusqu’à la version 1.4. Lorsque ce ne
sera pas le cas, nous le mentionnerons en rappelant la
version minimale requise.

À partir de 2017, le rythme des mises à jour de Kubernetes


est devenu trimestriel, qu’il s’agisse d’une version mineure
ou d’une sous-mineure. Par exemple, la version 1.6 a été
publiée en mars 2017, la 1.7 en juin, la 1.8 en septembre et
la 1.9 en décembre. Vous pouvez vous attendre à ce qu’une
fonction soit supportée dans trois sous-versions en même
temps (voir la stratégie de mise à jour Kubernetes à
l’adresse
https://github.com/eBay/Kubernetes/blob/master/doc
s/design/versioning.md). Ainsi, les objets de l’interface
API considérés comme stables dans la version 1.7 ont été
supportés au moins jusqu’en mars 2018. Le livre n’utilise
quasiment que des interfaces API stables, et les extraits
devraient fonctionner dans les versions plus récentes de
Kubernetes.

N .d. T. : Nous avons choisi de conserver la même version de


Kubernetes pour la version française. Nous n’avons donc pas
confronté les exemples à sa plus récente version. Si des
incompatibilités mineures apparaissent, vous saurez les résoudre
puisque ce livre est destiné à des personnes accoutumées à devoir
gérer l’inéluctable prix à payer pour bénéficier du progrès
permanent que constituent les nouvelles versions : traquer et
résoudre les incompatibilités entre versions de librairies devant
cohabiter.
Environnement technique
présupposé
Ce livre vise les utilisateurs de niveau intermédiaire,
supposés maîtriser un certain nombre de concepts en termes
de développement et d’administration système. Voici les
éléments qu’il est conseillé de savoir utiliser pour bien
exploiter ce livre.

bash (interpréteur shell Unix)


Il s’agit de l’interpréteur shell Unix par défaut sous Linux
comme sous macOS. Il est donc conseillé de savoir utiliser
cet interpréteur pour intervenir sur les fichiers, modifier les
droits d’accès et les privilèges des utilisateurs, déplacer des
fichiers et faire un minimum de programmation de script
bash. Si nécessaire, revoyez un livre décrivant l’interpréteur
shell bash.

Gestion des paquetages


Plusieurs des outils présentés par ce livre sont dépendants
d’autres paquetages qu’il faut donc installer. Il est
nécessaire de savoir utiliser le système de gestion de
paquetages correspondant à votre système. Lorsqu’il s’agit
de Ubuntu/Debian, l’outil correspond à apt ; sous
CentosRHEL, il s’agit de yum et sous macOS, vous utiliserez
port ou brew. Dans tous les cas, vous devez savoir comment
installer, mettre à jour et supprimer un paquetage.

Git
Git est devenu le standard des systèmes de contrôle des
versions distribuées (versionneur). Si vous connaissez déjà
l’un des outils CVS et SVN, mais pas Git, n’hésitez pas à vous
y intéresser. Si nécessaire, procurez-vous un livre ou un
tutoriel qui lui est dédié. Le site Web de GitHub
(http://github.com) est un excellent point de départ pour
créer un référentiel personnel. Pour plus d’informations au
sujet de GitHub, visitez la page
http://training.github.com qui comporte des tutoriels
interactifs (http://try.github.io).

Langage Python
Nous invitons systématiquement nos étudiants, après avoir
appris à programmer en C et C++ ou Java, à adopter en outre
un langage de script. Fut un temps où Perl régnait dans ce
domaine, mais de nos jours ce sont plutôt Ruby et Go qui
prennent l’avantage. La plupart des exemples du livre
utilisent le langage Python, mais il y a quelques exemples
Ruby. Vous trouverez chez le même éditeur plusieurs livres à
propos du langage Python.

Langage Go
Kubernetes est écrit en langage Go, l’un des nouveaux
langages de prédilection dans de nombreuses start-up, pour
de nombreux projets open source de niveau système. Ce livre
ne présente pas la programmation en Go, mais montre
comment compiler certains projets Go. Il n’est pas inutile
d’avoir un minimum de connaissances de la mise en place
d’un environnement de travail Go. Pour en savoir plus, nous
vous renvoyons au tutoriel disponible (http://bit.ly/go-
intro).

N.d.T. : Pour une utilisation locale des outils de Kubernetes, il vous


faudra éventuellement mettre en place une machine virtuelle, par
exemple Virtual Box ou KVM.
Fichiers source des exemples
Pour chaque chapitre, tous les codes source des exemples
sont réunis dans un sous-répertoire correspondant. Tous les
fichiers sont réunis dans une archive au format ZIP que nous
vous invitons à télécharger depuis la page dédiée au livre sur
le site de l’éditeur :

www.editionsfirst.fr

Recherchez le titre du livre grâce à la zone de recherche,


puis dans la fiche du livre, cliquez sur Contenu
additionnel, et enfin sur Télécharger.

N’hésitez pas à consulter l’éventuel fichier LISEZMOI.txt que vous


trouverez dans l’archive des exemples si des remarques
complémentaires ou des points de mise à jour rendent l’existence de
ce fichier justifiée.

Les fichiers des exemples de la version anglaise sont


disponibles sur le site GitHub. Pour les récupérer, il suffit de
demander un clone du référentiel à l’adresse suivante :

$ git clone https://github.com/k8s-


cookbook/recipes

Entrez ensuite dans le sous-répertoire correspondant au chapitre


désiré et récupérez le fichier dont vous avez besoin.
Les exemples fournis ne constituent pas des versions
optimisées à utiliser telles quelles en production. Ils
proposent une configuration minimale de base pour illustrer
les techniques décrites.
Conventions typographiques
Pour rendre plus aisée l’exploitation du contenu du livre,
nous avons pris soin de mettre en valeur certains termes par
un codage visuel à portée sémantique, lorsqu’ils
apparaissent dans le corps du texte :

Concept

Mise en valeur de la première apparition d’un terme


essentiel.

MotClé

Nous imprimons ainsi toutes les entités Java fondamentales


définies par le langage : noms des types, des méthodes, des
fonctions, des opérateurs et des traits.

NomVariable

Codage réservé aux noms des variables et d’objets choisis


par le développeur.

Commande

Codage utilisé pour les noms de commandes et d’options de


menus.

NomFichier
Codage mettant en exergue les noms de fichiers et de
répertoires (dossiers), ainsi que les chemins d’accès et
adresses Web locales.

AdresseWeb

Codage des adresses Web.

Cette icône marque un paragraphe contenant une astuce ou une


précision.

Cette icône marque une mise en garde ou bien souligne un concept


pouvant être à l’origine d’une équivoque.

Cette icône marque une précision technique.


Réutilisation des exemples
Ce livre a été conçu pour vous aider à réaliser votre travail.
Les exemples de code source peuvent être utilisés dans vos
programmes comme dans vos documentations. Vous n’avez
pas besoin de nous demander l’autorisation préalable, sauf à
réutiliser tels quels les codes complets. Vous pouvez tout à
fait écrire un programme qui utilise plusieurs portions de
code. En revanche, vous devrez nous demander avant de
commercialiser un support de stockage contenant des
exemples de ce livre. Vous pouvez citer ces exemples pour
répondre à une question dans un forum. En revanche, la
réutilisation du code source dans un autre livre requiert une
autorisation préalable.

Vous n’êtes pas forcé de nous citer, mais si vous en avez


envie, vous pouvez par exemple indiquer Kubernetes – Guide
pratique par Sébastien Goasguen et Michael Hausenblas.
Pour nous contacter
Vous pouvez contacter les éditions F1RST à l’adresse
suivante, en indiquant comme sujet « Kubernetes » :

firstinfo@efirst.com

Remerciements
Nous remercions toute la communauté Kubernetes d’avoir
créé un logiciel de cette qualité. C’est une communauté de
haut niveau, à l’esprit ouvert et toujours prête à vous venir
en aide.

Pour la réalisation de ce livre, nous tenons à remercier pour


leur relecture avisée Ihor Dvoretski, Liz Rice et Ben Hall.
CHAPITRE 1
Pour débuter avec Kubernetes

Les sections de ce premier chapitre se proposent de vous


guider dans vos premiers pas avec Kubernetes. Nous verrons
comment s’en servir sans l’installer et présenterons des
outils tels que l’interface sur ligne de commande kubectl et
le tableau de bord dashboard qui permet d’entrer en
interaction avec un cluster (grappe). Nous verrons également
l’outil Minikube qui constitue une solution complète
utilisable depuis un ordinateur portable.
Utiliser Kubernetes sans
l’installer

Problème
Vous voulez tester Kubernetes sans devoir l’installer
d’abord.

Solution
Rendez-vous sur le site de Katacoda
(https://www.katacoda.com/courses/kubernetes/play-
ground) qui constitue un terrain de jeu Kubernetes. Vous
devez vous authentifier auprès de GitHub ou d’un des
réseaux sociaux les plus répandus. Vous verrez alors
apparaître la page présentée en Figure 1.1.
Figure 1.1 : Vue générale du terrain de jeu Kubernetes de Katacoda.

Précisons que l’environnement que vous démarrez dans ce


contexte n’est disponible que pour un temps limité
(actuellement, une heure), mais tout est gratuit et
directement accessible depuis un navigateur.

L’autre solution pour découvrir Kubernetes sans l’installer


consiste à suivre le tutoriel interactif sur le site Web de
Kubernetes :

https://kubernetes.io/fr/docs/tutorials/

La partie interactive se fonde sur Katacoda.

N. d. T. : Saluons la formidable dynamique dont fait preuve la


communauté Kubernetes pour adapter la base documentaire en
plusieurs langues dont le français.
Installer kubectl (le client
Kubernetes)

Problème
Vous désirez installer l’interface sur ligne de commande de
Kubernetes pour interagir avec un cluster Kubernetes.

Solution
Vous disposez de trois solutions pour installer kubectl :

• télécharger les fichiers compressés tarball du code


source ;

• utiliser un gestionnaire de paquetages ;

• reconstruire l’outil depuis le code source (voir


Chapitre 13).

Parmi les approches proposées dans la documentation


d’installation, la plus simple consiste à télécharger la
version officielle la plus récente. Voici comment procéder,
par exemple sous Linux :

$ curl -LO
https://storage.googleapis.com/kubernetes-
release/release/ \
$(curl -s
https://storage.googleapis.com/kubernetes-
release/ \
release/stable.txt) \
/bin/linux/amd64/kubectl

$ chmod +x ./kubectl

$ sudo mv ./kubectl /usr/local/bin/kubectl

Notez que l’outil curl doit être présent. S’il ne l’est pas, vous
l’installez par exemple avec sudo apt install curl.

Sous macOS, vous installez l’outil kubectl avec brew :

$ brew install kubectl

Ceux d’entre vous qui utilisent le moteur Google Kubernetes


Engine (voir Chapitre 2) disposent d’office de kubectl dans le
cadre de l’installation de la commande gcloud. Voici
comment le vérifier (ici, sur la machine de Sébastien) :

$ which kubectl
/Users/sebgoa/google-cloud-sdk/bin/kubectl

Sachez que les versions récentes de l’outil Minikube (voir


section « Installer Minikube pour utiliser une instance
locale de Kubernetes ») contiennent kubectl et l’installent
avec accès par la variable $PATH s’il n’est pas trouvé.
Avant de passer à une autre section, vous devez vérifier que
votre outil kubectl est fonctionnel, par exemple en affichant
son numéro de version. La commande tente aussi de
récupérer le numéro de version du cluster Kubernetes par
défaut :

$ kubectl version
Client Version: version.Info{Major:"1", \
Minor:"7", \
GitVersion:"v1.7.0",
\

GitCommit:"fff5156...", \

GitTreeState:"clean", \
BuildDate:"2017-03-
28T16:36:33Z", \
GoVersion:"go1.7.5",
\
Compiler:"gc", \

Platform:"darwin/amd64"}
...

Voir aussi
• Documentation d’installation de kubectl
(https://kubernetes.io/docs/tasks/kubectl/insta
ll/)
Installer Minikube pour utiliser
une instance locale de
Kubernetes

Problème
Vous voulez utiliser Kubernetes pour faire des tests, du
développement ou pour vous former sur votre machine
locale.

Solution
Utilisez Minikube qui permet d’accéder à Kubernetes depuis
une machine locale sans rien installer, sauf les binaires de
Minikube. Cet outil utilise un hyperviseur local (par exemple
VirtualBox ou KVM) pour lancer une machine virtuelle qui
exécute Kubernetes en mode simple. Vérifiez donc que vous
avez installé une machine virtuelle sur votre système.

Vous pouvez installer l’interface client de Minikube


localement en récupérant la plus récente version ou
recompiler depuis les sources. Voici par exemple comment
récupérer la version 0.18.0 sur une machine Linux :
$ curl -Lo minikube
https://storage.googleapis.com/minikube/releases
/v0.18.0/ \
minikube-linux-amd64

$ chmod +x minikube

$ sudo mv minikube /usr/local/bin/

Les binaires de Minikube deviennent ensuite accessibles par


la variable de chemin d’accès depuis n’importe quel
répertoire.

Discussion
Une fois que vous avez installé Minikube, vérifiez qu’il est
prêt à l’emploi en demandant son numéro de version :

$ minikube version
minikube version: v0.18.0

Pour démarrer l’outil, procédez ainsi :

$ minikube start

Une fois la phase de démarrage achevée, le client de


Kubernetes kubectl va immédiatement utiliser le contexte
Minikube. Vous pouvez vérifier les nœuds présents dans
votre cluster et récupérer ainsi le nom d’hôte de Minikube :
$ kubectl get nodes
NAME STATUS AGE
minikube Ready 5d

Voir aussi
• Documentation de Minikube
(https://kubernetes.io/docs/getting-started-
guides/minikube/)

• Code source de Minikube sur GitHub


(https://github.com/kubernetes/minikube)
Développer localement avec
Minikube

Problème
Vous avez besoin de tester votre application Kubernetes
localement avec Minikube pour en poursuivre le
développement. Vous avez bien sûr installé et démarré
Minikube (voir section « Installer Minikube pour utiliser
une instance locale de Kubernetes »). Vous avez besoin de
connaître quelques commandes pour simplifier votre
développement.

Solution
L’interface cliente Minikube propose un certain nombre de
commandes pour vous simplifier la vie ainsi qu’une aide
interne permettant de se remémorer les sous-commandes.
En voici un extrait :

$ minikube
...
Available Commands:
addons Modify minikube's kubernetes addons.
...
start Starts a local kubernetes cluster.
status Gets the status of a local kubernetes
cluster.
stop Stops a running local kubernetes
cluster.
version Print the version of minikube.

En complément des trois commandes indispensables start,


stop et delete, vous aurez intérêt à apprendre à maîtriser
ip, ssh, dashboard et docker-env.

Minikube exploite un moteur Docker pour pouvoir démarrer des


conteneurs. Pour pouvoir accéder à ce moteur depuis votre machine
locale avec le client Docker local, il est nécessaire de configurer
correctement l’environnement Docker avec la commande minikube
docker-env.

Discussion
Pour démarrer une machine virtuelle VM qui exécute
localement Kubernetes, vous utilisez la commande minikube
start. Sans mention contraire, elle réserve 2 Go de mémoire
vive. Vous ne devez pas oublier de les restituer au moyen de
la commande minikube stop. Pour réclamer un plus grand
espace mémoire pour la machine virtuelle et plus de
puissance CPU tout en choisissant la version de Kubernetes à
utiliser, procédez ainsi :

$ minikube start --cpus=4 --memory=4000 --


kubernetes-version=v1.7.2
Si vous avez besoin de déboguer le démon Docker utilisé
dans Minikube, vous pourrez trouver la commande ssh
pratique. Elle permet d’ouvrir une session dans la machine
virtuelle. Pour connaître l’adresse IP de la VM de Minikube,
utilisez la commande minikube ip. Enfin, pour accéder au
tableau de bord Kubernetes dans le navigateur, vous utilisez
minikube dashboard.

Si vous constatez que Minikube devient instable, ou si vous avez


besoin de redémarrer, utilisez en séquence minikube stop puis
minikube delete et enfin minikube start.
Démarrer une application sur
Minikube

Problème
Vous avez démarré Minikube (voir section « Installer
Minikube pour utiliser une instance locale de Kubernetes »)
et vous voulez maintenant démarrer votre première
application dans Kubernetes.

Solution
En guise d’exemple, voyons comment démarrer la plate-
forme de microblogue Ghost (https://ghost.org) dans
Minikube. Deux commandes kubectl sont nécessaires :

$ kubectl run ghost --image=ghost:0.9


$ kubectl expose deployments ghost --port=2368 -
-type=NodePort

Vous interrogez manuellement l’état du code pour savoir


quand il a démarré. Une fois cela fait, vous utilisez la
commande minikube service pour accéder à Ghost en
ouvrant automatiquement le navigateur :
$ kubectl get pods
NAME READY STATUS RESTARTS
AGE
ghost-8449997474-kn86m 1/1 Running 0
2h

$ minikube service ghost

Discussion
La commande run de kubectl est un générateur qui permet
de créer un objet de type Deployment (voir Chapitre 4). La
commande expose de kubectl est également un générateur,
sauf qu’elle permet de créer un objet de type Service (voir
Chapitre 5). Ce genre d’objet sert à router le trafic réseau
vers les conteneurs qui ont été démarrés par votre
déploiement.
Accéder au tableau de bord
Minikube

Problème
Dans Minikube, vous voulez visualiser le tableau de bord de
Kubernetes pour pouvoir démarrer votre application depuis
une interface utilisateur graphique.

Solution
Voici comment ouvrir le tableau de bord Kubernetes depuis
Minikube :

$ minikube dashboard

Dans l’angle supérieur droit de la fenêtre, cliquez le signe +


pour voir apparaître la page montrée en Figure 1.2.
Figure 1.2 : Aperçu de la vue de création d’applications du tableau de bord.

Discussion
Pour créer une application, utilisez dans le coin supérieur
droit le bouton Create puis attribuez un nom à l’application
et choisissez l’image Docker à utiliser. Cliquez ensuite le
bouton Deploy pour faire apparaître une fenêtre montrant
les déploiements et les jeux de réplication. Après quelques
instants, vous voyez apparaître un pod. (Nous décrirons
dans la suite du livre certaines primitives API essentielles.)

La Figure 1.3 montre le tableau de bord après création d’une


application avec le conteneur Redis.
Pour obtenir les mêmes informations en mode texte sur la
ligne de commande, revenez à votre terminal et saisissez la
commande suivante :

$ kubectl get pods,rs,deployments


NAME READY STATUS
RESTARTS AGE
po/redis-3215927958-4x88v 1/1 Running 0
24m

NAME DESIRED CURRENT READY


AGE
rs/redis-3215927958 1 1 1 24m

NAME DESIRED CURRENT UP-TO-


DATE AVAILABLE AGE
deploy/redis 1 1 1
1 24m
Figure 1.3 : Aperçu du tableau de bord avec une application Redis.

Le pod Redis exécute le serveur Redis, comme le montre le


journal :
CHAPITRE 2
Créer un cluster Kubernetes

Découvrons dans ce chapitre les différentes façons de mettre


en place un cluster Kubernetes complet. Nous verrons l’outil
de bas niveau standardisé (kubeadm) qui sert de
soubassement à d’autres outils d’installation. Nous verrons
où trouver les binaires pour le plan de contrôle, ainsi que
pour les nœuds travailleurs (workers), comment configurer
Kubernetes en conteneur avec hyperkube, puis comment
écrire des fichiers d’unité systemd pour superviser les
composants Kubernetes. Nous verrons pour finir comment
mettre en place des clusters dans Google Cloud et dans
Microsoft Azure.
Installer kubeadm pour créer un
cluster Kubernetes

Problème
Vous voulez utiliser kubeadm pour amorcer (bootstrap) un
cluster Kubernetes à partir de zéro.

Solution
Vous téléchargez l’outil client kubeadm depuis le référentiel
du paquetage Kubernetes.

L’outil kubeadm doit être installé sur tous les serveurs qui
vont faire partie du cluster Kubernetes et pas seulement sur
le serveur maître, donc sur tous les nœuds.

Par exemple, si vos machines hôtes sont sous Ubuntu, vous


devez réaliser les opérations suivantes en tant que super-
utilisateur root sur chaque hôte pour préparer le référentiel
du paquetage Kubernetes :

# apt-get update && apt-get install -y apt-


transport-https
# curl -s
https://packages.cloud.google.com/apt/doc/apt-
key.gpg | apt-key add -
# cat <<EOF
>/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial
main
EOF
# apt-get update

Vous pouvez ensuite passer à l’installation du moteur


Docker et des différents outils Kubernetes. Quatre
composants sont à installer :

• le binaire kubelet ;

• l’interface client kubeadm ;

• l’interface client kubectl ;

• l’extension enfichable kubernetes-cni (Container


Networking Interface).

Pour les installer, lancez les deux commandes suivantes :

# apt-get install -y docker.io


# apt-get install -y kubelet kubeadm kubectl
kubernetes-cni

Discussion
Une fois tous les binaires et outils installés, vous pouvez
commencer l’amorçage de votre cluster Kubernetes.
Procédez à l’initialisation du cluster sur le nœud maître
ainsi :

# kubeadm init
[kubeadm] WARNING: kubeadm is in beta, please do
not use it for production
clusters.
[init] Using Kubernetes version: v1.7.8
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks
...

Une fois l’initialisation achevée, la commande qui sera


proposée doit être exécutée sur tous les nœuds travailleurs
(voir prochaine section). Cette commande exploite un jeton
(token) qui a été généré par le processus d’initialisation.

Voir aussi
• Utilisation de kubeadm pour créer un cluster
(https://kubernetes.io/docs/setup/independent/c
reate-cluster-kubeadm/)
Amorcer un cluster Kubernetes
avec kubeadm

Problème
Vous avez initialisé votre nœud maître Kubernetes (voir
section précédente). Vous devez maintenant ajouter des
nœuds travailleurs à ce cluster.

Solution
Le référentiel du paquetage Kubernetes doit avoir été
configuré et l’outil kubeadm installé, comme indiqué dans la
précédence section. Utilisez la commande join en lui
fournissant le jeton qui a été généré lors de l’étape
d’initialisation sur le nœud maître :

$ kubeadm join --token <jeton>

Rebasculez dans la session du terminal maître pour


constater que les nœuds ont été joints :

$ kubectl get nodes

Discussion
La dernière étape consiste à créer un réseau conforme aux
contraintes réseau Kubernetes, notamment en attribuant
une seule adresse IP par pod. Servez-vous pour ce faire
d’une des extensions réseau disponibles (voir aussi
l’installation des extensions dans la documentation
Kubernetes). Vous pouvez par exemple installer l’outil
Weave Net sur un cluster Kubernetes à partir de la
version 1.6.0 avec une seule commande kubectl :

$ export kubever=$(kubectl version | base64 | tr


-d '\n')
$ kubectl apply -f
"https://cloud.weave.works/k8s/net?k8s-
version=$kubever"

Cette commande provoque la création de jeux de démons


(voir Chapitre 7) qui vont être exécutés sur tous les nœuds
du cluster. Ces jeux de démons exploitent le réseau de la
machine hôte ainsi qu’une extension plug-in CNI afin de
configurer le réseau de nœuds local. Une fois que le réseau
est configuré, vos nœuds de cluster basculent dans l’état
READY (prêt).

Nous vous renvoyons à la documentation pour les autres


extensions réseau utilisables pour créer un réseau de pods
pendant le processus d’amorçage avec kubeadm
(https://kubernetes.io/docs/setup/independent/creat
e-cluster-kubeadm/#pod-network).
Voir aussi
• La documentation de création d’un cluster avec
kubeadm
(https://kubernetes.io/docs/setup/independent/c
reate-cluster-kubeadm/)

• Installer une extension add-on dans Kubernetes


(https://kubernetes.io/docs/concepts/cluster-
administration/addons/)

• Weaveworks
(https://www.weave.works/docs/net/latest/kube-
addon/)
Télécharger une version de
Kubernetes depuis GitHub

Problème
Vous avez besoin de télécharger une version officielle de
Kubernetes au lieu de la compiler à partir des fichiers de
code source.

Solution
Vous pouvez procéder manuellement en vous rendant sur la
page de diffusion des versions sur GitHub
(https://github.com/kubernetes/kubernetes/releases)
. Choisissez une version ou une préversion, puis choisissez le
paquet de fichiers à compiler ou bien le fichier portant le
nom kubernetes.tar.gz.

Une autre approche consiste à d’abord lister les noms des


versions les plus récentes via l’interface API GitHub, comme
ceci :

$ curl -s
https://api.github.com/repos/kubernetes/kubernet
es/releases | \
jq -r .[].assets[].browser_download_url
https://github.com/kubernetes/kubernetes/release
s/download/v1.9.0/kubernetes.tar.gz
https://github.com/kubernetes/kubernetes/release
s/download/v1.9.0-beta.2/kubernetes.tar.gz
https://github.com/kubernetes/kubernetes/release
s/download/v1.8.5/kubernetes.tar.gz
https://github.com/kubernetes/kubernetes/release
s/download/v1.9.0-beta.1/kubernetes.tar.gz
https://github.com/kubernetes/kubernetes/release
s/download/v1.7.11/kubernetes.tar.gz
...

Vous pouvez ensuite télécharger le fichier kubernetes.tar.gz


de la version choisie. Voici par exemple comment récupérer
la version 1.7.11 :

$ wget
https://github.com/kubernetes/kubernetes/release
s/download/ \
v1.7.11/kubernetes.tar.gz

Si vous voulez compiler Kubernetes depuis les fichiers


source, voyez le Chapitre 13.

Pensez à vérifier l’intégrité du fichier archive kubernetes.tar.gz au


moyen de sa somme de hachage. La valeur SHA256 est indiquée sur
la page GitHub de la version. Une fois que vous avez téléchargé le
fichier archive, faites générer la somme de hachage pour la
comparer. Même si la version n’est pas signée, contrôler la somme
de hachage vous rassure sur l’intégrité du fichier.
Télécharger les binaires client et
serveur

Problème
Vous avez téléchargé une archive de version (voir précédente
section), mais elle ne contient pas les fichiers binaires.

Solution
Pour éviter que les fichiers d’archive deviennent trop
volumineux, ils ne contiennent pas les fichiers binaires. Il
faut les télécharger dans un deuxième temps. Pour ce faire,
il suffit d’exécuter le script nommé get-kube-binaries.sh,
comme ceci :

$ tar -xvf kubernetes.tar.gz


$ cd kubernetes/cluster
$ ./get-kube-binaries.sh

Une fois le processus achevé, les binaires du client seront


dans client/bin :

$ tree ./client/bin
./client/bin
├── kubectl
└── kubefed

Les binaires du serveur seront dans


server/kubernetes/server/bin :

$ tree server/kubernetes/server/bin
server/kubernetes/server/bin
├── cloud-controller-manager
├── kube-apiserver
...

Si vous voulez télécharger directement les fichiers binaires du client


ou du serveur sans passer par l’étape de release, rendez-vous à
l’adresse https://dl.k8s.io. Voici par exemple comment obtenir
les binaires du client et du serveur sous Linux pour la version 1.7.11 :

$ wget https://dl.k8s.io/v1.7.11/kubernetes-
client-linux-amd64.tar.gz
$ wget https://dl.k8s.io/v1.7.11/kubernetes-
server-linux-amd64.tar.gz
Utiliser une image hyperkube
pour exécuter un nœud maître
Kubernetes avec Docker

Problème
Vous avez besoin de créer un nœud maître Kubernetes avec
plusieurs conteneurs Docker. Vous avez besoin d’exécuter
dans les conteneurs le serveur API, le planificateur
scheduler, le contrôleur et le magasin de clés/valeurs etcd.

Solution
Vous pouvez vous appuyer sur un binaire hyperkube, ainsi
qu’un conteneur etcd. En effet, hyperkube est un binaire
complet qui est disponible sous forme d’une image Docker.
Vous pouvez vous en servir pour démarrer tous les processus
Kubernetes.

La création d’un cluster Kubernetes suppose de mettre en


place une solution de stockage pour mémoriser l’état du
cluster. Dans Kubernetes, la solution correspond à un
magasin distribué de clés/valeurs qui porte le nom etcd.
Vous devez donc d’abord démarrer une instance de etcd de
la façon suivante :
$ docker run -d \
--name=k8s \
-p 8080:8080 \
gcr.io/google_containers/etcd:3.1.10 \
etcd --data-dir /var/lib/data

Vous pouvez ensuite démarrer le serveur API au moyen


d’une image hyperkube qui contient les binaires du serveur
API. Cette image est disponible dans le référentiel GCR
(Google Container Registry) à l’adresse
gcr.io/google_containers/hyperkube:v1.7.11. Vous
remplacez bien sûr le numéro de version 1.7.11 par celui de la
version que vous utilisez. Nous nous servons ici de quelques
paramètres pour pouvoir faire fonctionner le serveur API de
façon non sécurisée sur un port local :

$ docker run -d \
--net=container:k8s \

gcr.io/google_containers/hyperkube:v1.7.11 \
/apiserver --etcd-
servers=http://127.0.0.1:2379 \
--service-cluster-ip-range=10.0.0.1/24 \
--insecure-bind-address=0.0.0.0 \
--insecure-port=8080 \
--admission-control=AlwaysAdmit
Il ne reste plus qu’à démarrer le contrôleur d’admission qui
va pointer vers le serveur API :

$ docker run -d \
--net=container:k8s \

gcr.io/google_containers/hyperkube:v1.7.11 \
/controller-manager --
master=127.0.0.1:8080

Le serveur API, etcd et le contrôleur-manager partagent le


même espace de noms réseau, ce qui leur permet de
dialoguer à l’adresse 127.0.0.1 bien qu’ils s’exécutent dans
des conteneurs différents.

Pour vérifier que votre configuration est fonctionnelle,


utilisez etcdctl dans le conteneur etcd en demandant
d’afficher la liste du contenu de son répertoire /registry :

$ docker exec -ti k8s /bin/sh


# export ETCDCTL_API=3
# etcdctl get "/registry/api" --prefix=true

Vous pouvez également entrer en contact avec votre serveur


API Kubernetes et commencer à explorer l’API :

$ curl -s curl http://127.0.0.1:8080/api/v1 |


more
{
"kind": "APIResourceList",
"groupVersion": "v1",
"resources": [
{
"name": "bindings",
"singularName": "",
"namespaced": true,
"kind": "Binding",
"verbs": [
"create"
]
},
...

Pour le moment, vous n’avez pas démarré le scheduler, ni


mis en place les nœuds avec kubelet et kube-proxy. Nous
venons seulement de voir comment exécuter le serveur API
Kubernetes en démarrant trois conteneurs locaux.

Il vous sera parfois utile de pouvoir utiliser l’image Docker


d’hyperkube pour vérifier certaines options de configuration d’un
binaire Kubernetes. Voici par exemple comment vérifier l’aide de la
commande principale /apiserver :

$ docker run --rm -ti \

gcr.io/google_containers/hyperkube:v1.7.11 \
/apiserver --help

Discussion
Cette procédure est très pratique pour commencer à explorer
les différents composants de Kubernetes de façon locale,
mais elle est déconseillée dans une configuration destinée à
être mise en production.

Voir aussi
• Images Docker d’hyperkube
(https://github.com/kubernetes/kubernetes/tree/
master/cluster/images/hyperkube)
Écrire un fichier unité systemd
pour exécuter des composants
Kubernetes

Problème
Vous avez fait vos premiers pas avec Minikube (voir
Chapitre 1) et vous savez comment amorcer un cluster
Kubernetes avec kubeadm (voir la section « Amorcer un
cluster Kubernetes avec kubeadm »). Vous voulez
maintenant installer un cluster à partir de zéro. Il vous faut
exécuter les composants Kubernetes avec des fichiers
d’unité systemd. Vous n’avez pour l’instant besoin que d’un
exemple simple d’exécution de kubelet via systemd.

Solution
L’outil systemd sert à gérer des systèmes et des services ;
on parle parfois de système init. Il est devenu le système de
gestion de services par défaut sous Ubuntu 16.04 et
CentOS 7.

En étudiant comment fonctionne kubeadm, vous aurez une


bonne idée de la façon dont vous devez procéder à votre
tour. Si vous regardez en détail la configuration de kubeadm,
vous verrez que le kubelet qui s’exécute sur chacun des
clusters, y compris le maître, est géré par systemd.

Voyons cela par un exemple. Vous pouvez le reproduire en


vous connectant à n’importe quel nœud d’un cluster qui a
été construit avec kubeadm (voir la section « Amorcer un
cluster Kubernetes avec kubeadm ») :

# systemctl status kubelet


• kubelet.service - kubelet: The Kubernetes Node
Agent
Loaded: loaded
(/lib/systemd/system/kubelet.service; enabled;
vendor preset: enabled)
Drop-In: /etc/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Tue 2017-06-13
08:29:33 UTC; 2 days ago
Docs: http://kubernetes.io/docs/
Main PID: 4205 (kubelet)
Tasks: 17
Memory: 47.9M
CPU: 2h 2min 47.666s
CGroup: /system.slice/kubelet.service
├─4205 /usr/bin/kubelet --
kubeconfig=/etc/kubernetes/kubelet.conf \
| --require-
kubeconfig=true \
| --pod-manifest-
path=/etc/kubernetes/manifests \
| --allow-
privileged=true \
| --network-
plugin=cni \
| --cni-conf
└─4247 journalctl -k -f

Cette commande fournit un lien vers le fichier unité de


systemd, dans /lib/systemd/system/ kubelet.service et un lien
vers sa configuration dans
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.

Le fichier d’unité est très simple, puisqu’il se contente de


pointer sur le binaire kubelet qui est installé dans /usr/bin :

[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=http://kubernetes.io/docs/

[Service]
ExecStart=/usr/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target

Le fichier de configuration permet de voir comment est


démarré le binaire kubelet :
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--
kubeconfig=/etc/kubernetes/kubelet.conf
--require-kubeconfig=true"
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-
manifest-path=/etc/kubernetes/manifests
--allow-privileged=true"
Environment="KUBELET_NETWORK_ARGS=--network-
plugin=cni
--cni-conf-dir=/etc/cni/net.d --cni-
bin-dir=/opt/cni/bin"
Environment="KUBELET_DNS_ARGS=--cluster-
dns=10.96.0.10
--cluster-domain=cluster.local"
Environment="KUBELET_AUTHZ_ARGS=--authorization-
mode=Webhook
--client-ca-
file=/etc/kubernetes/pki/ca.crt"
ExecStart=
ExecStart=/usr/bin/kubelet
$KUBELET_KUBECONFIG_ARGS
$KUBELET_SYSTEM_PODS_ARGS $KUBELET_NETWORK_
ARGS $KUBELET_DNS_ARGS $KUBELET_AUTHZ_ARGS
$KUBELET_EXTRA_ARGS

Toutes les options indiquées sont des options de démarrage


du binaire kubelet, par exemple --kubeconfig et sont
définies par la variable d’environnement
$KUBELET_CONFIG_ARGS.
Discussion
Le fichier d’unité que nous venons de voir ne gère que
kubelet. Vous pouvez écrire des fichiers d’unité pour tous les
autres composants d’un cluster Kubernetes : le serveur API,
le contrôleur manager, le planificateur scheduler ou le
proxy). Vous trouverez des exemples de fichiers d’unité pour
chacun de ces composants sur le site Kubernetes the Hard
Way.

Cela dit, vous n’avez que kubelet à démarrer. L’option de


configuration --pod-manifest-path permet de transmettre
le nom d’un répertoire dans lequel kubelet va chercher des
fichiers manifestes dont il va démarrer automatiquement le
contenu. Avec kubeadm, ce répertoire sert à donner accès au
manifeste du serveur API, au scheduler, à etcd et au
contrôleur manager. Autrement dit, Kubernetes sait se gérer
lui-même ; le seul élément qui est géré par systemd est le
processus kubelet lui-même.

Nous pouvons vérifier cela en listant le contenu du


répertoire /etc/kubernetes/manifests dans votre cluster basé
sur kubeadm :

# ls -l /etc/kubernetes/manifests
total 16
-rw------- 1 root root 1071 Jun 13 08:29
etcd.yaml
-rw------- 1 root root 2086 Jun 13 08:29 kube-
apiserver.yaml
-rw------- 1 root root 1437 Jun 13 08:29 kube-
controller-manager.yaml
-rw------- 1 root root 857 Jun 13 08:29 kube-
scheduler.yaml

En lisant le contenu du manifeste etcd.yaml, nous voyons


qu’il s’agit d’un pod avec un seul conteneur qui exécute
etcd :

# cat /etc/kubernetes/manifests/etcd.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
- command:
- etcd
- --listen-client-urls=http://127.0.0.1:2379
- --advertise-client-urls=http://127.0.0.1:2379
- --data-dir=/var/lib/etcd
image:
gcr.io/google_containers/etcd-amd64:3.0.17
...

Voir aussi
• Options de configuration de kubelet

• freedesktop.org, « system »

• Kubernetes the Hard Way, « Bootstrapping the Kubernetes


Control Plane »
(https://github.com/kelseyhightower/kubernetes-
the-hard-way/blob/master/docs/08-bootstrap-
ping-kubernetes-controllers.md ? )
Créer un cluster Kubernetes
dans GKE

Problème
Vous avez besoin de créer un cluster Kubernetes dans GKE
(Google Kubernetes Engine).

Solution
En utilisant l’interface sur ligne de commande gcloud, vous
créez un cluster Kubernetes au moyen de la commande
container clusters create :

$ gcloud container clusters create oreilly.

En l’absence d’option spécifique, vous créez ainsi un cluster


Kubernetes avec trois nœuds de calcul. Le nœud maître est
géré par le service GKE ; vous ne pouvez pas y accéder.

Discussion
Pour pouvoir utiliser GKE, trois actions préalables sont
requises :
• création d’un compte sur la plate-forme Google Cloud
avec la facturation activée ;

• création d’un projet et activation du service GKE dans ce


projet ;

• installation de l’outil sur ligne de commande gcloud


sur votre machine.

Vous pouvez tirer profit de Google Cloud Shell pour


simplifier la mise en place de gcloud. Il s’agit d’une solution
en ligne basée navigateur.

Une fois que le cluster existe, vous pouvez en lister le


contenu ainsi :

$ gcloud container clusters list


NAME ZONE MASTER_VERSION
MASTER_IP ... STATUS
oreilly europe-west1-b 1.7.8-gke.0
35.187.80.94 ... RUNNING

L’outil gcloud permet de redimensionner le cluster, de le mettre à


jour et de l’actualiser :

...
COMMANDS
...
resize
Resizes an existing cluster for running
containers.
update
Update cluster settings for an existing
container cluster.
upgrade
Upgrade the Kubernetes version of an
existing container cluster.

N’oubliez pas de supprimer votre cluster lorsque vous avez


fini de l’utiliser, si vous ne voulez pas être facturé
inutilement pour son utilisation :

$ gcloud container clusters delete oreilly

Voir aussi
• Démarrage rapide de GKE
(https://cloud.google.com/container-
engine/docs/quicks-tart)

• Démarrage rapide de Google Cloud Shell


(https://cloud.google.com/shell/docs/qui-
ckstart)
Créer un cluster Kubernetes
dans ACS (Azure Container
Service)

Problème
Vous avez besoin de créer un cluster Kubernetes sur Azure
Container Service (ACS).

Solution
Pour réaliser cette procédure, vous devez d’abord créer un
compte gratuit Azure (https://azure.microsoft.com/en-
us/free/) puis installer l’outil d’interface sur ligne de
commande az en
version 2.0 (https://docs.microsoft.com/en-
us/cli/azure/install-azure-cli).

Vérifiez d’abord que vous disposez de la bonne version de


l’outil client puis ouvrez une session :

$ az --version | grep ^azure-cli


azure-cli (2.0.13)

$ az login
To sign in, use a web browser to open the page
https://aka.ms/devicelogin and
enter the code XXXXXXXXX to authenticate.
[
{
"cloudName": "AzureCloud",
"id": "****************************",
"isDefault": true,
"name": "Free Trial",
"state": "Enabled",
"tenantId": "*****************************",
"user": {
"name": "******@hotmail.com",
"type": "user"
}
}
]

En guise de préparation, demandez la création d’un groupe


de ressources Azure (l’équivalent d’un projet dans Google
Cloud) en lui donnant le nom k8s. Ce groupe va réunir
toutes vos ressources, et notamment les machines virtuelles
et les composants réseau, ce qui simplifiera ensuite le
nettoyage et la restitution :

$ az group create --name k8s --location


northeurope
{
"id":
"/subscriptions/************************/resourc
eGroups/k8s",
"location": "northeurope",
"managedBy": null,
"name": "k8s",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null
}

Si vous ne savez pas quelle région choisir pour le paramètre --


location, utilisez la commande az account list-locations et
choisissez une région proche.

Maintenant que vous disposez de votre groupe de ressources


k8s, vous pouvez demander la création du cluster avec un
seul nœud de calcul (nœud que l’on appelle un agent dans
Azure) :

$ az acs create --orchestrator-type kubernetes \


--resource-group k8s \
--name k8scb \
--agent-count 1 \
--generate-ssh-keys
waiting for AAD role to propagate.done
{
...
"provisioningState": "Succeeded",
"template": null,
"templateLink": null,
"timestamp": "2017-08-13T19:02:58.149409+00:00"
},
"resourceGroup": "k8s"
}

Précisons que cette commande de création create peut


prendre un quart d’heure pour rendre la main.

Si vous utilisez un compte Azure de test gratuit, vous n’avez pas


assez de quotas pour créer un cluster Kubernetes avec trois agents,
et vous verrez un message dans le style suivant :

Operation results in exceeding quota limits of Core.

Maximum allowed : 4, Current in use : 0, Additional


requested : 8.

Pour contourner ce problème, soit vous créez un cluster plus


petit avec par exemple --agent-count 1 (comme nous
l’avons fait ci-dessus), soit vous contractez un abonnement
payant.

Le portail Azure devrait vous montrer quelque chose dans le


style de la Figure 2.1. Cherchez le groupe de ressources k8s,
puis naviguez jusqu’à la page de déploiement.
Figure 2.1 : Aperçu du portail Azure montrant les déploiements ACS dans le groupe de
ressources k8s.

Vous pouvez, à partir de ce moment, vous connecter à votre


cluster :

$ az acs kubernetes get-credentials --resource-


group=k8s --name=k8scb

Vous pouvez ensuite interroger l’environnement pour


vérifier la configuration :

$ kubectl cluster-info
Kubernetes master is running at https://k8scb-
k8s-143f1emgmt.northeurope.cloudapp
.azure.com
Heapster is running at https://k8scb-k8s-
143f1emgmt.northeurope.cloudapp.azure
.com/api/v1/namespaces/kube-
system/services/heapster/proxy
KubeDNS is running at https://k8scb-k8s-
143f1emgmt.northeurope.cloudapp.azure
.com/api/v1/namespaces/kube-
system/services/kube-dns/proxy
kubernetes-dashboard is running at
https://k8scb-k8s-143f1emgmt.northeurope
.cloudapp.azure.com/api/v1/namespaces/kube-
system/services/kubernetes-dashboard
/proxy
tiller-deploy is running at https://k8scb-k8s-
143f1emgmt.northeurope.cloudapp
.azure.com/api/v1/namespaces/kube-
system/services/tiller-deploy/proxy
To further debug and diagnose cluster problems,
use 'kubectl cluster-info dump'.

$ kubectl get nodes


NAME STATUS
AGE VERSION
k8s-agent-1a7972f2-0 Ready 7m
v1.7.8
k8s-master-1a7972f2-0 Ready,SchedulingDisabled
7m v1.7.8

La seconde commande ci-dessus confirme que nous avons


mis en place un maître et un nœud de calcul (un agent).

Lorsque vous avez fini d’utiliser ACS, pensez à arrêter le


cluster et à supprimer toutes les ressources, en supprimant
directement le groupe de ressources k8s :

$ az group delete --name k8s --yes --no-wait

La commande de suppression rend la main immédiatement,


mais elle peut demander environ un quart d’heure pour que
toutes les ressources (machines virtuelles, réseaux virtuels
et disques) soient réellement supprimées ou libérées puis le
groupe de ressources détruit. Pensez à vérifier dans le
portail Azure que tout a bien été réalisé comme demandé.

Si vous ne voulez ou ne pouvez pas installer l’outil client Azure CLI,


vous pouvez depuis votre navigateur vous servir de l’interpréteur
Azure Cloud Shell pour installer votre cluster Kubernetes.

Voir aussi
• Dans la documentation de Microsoft Azure, la page
consacrée au déploiement de clusters Kubernetes pour
conteneurs Linux (https://docs.microsoft.com/en-
us/azure/contai-ner-
service/kubernetes/container-service-
kubernetes-walkthrough)
CHAPITRE 3
Apprendre à utiliser le client
Kubernetes

Ce chapitre réunit quelques sections abordant les principes


d’utilisation de l’outil sur ligne de commande kubectl de
Kubernetes. Son installation a été présentée dans le
Chapitre 1 ; des cas d’utilisation plus complexes seront
présentés dans le Chapitre 6, dans lequel nous aborderons
l’interface API de Kubernetes.
Obtenir la liste des ressources

Problème
Vous voulez connaître la liste des ressources Kubernetes
d’un certain type.

Solution
Utilisez la commande get de kubectl en indiquant le type de
ressource. Voici comment lister tous les pods :

$ kubectl get pods

Pour obtenir la liste de tous les services et déploiements :

$ kubectl get services,deployments

Pour la liste d’un déploiement en particulier :

$ kubectl get deployment monappk8s

Pour obtenir la liste de toutes les ressources :

$ kubectl get all


Précisons que la commande get est très élémentaire, mais
très utile pour obtenir rapidement une vue d’ensemble de ce
qui se passe dans un cluster. C’est un peu l’équivalent de la
commande ps sous Unix.

La plupart des ressources acceptent un nom abrégé avec kubectl, ce


qui fait gagner du temps. Voici quelques exemples :

• configmaps (abrégée en cm)

• daemonsets (abrégée en ds)

• deployments (abrégée en deploy)

• endpoints (abrégée en ep)

• events (abrégée en ev)

• horizontalpodautoscalers (abrégée en hpa)

• ingresses (abrégée en ing)

• namespaces (abrégée en ns)

• nodes (abrégée en no)

• persistentvolumeclaims (abrégée en pvc)

• persistentvolumes (abrégée en pv)

• pods (abrégée en po)

• replicasets (abrégée en rs)


• replicationcontrollers (abrégée en rc)

• resourcequotas (abrégée en quota)

• serviceaccounts (abrégée en sa)

• services (abrégée en svc)


Supprimer des ressources

Problème
Certaines de vos ressources ne vous sont plus utiles et vous
voulez vous en débarrasser.

Solution
Utilisez la commande delete de kubectl en indiquant le
type et le nom de la ressource à supprimer.

Voici comment supprimer toutes les ressources dans


l’espace de noms mon_app :

$ kubectl get ns
NAME STATUS AGE
default Active 2d
kube-public Active 2d
kube-system Active 2d
my-app Active 20m
$ kubectl delete ns my-app
namespace "my-app" deleted

La création d’un espace de noms est décrite dans le


Chapitre 6.
Vous pouvez supprimer une ressource en particulier et
influencer le processus de suppression. Voici par exemple
comment supprimer les services et les déploiements qui
sont marqués par

app=niceone :

$ kubectl delete svc,deploy -l app=niceone

Voici comment forcer la suppression d’un code :

$ kubectl delete pod hangingpod --grace-period=0


--force

Pour supprimer tous les pods dans l’espace de noms test,


procédez ainsi :

$ kubectl delete pods --all --namespace test

Discussion
Interdisez-vous de supprimer des objets supervisés tels que
des pods contrôlés directement par un déploiement. Vous
devez soit supprimer les superviseurs, soit appliquer des
opérations spécifiques pour vous débarrasser des ressources
gérées. C’est ainsi que vous supprimez effectivement tous
les pods sur lesquels se fonde un déploiement si vous le
rétrécissez jusqu’à n’avoir que zéro réplica (ou réplique)
(voir le Chapitre 9).

Vous devez par ailleurs garder à l’esprit le mécanisme de


cascade des suppressions. Par exemple, lorsque vous
supprimez une définition de ressource spécifique CRD
(Custom Resource Definition) comme montré au Chapitre 13, à
la section « Enrichir l’API par des définitions de ressources
personnalisées (CRD) », cela provoque également la
suppression de tous les objets dépendants. N’hésitez pas à
consulter la documentation Kubernetes concernant la
collecte des rebuts (garbage collection), pour en savoir plus
sur les stratégies de suppression en cascade et comment
intervenir sur leur déroulement.
Surveiller les changements de
ressources avec kubectl

Problème
Vous voulez surveiller les changements que subissent les
objets Kubernetes de façon interactive dans votre fenêtre de
terminal.

Solution
La commande kubectl dispose d’une option --watch prévue
dans ce sens. Voici par exemple comment placer les pods
sous surveillance :

$ kubectl get pods --watch

Sachez que cette commande est bloquante et


autoactualisante, comme la commande classique

top.

Discussion
Cette option --watch est pratique, mais pas toujours très
fiable en termes de réactualisation de l’affichage. Vous
pouvez dans ce cas utiliser plutôt la commande watch :

$ watch kubectl get pods


Éditer des ressources avec
kubectl

Problème
Vous avez besoin de modifier les propriétés d’une ressource
Kubernetes.

Solution
Servez-vous de la commande edit de l’outil kubectl en
indiquant le type de ressource :

$ kubectl run nginx --image=nginx


$ kubectl edit deploy/nginx

Utilisez ensuite votre éditeur pour modifier le déploiement


de nginx, par exemple en réglant le nombre de réplicas à la
valeur 2. Dès que vous enregistrez votre travail, vous voyez
un message de confirmation dans le style suivant :

deployment "nginx" edited

Discussion
Si vous avez un souci au niveau de l’outil d’édition, utiliser
EDITOR=vi. Notez que toutes les modifications ne
déclenchent pas un déploiement.

Vous disposez de raccourcis pour certains déclenchements.


Par exemple, pour changer la version d’image utilisée par un
déploiement, spécifiez kubectl set image. Cela provoque
la mise à jour des images de conteneurs existants pour les
ressources (applicable aux déploiements, aux contrôleurs de
réplication et jeux de réplicas, aux jeux de démons, aux jobs
et aux pods simples).
Demander une description des
ressources des champs à
kubectl

Problème
Vous avez besoin de mieux connaître un certain type de
ressource, par exemple, services, ou bien de savoir à quoi
sert un champ, en particulier dans un manifeste Kubernetes,
avec par exemple un rappel des valeurs par défaut et du
statut obligatoire ou facultatif du champ.

Solution
Vous vous servez de la commande explain de kubectl :

$ kubectl explain svc


DESCRIPTION:
Service is a named abstraction of software
service (for example, mysql) consisting of local
port (for example 3306) that the proxy listens
on, and the selector that determines which
pods will answer requests sent through the
proxy.

FIELDS:
Status <Object>
Most recently observed status of the
service. Populated by the system. Read-only.
More
info:
https://git.k8s.io/community/contributors/devel/
api-conventions.md#spec-and-
status/
apiVersion <string>
APIVersion defines the versioned schema of
this representation of an object. Servers
should convert recognized schemas to the
latest internal value, and may reject
unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/
api-
conventions.md#resources
kind <string>
Kind is a string value representing the
REST resource this object represents. Servers
may infer this from the endpoint the
client submits requests to. Cannot be updated.
In
CamelCase. More info:
https://git.k8s.io/community/contributors/devel/
api-conventions
.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/
api-conventions.md#metadata
spec <Object>
Spec defines the behavior of a service.
https://git.k8s.io/community/
contributors/devel/
api-conventions.md#spec-and-status/

$ kubectl explain svc.spec.externalIPs


FIELD: externalIPs <[]string>

DESCRIPTION:
externalIPs is a list of IP addresses for
which nodes in the cluster will also accept
traffic for this service. These IPs are
not managed by Kubernetes. The user is
responsible
for ensuring that traffic arrives at a
node with this IP. A common example is external
load-balancers that are not part of the
Kubernetes system.

Discussion
Cette commande explain extrait les descriptions des
ressources des champs à partir des définitions
Swagger/OpenAPI qui sont rendues accessibles par le
serveur API.

Voir aussi
• Article de blog de Ross Kukulinski « kubectl
explain — #HeptioProTip »
(https://blog.heptio.com/kubectl-explain-
heptioprotip-ee883992a243)

• Documentation de référence de kubectl concernant


explain
(https://kubernetes.io/docs/reference/generated
/kubectl/kubectl-commands#explain)

• Description de l’API Kubernetes


(https://kubernetes.io/docs/concepts/overview/k
ubernetes-api/)
CHAPITRE 4
Créer et modifier des charges de
travail simples

Nous allons voir dans ce chapitre comment gérer les


principaux types de charges de travail que sont les pods et
les déploiements. Nous verrons comment les créer sur la
ligne de commande, puis par un fichier manifeste YAML.
Nous verrons enfin comment redimensionner et mettre à
jour un déploiement.
Créer un déploiement avec
kubectl

Problème
Vous avez besoin de lancer rapidement une application à
longue durée de vie, par exemple un serveur Web.

Solution
Utilisez la commande kubectl run qui est un générateur
permettant de créer un manifeste de déploiement de façon
impromptue. Voici par exemple comment créer un
déploiement pour démarrer la plate-forme de micro-
blogging Ghost :

$ kubectl run ghost --image=ghost:0.9

$ kubectl get deploy/ghost


NAME DESIRED CURRENT UP-TO-DATE AVAILABLE
AGE
ghost 1 1 1 0 16s

Discussion
La commande run accepte un certain nombre de paramètres
pour configurer les déploiements. En voici quelques-uns :

• réglage des variables d’environnement avec –env ; ;

• définition d’un numéro de port de conteneur avec --


port ;

• indication d’une commande à exécuter avec --command


;

• création automatique d’un service associé avec --


expose ;

• indication du nombre de pods avec --replicas.

Parmi les usages habituels, voici comment démarrer Ghost


sur le port 2368, en créant un service associé :

$ kubectl run ghost --image=ghost:0.9 --


port=2368 --expose

Voici comment lancer MySQL en définissant le mot de passe


root :

$ kubectl run mysql --image=mysql:5.5 --


env=MYSQL_ROOT_PASSWORD=root

Pour lancer un conteneur nommé buzybox en exécutant la


commande sleep 3600 au démarrage, saisissez :
$ kubectl run myshell --image=busybox --command
-- sh -c "sleep 3600"

Pour d’autres détails au sujet des paramètres, voyez l’aide


par cubectl run --help.
Créer des objets à partir d’un
fichier manifeste

Problème
Au lieu de créer un objet avec un générateur tel que cubectl
run, vous voulez définir ses propriétés, puis le créer.

Solution
Utilisez la commande kubectl create ainsi :

$ kubectl create -f <manifeste>

Nous indiquons dans la section « Créer un espace de noms


pour éviter les collisions » du Chapitre 6 comment créer un
espace de noms avec un manifeste YAML. L’exemple qui suit
est volontairement simplifié et le manifeste est très court.
Vous pouvez l’écrire dans un des formats YAML ou JSON.
Voici par exemple un fichier manifeste portant le nom
myns.yaml :

Listing 4.1 : myns.yaml

apiVersion: v1
kind: Namespace
metadata:
name: myns

Pour créer ce genre d’objet, vous utilisez la commande


suivante :

$ kubectl create -f myns.yaml

Discussion
Vous pouvez indiquer pour l’option -f une adresse URL ou
un chemin d’accès à un fichier de votre système local. Voici
par exemple comment créer la partie frontale de
l’application canonique Guestbook. Nous récupérons d’abord
l’adresse URL du fichier brut YAML qui définit l’application
dans un seul fichier manifeste et nous pouvons ensuite
saisir la commande suivante :

$ kubectl create -f
https://raw.githubusercontent.com/kubernetes/kub
ernetes/ \

master/examples/guestbook/frontend-
deployment.yaml
Rédiger un manifeste de pod

Problème
Vous voulez écrire le contenu d’un fichier manifeste de pod
sans utiliser de générateur tel que kubectl run.

Solution
Un pod correspond à un objet /api/v1. Comme tous les
autres objets de Kubernetes, son fichier manifeste contient
les champs suivants :

• apiversion spécifie la version de l’API ;

• kind indique le type de l’objet ;

• metadata fournit quelques métadonnées concernant


l’objet ;

• spec indique la spécification de l’objet.

Ce manifeste contient un tableau de définitions de


conteneurs et un autre tableau facultatif de définition de
volumes (voir aussi le Chapitre 8). Voici l’aspect de ce
manifeste dans sa version minimale avec un seul conteneur
et aucun volume :
Listing 4.2 : oreilly.yaml

apiVersion: v1
kind: Pod
metadata:
name: oreilly
spec:
containers:
- name: oreilly
image: nginx

Vous pouvez enregistrer ce manifeste dans un fichier que


vous appelez par exemple oreilly.yaml puis vous utilisez
kubectl pour créer le pod :

$ kubectl create -f oreilly.yaml

Discussion
La spécification API d’un pod peut être bien plus riche que
ce que nous avons montré comme solution. Un pod peut par
exemple contenir plusieurs conteneurs, comme ceci :

apiVersion: v1
kind: Pod
metadata:
name: oreilly
spec:
containers:
- name: oreilly
image: nginx
- name: safari
image: redis

La définition du code peut indiquer les définitions des


volumes pour charger les données dans les conteneurs (voir
Chapitre 8), ainsi que des sondes pour surveiller l’état de
l’application dans le conteneur (voir Chapitre 11).

Vous trouverez dans la documentation une description


complète des champs de la spécification ainsi qu’un lien
vers la spécification complète de l’objet API concerné.

N’utilisez cette méthode que dans des cas très particuliers.


Autrement dit, ne créez jamais un pod isolément. Utilisez un objet
de type Deployment (voir la prochaine section) pour superviser vos
pods par le biais d’un autre objet qui est un jeu de réplicas
ReplicaSet.

Voir aussi
• La documentation de référence des pods de Kubernetes
(https://kubernetes.io/docs/api-
reference/v1.7/#pod-v1-core)

• La documentation de ReplicaSet
(https://kubernetes.io/docs/concepts/workloads/
controllers/replicaset/)
Lancer un déploiement via un
manifeste

Problème
Vous voulez jouir d’un contrôle complet de la façon dont une
application à longue durée de vie est lancée puis supervisée.

Solution
Rédigez un manifeste stipulant un objet Deployment.
Revenez à la précédente section pour les grands principes de
ce genre de fichier.

Partons d’un fichier manifeste portant le nom fancyapp.yaml


et contenant ceci :

Listing 4.3 : fancyapp.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: fancyapp
spec:
replicas: 5
template:
metadata:
labels:
app: fancy
env: development
spec:
containers:
- name: sise
image: mhausenblas/simpleservice:0.5.0
ports:
- containerPort: 9876
env:
- name: SIMPLE_SERVICE_VERSION
value: "0.9"

Vous constatez que plusieurs choses peuvent être décidées


de façon explicite pour contrôler le lancement de
l’application :

• choix du nombre de pods (réplicas), c’est-à-dire de


copies identiques qu’il faut lancer puis superviser ;

• attribution d’un label, par exemple avec


env=development (voir aussi le Chapitre 6) ;

• configuration de variables d’environnement, comme


SIMPLE_SERVICE_VERSION.

Voyons maintenant à quoi ressemble un déploiement basé


sur ce genre de fichier :
$ kubectl create -f fancyapp.yaml
deployment "fancyapp" created

$ kubectl get deploy


NAME DESIRED CURRENT UP-TO-DATE AVAILABLE
AGE
fancyapp 5 5 5 0
8s

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
fancyapp-1223770997 5 5 0 13s

$ kubectl get po
NAME READY STATUS
RESTARTS AGE
fancyapp-1223770997-18msl 0/1
ContainerCreating 0 15s
fancyapp-1223770997-1zdg4 0/1
ContainerCreating 0 15s
fancyapp-1223770997-6rqn2 0/1
ContainerCreating 0 15s
fancyapp-1223770997-7bnbh 0/1
ContainerCreating 0 15s
fancyapp-1223770997-qxg4v 0/1
ContainerCreating 0 15s

Si nous répétons la dernière commande au bout de quelques


secondes :
$ kubectl get po
NAME READY STATUS
RESTARTS AGE
fancyapp-1223770997-18msl 1/1 Running 0
1m
fancyapp-1223770997-1zdg4 1/1 Running 0
1m
fancyapp-1223770997-6rqn2 1/1 Running 0
1m
fancyapp-1223770997-7bnbh 1/1 Running 0
1m
fancyapp-1223770997-qxg4v 1/1 Running 0
1m

Lorsque vous aurez besoin de supprimer un déploiement, et donc


également ses jeux de réplicas et ses pods, il suffit d’exécuter la
commande kubectl delete deploy/ fancyapp. N’essayez jamais
de supprimer individuellement les pods car ils seront
automatiquement recréés par le déploiement. Les débutants font
souvent ce genre d’erreur.

Un déploiement permet d’adapter une application à un


changement d’échelle et de charge (voir Chapitre 9) et aussi
de publier une nouvelle version ou de revenir à une version
antérieure. Les déploiements sont donc bien adaptés aux
applications sans conservation d’état stateless qui
demandent des pods ayant tous les mêmes caractéristiques.

Discussion
Le déploiement tient le rôle de superviseur pour les pods et
les jeux de réplicas (RS). Vous contrôlez précisément quand
et comment chaque nouvelle version de pod est publiée ou
rétrogradée à une version antérieure. Les jeux RS et les pods
qui sont ainsi supervisés n’ont en général aucun intérêt pour
vous, sauf si vous avez besoin de déboguer un pod (voir
Chapitre 12). La Figure 4.1 montre comment avancer et
reculer parmi les différentes révisions d’un déploiement.

Figure 4.1 : Plusieurs révisions d’un déploiement.

Notez que les RS (les jeux de réplicas, ReplicaSet) tentent


de progressivement remplacer le contrôleur de réplication
(RC) des débuts. Il est donc conseillé de commencer à
réfléchir en termes de RS plutôt qu’en termes de RC. Pour
l’instant, la seule différence est que les RS supportent les
labels et les requêtes basées sur des jeux. Attendez-vous à
voir d’autres fonctions ajoutées à RS, jusqu’au moment où le
contrôleur RC sera déclaré déprécié.

Pour générer votre manifeste, vous utilisez la commande


kubectl create avec l’option --dry-run. Vous pouvez
faire générer le manifeste dans un des formats YAML ou
JSON et l’enregistrer pour vous en servir plus tard. Voici par
exemple comment créer un manifeste pour le déploiement
nommé fancyapp avec l’image Docker nginx :

$ kubectl create deployment fancyapp --image


nginx -o json --dry-run
{
"kind": "Deployment",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": "fancy-app",
"creationTimestamp": null,
"labels": {
"app": "fancy-app"
}
},
...

Voir aussi
• Documentation des déploiements de Kubernetes
(https://kubernetes.io/docs/concepts/workloads/
controllers/deployment/)
Actualiser un déploiement

Problème
Vous utilisez un déploiement et désirez déployer une
nouvelle version de l’application.

Solution
Mettez à jour le déploiement, puis laissez la stratégie de
mise à jour par défaut nommée RollingUpdate réaliser
automatiquement le rollout.

N. d. T. : L’anglais a manqué de précision en décidant de nommer


déploiement une entité alors que c’est déjà le nom d’une action
(« déployer une application ») consistant à rendre accessible,
opérationnelle, la définition de cette entité. L’action correspond
donc à rollout, que nous traduirons par publication.

Supposons que vous ayez créé une nouvelle image de


conteneur et désiriez actualiser le déploiement basé sur cette
image :

$ kubectl run sise --


image=mhausenblas/simpleservice:0.4.0
deployment "sise" created

$ kubectl set image deployment sise


mhausenblas/simpleservice:0.5.0
deployment "sise" image updated

$ kubectl rollout status deployment sise


deployment "sise" successfully rolled out

$ kubectl rollout history deployment sise


deployments "sise"
REVISION CHANGE-CAUSE
1 <none>
2 <none>

Après cette commande, vous avez réussi à publier une


nouvelle révision du déploiement, seule l’image de
conteneur ayant changé. Toutes les autres propriétés du
déploiement, par exemple, le nombre de réplicas, n’ont pas
changé. Comment faire pour actualiser ces autres aspects du
déploiement, par exemple une variable d’environnement ?
Plusieurs commandes de kubectl sont disponibles à cet effet.
Par exemple, pour ajouter une définition de port au
déploiement d’exemple, vous utilisez la commande kubectl
edit :

$ kubectl edit deploy sise

La commande charge le code du déploiement actuel dans


l’éditeur par défaut. Si vous avez défini puis exporté la
variable d’environnement KUBE_EDITOR, c’est cet éditeur
qui sera utilisé.

Supposons que nous désirions ajouter la définition de port


suivante :

...
ports:
- containerPort: 9876
...

La Figure 4.2 montre la session d’édition. Ici,


KUBE_EDITOR avait été rendue égale à vi.

Vous enregistrez vos retouches puis sortez de l’éditeur.


Kubernetes déclenche un nouveau déploiement avec le
nouveau port défini. Vérifions cela :

$ kubectl rollout history deployment sise


deployments "sise"
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>

Nous constatons que la révision 3 a été mise en ligne avec


les modifications introduites lors de l’édition. La colonne
CHANGE-CAUSE reste vide parce que vous n’avez pas utilisé
l’option --record avec kubectl create. Si vous vous
servez de cette option, vous pourrez voir ce qui a déclenché
chaque révision.

Nous avions dit qu’il y avait d’autres commandes kubectl


pour actualiser un déploiement :

kubectl apply permet de mettre à jour un déploiement ou


de le créer s’il n’existe pas encore, en partant d’un fichier
manifeste, par exemple comme ceci :

kubectl apply -f simpleservice.yaml

kubectl replace remplace un déploiement en utilisant un


fichier manifeste, par exemple kubectl replace -f
simple_service.yaml. Notez qu’à la différence de apply, le
déploiement doit exister.

kubectl patch permet d’actualiser une clé en particulier,


par exemple ainsi :

kubectl patch deployment sise -p '{"spec":


{"template":
{"spec": {"containers":
[{"name": "sise", "image":
"mhausenblas/simpleservice:0.5.0"}]}}}}'

Que faire si vous constatez que vous avez fait une erreur ou
détecté des problèmes avec la nouvelle version publiée ?
Kubernetes permet facilement de régresser pour retrouver
un état stable et connu au moyen de la commande kubectl
rollout undo. Supposons que la dernière retouche était une
erreur et que vous vouliez revenir en révision 2 :

$ kubectl rollout undo deployment sise --to-


revision=2
Figure 4.2 : L’édition d’un déploiement.

Vous pourrez tout de suite vérifier que la définition de port a


été supprimée avec la commande suivante :

$ kubectl get deploy/sise -o yaml


La publication (rollout) d’un déploiement n’est déclenchée que
lorsque certaines parties du modèle du code ont été modifiées, c’est-
à-dire toutes les clés à partir de .spec.template. Cela concerne les
variables d’environnement, les ports ou l’image de conteneur. Les
modifications concernant les aspects du déploiement, par exemple,
le nombre de réplicas, ne provoquent pas le déclenchement d’une
nouvelle mise en ligne.
CHAPITRE 5
Utiliser les services

Ce chapitre montre comment les pods communiquent avec


le cluster, comment les applications se détectent les unes les
autres et comment exposer des pods de sorte qu’ils
deviennent accessibles de l’extérieur de leur cluster.

Nous allons utiliser pour ce faire une primitive qui porte le


nom de service Kubernetes (Figure 5.1).
Figure 5.1 : Le concept de service Kubernetes.

Un service fournit une adresse IP virtuelle (VIP) stable pour


un ensemble de pods. Cette adresse virtuelle permet aux
clients de toujours réussir à détecter et à se connecter aux
conteneurs exécutés dans les pods, même si ces pods
apparaissent et disparaissent. La nature virtuelle d’une
adresse VIP signifie qu’il ne s’agit pas d’une adresse IP
réelle connectée à une interface réseau ; son seul rôle est de
retransmettre le trafic vers un ou plusieurs pods. Les
correspondances entre l’adresse VIP et les pods prêts à
servir sont de la responsabilité du processus nommé kube-
proxy qui tourne sur chaque nœud du cluster. Ce processus
interroge le serveur API pour savoir s’il y a de nouveaux
services dans le cluster et se charge de mettre à jour la table
des règles (iptables) du nœud afin de fournir les
informations de routage appropriées.
Créer un service pour y exposer
votre application

Problème
Vous avez besoin de mettre en place un mécanisme stable et
fiable permettant de détecter et d’accéder à votre application
dans le cluster.

Solution
Créez un service Kubernetes pour les pods qui vont
supporter l’application.

Supposons que vous ayez écrit un déploiement nommé


nginx avec la commande suivante :

kubectl run nginx --image nginx

Pour créer automatiquement un objet de type Service avec


la commande expose de kubectl, vous utilisez cette
commande ainsi :

$ kubectl expose deploy/nginx --port 80


service "nginx" exposed

$ kubectl describe svc/nginx


Name: nginx
Namespace: default
Labels: run=nginx
Annotations: <none>
Selector: run=nginx
Type: ClusterIP
IP: 10.0.0.143
Port: <unset> 80/TCP
Endpoints: 172.17.0.5:80,172.17.0.7:80
Session Affinity: None
Events: <none>

Vous verrez alors l’objet apparaître dans la liste de tous les


services :

$ kubectl get svc | grep nginx


NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
nginx ClusterIP 10.109.24.56 <none>
80/TCP 2s

Discussion
Pour accéder à ce service depuis votre navigateur, vous
démarrez un proxy dans une autre session de terminal,
comme ceci :

$ kubectl proxy
Starting to serve on 127.0.0.1:8001
Vous pouvez ensuite ouvrir votre navigateur sur l’adresse
suivante :

$ open
http://localhost:8001/api/v1/proxy/namespaces/de
fault/services/nginx/

Il est également possible de rédiger le code de définition


d’un objet Service en produisant un fichier au format
YAML. Voici un exemple pour le même déploiement nginx :

apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
run: nginx
ports:
- port: 80

Le paramètre essentiel dans ce fichier YAML est selector,


qui permet de sélectionner tous les pods qui doivent
constituer l’abstraction de ce microservice. Kubernetes
utilise l’objet de type Service pour configurer de façon
dynamique la table iptables pour tous les nœuds qui doivent
pouvoir envoyer du trafic réseau vers les conteneurs qui
constituent le microservice. La sélection passe par une
requête de label (voir Chapitre 6). Le résultat est une liste de
points d’extrémité (endpoints).

Si vous avez l’impression que le service ne fonctionne pas bien,


vérifiez les labels utilisés dans la rubrique du sélecteur et qu’un
ensemble de points d’extrémité a bien été peuplé au moyen de
kubectl get endpoints. Si ce n’est pas le cas, la raison en est
généralement que le sélecteur ne parvient pas à trouver de pod
satisfaisant.

Les superviseurs de pods (déploiements ou contrôleurs de


réplication) n’ont pas la même approche que les services.
Superviseurs et services utilisent des labels pour identifier les pods
qu’ils cherchent, mais leur travail n’est pas le même : les
superviseurs surveillent la bonne santé des pods et les font
redémarrer alors que les services sont chargés de les rendre
accessibles de façon fiable.

Voir aussi
• Documentation des services de Kubernetes
(https://kubernetes.io/docs/concepts/services-
networking/service)

• Tutoriel Kubernetes sur l’utilisation d’un service pour


exposer une application
(https://kubernetes.io/docs/tutorials/kubernete
s-basics/expose-intro/)
Vérifier l’entrée DNS d’un service

Problème
Vous avez créé (voir la section précédente) un service et vous
désirez maintenant vérifier que votre inscription DNS est
correcte.

Solution
Par défaut, le type de service utilisé par Kubernetes est
ClusterIP. Cela permet d’exposer le service sur une adresse
IP interne au cluster. Si l’extension de cluster DNS est
disponible et fonctionne correctement, vous pouvez alors
accéder au service au moyen d’un nom de domaine
totalement qualifié FQDN (Fully Qualified Domain Name) en
utilisant la syntaxe suivante :

$SERVICENAME.$NAMESPACE.svc.cluster.local

Pour en vérifier le bon fonctionnement, démarrez une


session d’interpréteur interactif dans un des conteneurs du
cluster, par exemple en utilisant la commande kubectl run
avec l’image busybox, comme ceci :
$ kubectl run busybox --image busybox -it --
/bin/sh
If you don't see a command prompt, try pressing
enter.

/ # nslookup nginx
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-
system.svc.cluster.local

Name: nginx
Address 1: 10.109.24.56
nginx.default.svc.cluster.local

L’adresse IP indiquée pour le service doit correspondre à


l’adresse IP de son cluster.
Changer le type d’un service

Problème
Vous avez défini un service, par exemple de type ClusterIP
comme vu dans la précédente section. Vous voulez changer
son type, afin de pouvoir exposer votre application en tant
que NodePort ou en utilisant un équilibreur de charge d’un
fournisseur de cloud, donc avec le type de service
LoadBalancer.

Solution
Pour changer le type de service, vous vous servez de votre
éditeur préféré avec la commande kubectl edit. Supposons
que vous disposiez d’un fichier manifeste portant le nom
simple-nginx-svc.yaml avec le contenu suivant :

Listing 5.1 : simple-nginx-svc.yaml

kind: Service
apiVersion: v1
metadata:
name: webserver
spec:
ports:
- port: 80
selector:
app: nginx

Créez votre service webserver, puis émettez une requête :

$ kubectl create -f simple-nginx-svc.yaml

$ kubectl get svc/webserver


NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webserver 10.0.0.39 <none> 80/TCP 56s

Vous pouvez maintenant changer le type de service, pour


choisir, par exemple, NodePort :

$ kubectl edit svc/webserver

La commande va récupérer la spécification actuelle du


serveur API pour ce service, puis charger le texte dans
l’éditeur par défaut. Vous obtenez quelque chose dans le
style de la Figure 5.2. Vous remarquez que nous utilisons ici
l’éditeur vi (EDITOR=vi).
Figure 5.2 : Capture de l’édition d’un service avec kubectl edit.

Enregistrez vos modifications (changer le type en NodePort


et containerPort en Port) et vérifiez que le service a bien
été mis à jour :

$ kubectl get svc/webserver


NAME CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
webserver 10.0.0.39 <nodes> 80:30080/TCP
7m

$ kubectl get svc/webserver -o yaml


apiVersion: v1
kind: Service
metadata:
creationTimestamp: 2017-08-31T05:08:12Z
name: webserver
namespace: default
resourceVersion: "689727"
selfLink:
/api/v1/namespaces/default/services/webserver
uid: 63956053-8e0a-11e7-8c3b-080027390640
spec:
clusterIP: 10.0.0.39
externalTrafficPolicy: Cluster
ports:
- nodePort: 30080
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}

En théorie, vous pouvez changer le type de service selon vos


besoins exacts, mais tenez compte des implications de
certains types. Par exemple, LoadBalancer peut déclencher
l’approvisionnement de composants d’infrastructure d’un
cloud public, ce qui peut entraîner une facturation
supplémentaire si vous n’y prenez garde et ne surveillez pas
l’utilisation.
Déployer un contrôleur Ingress
sur Minikube

Problème
Vous voulez déployer un contrôleur d’entrée réseau Ingress
sur Minikube pour gérer des objets Ingress. Ce genre
d’objet vous intéressera dès que vous aurez besoin de
fournir un accès à vos applications s’exécutant dans
Kubernetes depuis l’extérieur du cluster Kubernetes. Pour
autant, vous ne voulez pas créer un service de type NodePort
ou LoadBalancer.

Solution
Pour qu’un objet Ingress (décrit dans la section suivante)
fasse effet en fournissant une route depuis l’extérieur d’un
cluster vers vos pods, il faut déployer un contrôleur Ingress.

Dans Minikube, vous devez activer l’extension ingress,


comme ceci :

$ minikube addons enable ingress

Vous devez ensuite voir apparaître l’extension ingress


activée dans la liste des extensions Minikube, ce que vous
vérifiez ainsi :

$ minikube addons list | grep ingress


- ingress: enabled

Patientez environ une minute et vous verrez apparaître de


nouveaux pods dans l’espace de noms

kube-system :

$ kubectl get pods -n kube-system


NAME READY STATUS
RESTARTS AGE
default-http-backend-6tv69 1/1 Running 1
1d
...
nginx-ingress-controller-95dqr 1/1 Running
1 1d
...

À partir de ce moment, vous êtes prêt à créer des objets


Ingress.

Voir aussi
• Documentation concernant les objets Ingress
(https://kubernetes.io/docs/concepts/services-
networking/ingress/)
• Contrôleur Ingress basé nginx
(https://github.com/kubernetes/ingress-
nginx/blob/master/README.md)
Rendre des services accessibles
de l’extérieur du cluster

Problème
Vous avez besoin d’accéder à un service Kubernetes de
l’extérieur du cluster.

Solution
Utilisez un contrôleur Ingress (voir précédente section).
Vous le configurez en créant des objets Ingress. L’exemple
qui suit montre le manifeste d’une règle Ingress qui permet
de configurer un chemin d’accès à un service nginx :

$ cat nginx-ingress.yaml

Listing 5.2 : nginx-ingress.yaml

kind: Ingress
apiVersion:
extensions/v1beta1
metadata:
name: nginx-
public
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host:
http:
paths:
- path: /web
backend:
serviceName: nginx
servicePort: 80

$ kubectl create -f nginx-ingress.yaml

Vous pouvez voir l’objet Ingress que vous avez créé pour
nginx dans le tableau de bord Kubernetes (Figure 5.3).

Figure 5.3 : Un objet Ingress pour nginx.


Le tableau de bord Kubernetes ci-dessus permet de voir que
nginx devient disponible via l’adresse IP 192.168.99.100. Le
fichier manifeste stipule que le service doit être exposé par
le chemin /web. Ces informations vous permettent d’accéder
à nginx de l’extérieur du cluster, comme ceci :

$ curl -k https://192.168.99.100/web
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial,
sans-serif;
}
</style>
</head>

<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is
successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please


refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Discussion
Le fonctionnement général d’un service Ingress correspond
au diagramme de la Figure 5.4 : le contrôleur Ingress écoute
le point d’extrémité /ingresses du serveur API pour
découvrir les nouvelles règles. Il configure ensuite les routes
pour que le trafic externe arrive sur un service spécifique
interne au cluster, dans l’exemple, il s’agit de service1 sur
le port 9876.
Figure 5.4 : Concept général des entrées réseau Ingress.

Dans cette section, nous avons utilisé Minishift pour lequel


l’extension de contrôleur Ingress est déjà disponible. En général, il
vous faudra mettre en place vous-même un contrôleur Ingress.
Servez-vous des explications disponibles sur GitHub.

Voir aussi
• Le référentiel GitHub pour Kubernetes/ingress-nginx
(https://github.com/kubernetes/ingress)

• La page de blog de Milos Gajdo à propos des services


Kubernetes et d’Ingress
(http://containerops.org/2017/01/30/kubernetes-
services-and-ingress-under-x-ray/)
• La page de blog de Daemonza sur Kubernetes et nginx-
ingress-controller
(https://daemonza.github.io/2017/02/13/kubernet
es-nginx-ingress-controller/)
CHAPITRE 6
Explorer l’APIKubernetes et les
métadonnées clés

Les sections de ce chapitre s’intéressent aux interactions


fondamentales avec les objets Kubernetes ainsi qu’avec son
API. Tous les objets dans Kubernetes, qu’ils soient situés
dans un espace de noms comme un déploiement, ou qu’ils
soient globaux à tout un cluster comme dans le cas d’un
nœud, sont dotés de certains champs élémentaires, par
exemple metadata, spec et status. Le champ spec spécifie
l’état que vous désirez pour un objet alors que le champ
status contient l’état actuel de cet objet, tel qu’il est géré
par le serveur API de Kubernetes.
Découvrir les points d’extrémité
API d’un serveur API

Problème
Vous voulez connaître les différents points d’extrémité
(endpoints) API disponibles sur un serveur API Kubernetes.

Solution
Si vous avez accès à ce serveur API par un port privé sans
authentification, vous pouvez directement émettre une
requête HTTP vers le serveur API pour découvrir les points
d’extrémité. Vous pouvez par exemple, avec Minikube,
accéder via SSH à la machine virtuelle (minikube ssh) pour
vous connecter au serveur API sur le port 8080, comme
ceci :

$ curl localhost:8080/api/v1
...
{
"name": "pods",
"namespaced": true,
"kind": "Pod",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"proxy",
"update",
"watch"
],
"shortNames": [
"po"
]
},
...

L’affichage précédent concerne un objet de l’espèce Pod et


rappelle les opérations possibles (les verbes) et notamment
get et delete.

Si vous n’avez pas d’accès direct à la machine qui héberge le serveur


API Kubernetes, vous pouvez accéder localement à l’API via proxy au
moyen de kubectl. Cela revient à ouvrir une session avec
authentification locale :

$ kubectl proxy --port=8001 --api-prefix=/

Vous ouvrez ensuite une session dans une autre fenêtre :

$ curl localhost:8001/foobar
Le chemin API /foobar permet donc de dresser la liste de tous
les points d’extrémité API. Précisons que les deux options --
port et --api-prefix sont facultatives.

Discussion
Dans votre découverte des points d’extrémité API, vous allez
par exemple voir apparaître ceux-ci :

• /api/v1

• /apis/apps

• /apis/authentication.k8s.io

• /apis/authorization.k8s.io

• /apis/autoscaling

• /apis/batch

À chacun de ces points d’extrémité correspond un groupe


API. Les objets API d’un groupe sont versionnés pour
indiquer leur maturité (par exemple v1beta, v1beta2). Les
pods, les services, les mappes de configuration et les secrets
font par exemple tous partie du groupe API /api/v1. Les
déploiements font partie du groupe API
/apis/extensions/v1beta1.

Le groupe auquel appartient un objet correspond à ce que


l’on appelle le paramètre apiVersion dans la spécification
de cet objet, disponible par la référence API.

Voir aussi
• Principes des objets Kubernetes
(https://kubernetes.io/docs/concepts/overview/w
or-king-with-objects/kubernetes-objects/)

• Présentation de l’API Kubernetes


(https://kubernetes.io/docs/concepts/overview/k
ubernetes-api/)

• Conventions de l’API Kubernetes


(https://github.com/kubernetes/community/blob/m
aster/contributors/devel/sig-architecture/api-
conventions.md)
Structure d’un manifeste
Kubernetes

Problème
Kubernetes propose plusieurs générateurs de manifeste
(kubectl run, kubectl create), mais il n’est pas inutile
d’apprendre à rédiger un manifeste Kubernetes pour
exprimer les spécifications des objets. Pour y parvenir, il
faut connaître la structure d’un manifeste Kubernetes.

Solution
Nous avons découvert les différents groupes API dans la
section précédente et vu comment savoir à quel groupe
appartient chaque objet.

Toutes les ressources API sont soient des objets, soit des
listes. Chaque ressource est dotée d’un paramètre kind et
d’un paramètre apiVersion. De plus, chaque paramètre
kind doit posséder un bloc metadata qui contient le nom
de l’objet, son espace de noms (voir prochaine section) et
éventuellement quelques labels ou étiquettes (voir section
« Utiliser des labels dans des requêtes »), ainsi que des
annotations (voir section « Ajouter une annotation et une
ressource »).

Par exemple, dans le cas d’un pod, kind contient la valeur


Pod et apiVersion la valeur v1. Voici à quoi ressemble le
fichier manifeste minimal correspondant au format YAML :

apiVersion: v1
kind: Pod
metadata:
name: mypod
...

Un élément que la plupart des objets contiennent dans leur


manifeste est spec. Une fois l’objet créé, il est en mesure de
renvoyer un élément status :

apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
...
status
...

Voir aussi
• Principe des objets Kubernetes
(https://kubernetes.io/docs/concepts/overview/w
or-king-with-objects/kubernetes-objects/)
Créer un espace de noms pour
éviter les collisions

Problème
Vous voulez créer deux objets homonymes, tout en évitant
un conflit de noms.

Solution
Vous créez deux espaces de noms puis placez un objet dans
chaque espace.

Si vous ne précisez rien, les objets sont tous créés dans


l’espace de noms default. Vous pouvez donc créer un autre
espace de noms en l’appelant par exemple my-app. Si vous
demandez la liste des espaces de noms existants, vous
verrez apparaître l’espace default, deux autres espaces de
noms qui sont créés au démarrage (kube-system et kube-
public) ainsi que l’espace de noms spécifique que vous
venez de créer :

$ kubectl create namespace my-app


namespace "my-app" created

$ kubectl get ns
NAME STATUS AGE
default Active 30s
my-app Active 1s
kube-public Active 29s
kube-system Active 30s

Vous pouvez tout à fait créer un espace de noms en rédigeant un


manifeste. Si vous enregistrez le manifeste suivant sous le nom
app.yaml, vous pouvez ensuite demander la création de l’espace de
noms au moyen de la commande kubectl create -f app.yaml :

apiVersion: v1
kind: Namespace
metadata:
name: my-app

Discussion
Dès que vous tentez de créer deux objets homonymes dans le
même espace de noms, par exemple dans default, vous
provoquez une collision, ce qui est sanctionné par une erreur
émise par le serveur API de Kubernetes. Si vous démarrez le
second objet dans un autre espace de noms, le serveur API
accepte de le créer :

$ kubectl run foobar --image=ghost:0.9


deployment "foobar" created

$ kubectl run foobar --image=nginx:1.13


Error from server (AlreadyExists):
deployments.extensions "foobar" already exists

$ kubectl run foobar --image=nginx:1.13 --


namespace foobar
deployment "foobar" created

En effet, la plupart des objets API de Kubernetes sont situés


dans des espaces de noms, l’espace de chacun étant défini
dans les métadonnées de l’objet.

Sachez que l’espace de noms kube-system est réservé pour les


administrateurs et que l’espace kube-public est destiné à recevoir
les objets publics accessibles à tous les utilisateurs du cluster.
Définir des quotas dans un
espace de noms

Problème
Vous désirez poser une limite au volume de ressources
disponible dans le même espace de noms, par exemple pour
restreindre le nombre de pods pouvant être exécutés dans
cet espace.

Solution
Il suffit de définir un objet ResourceQuota pour poser une
limite pour un espace de noms :

$ cat resource-quota-pods.yaml

Listing 6.1 : resource-quota-pods.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
name: podquota
spec:
hard:
pods: "10"
Voici comment exploiter ce fichier :

$ kubectl create namespace my-app

$ kubectl create -f resource-quota-pods.yaml --


namespace=my-app

$ kubectl describe resourcequota podquota --


namespace=my-app
Name: podquota
Namespace: my-app
Resource Used Hard
-------- ---- ----
pods 0 10

Discussion
Vous pouvez définir plusieurs quotas par espace de noms, et
notamment le nombre de pods, de secrets et de mappes de
configuration.

Voir aussi
• Configuration des quotas des objets API
(https://kubernetes.io/docs/tasks/adminis-ter-
cluster/quota-api-object/)
Étiqueter un objet avec un label

Problème
Vous voulez marquer un objet avec un label pour pouvoir le
retrouver facilement. Ce label ou cette étiquette permettra
d’appliquer des requêtes d’utilisateur (voir section « Utiliser
des labels dans des requêtes ») ou de servir dans le contexte
d’une automatisation système.

Solution
Vous vous servez de la commande kubectl label. Voici
comment donner à un pod nommé foobar la paire
clé/valeur tier=frontend :

$ kubectl label pods foobar tier=frontend

Consultez l’aide de la commande par kubectl label --help. Vous


saurez ainsi comment modifier et supprimer les labels et même
comment étiqueter d’un geste toutes les ressources d’un espace de
noms.

Discussion
Les labels ou étiquettes de Kubernetes permettent de gérer
les objets de façon souple et non hiérarchisée. Chaque label
est incarné par une paire clé/valeur qui n’a pas de
signification particulière pour Kubernetes. (Le contenu
d’une telle paire n’est pas interprété par le système.) Les
labels servent à exprimer une appartenance (par exemple,
l’objet X appartient au département ABC), pour décrire un
environnement (par exemple, tel service est utilisé en
production) ou tout autre besoin de description d’objet.
Précisons qu’il y a des limites à la longueur des labels et aux
valeurs autorisées.

Voir aussi
• Labels et sélecteurs
(https://kubernetes.io/docs/concepts/overview/w
orking-with-ob-jects/labels/#syntax-and-
character-set)
Utiliser des labels dans des
requêtes

Problème
Vous avez besoin d’interroger des objets de façon efficace.

Solution
Servez-vous de la commande kubectl get --selector.
Partons de la série de pods suivante :

$ kubectl get pods --show-labels


NAME READY ... LABELS
cockroachdb-0 1/1 ...
app=cockroachdb,
cockroachdb-1 1/1 ...
app=cockroachdb,
cockroachdb-2 1/1 ...
app=cockroachdb,
jump-1247516000-sz87w 1/1 ... pod-
template-hash=1247516000,run=jump
nginx-4217019353-462mb 1/1 ... pod-
template-hash=4217019353,run=nginx
nginx-4217019353-z3g8d 1/1 ... pod-
template-hash=4217019353,run=nginx
prom-2436944326-pr60g 1/1 ...
app=prom,pod-template-hash=2436944326

Voici comment sélectionner uniquement les pods qui font


partie de l’application CockroachDB.

(app=cockroachdb) :

$ kubectl get pods --selector app=cockroachdb


NAME READY STATUS RESTARTS AGE
cockroachdb-0 1/1 Running 0 17h
cockroachdb-1 1/1 Running 0 17h
cockroachdb-2 1/1 Running 0 17h

Discussion
Le label d’un objet fait partie de ses métadonnées et tout
objet Kubernetes peut recevoir un label. Les labels sont
utilisés par le noyau Kubernetes pour sélectionner les pods
et les déploiements (voir Chapitre 4) et les services (voir le
Chapitre 5).

Vous définissez un label soit manuellement avec la


commande kubectl label (voir section précédente), soit
en le demandant par un manifeste d’objet, comme ceci :

apiVersion: v1
kind: Pod
metadata:
name: foobar
labels:
tier: frontend
...

Une fois que les labels sont définis, vous pouvez en obtenir
la liste avec kubectl get. Tenez compte des précisions
suivantes :

• -l est la version abrégée de --selector et permet


d’interroger les objets possédant la paire key=value
spécifiée.

• --show-labels liste tous les labels de chaque objet


renvoyé.

• -L (majuscule) ajoute une colonne à la liste renvoyée


pour la valeur du label demandé.

• La plupart des espèces d’objets permettent d’émettre


des requêtes conditionnelles de type ensemble. Vous
pouvez ainsi stipuler votre requête en demandant par
exemple « doit porter un label X et/ou Y ». La
commande suivante :

kubectl get pods -l 'env in (production,


development)'
dresse la liste des pods qui sont soit en production, soit
en développement.

En supposant deux pods en cours d’exécution, un doté du


label run=barfoo et l’autre du label run=foobar, vous
obtiendrez les résultats suivants :

$ kubectl get pods --show-labels


NAME READY ... LABELS
barfoo-76081199-h3gwx 1/1 ... pod-template-
hash=76081199,run=barfoo
foobar-1123019601-6x9w1 1/1 ... pod-template-
hash=1123019601,run=foobar

$ kubectl get pods -Lrun


NAME READY ... RUN
barfoo-76081199-h3gwx 1/1 ... barfoo
foobar-1123019601-6x9w1 1/1 ... foobar

$ kubectl get pods -l run=foobar


NAME READY ...
foobar-1123019601-6x9w1 1/1 ...

Voir aussi
• Documentation des labels Kubernetes
(https://kubernetes.io/docs/concepts/overview/w
orking-with-objects/labels/)
Ajouter une annotation et une
ressource

Problème
Vous aimeriez annoter une ressource en fournissant une
paire clé/valeur générique, éventuellement avec des données
non visibles par les humains.

Solution
Servez-vous de la commande kubectl annotate :

$ kubectl annotate pods foobar


description='texte d'automatisation'

Discussion
Les annotations permettent d’augmenter les possibilités
d’automatisation dans Kubernetes. Lorsque vous créez un
déploiement avec la commande kubectl run mais oubliez
d’utiliser l’option --record, la colonne CHANGE-CAUSE de
l’historique du déploiement reste vide (voir le Chapitre 4).
Pour garder une trace des commandes qui ont un effet sur le
déploiement, vous pouvez les annoter en injectant la clé
kubernetes.io/change-cause. En supposant un
déploiement nommé foobar, nous pouvons l’annoter ainsi :

$ kubectl annotate deployment foobar \


kubernetes.io/change-cause="Raison de la
nouvelle revision"

Toutes les modifications ultérieures qui seront appliquées au


déploiement seront ainsi enregistrées.
CHAPITRE 7
Gérer des charges de travail
spécialisées

Nous avons vu dans le Chapitre 4 comment démarrer des


applications ayant un régime de fonctionnement quasi
permanent, comme c’est le cas d’un serveur Web ou
d’application. Découvrons maintenant des charges de travail
plus spécialisées, comme le sont les processus de
traitements par lot, l’exécution de pods sur certains nœuds,
ou encore les applications natives à conservation d’état
(stateful) non conçues au départ pour fonctionner dans un
nuage cloud.
Exécuter un job de traitement
par lot

Problème
Vous avez besoin de lancer l’exécution d’un processus à
durée d’exécution limitée, par exemple une conversion en
masse, une opération de sauvegarde ou une mise à jour d’un
schéma de base de données.

Solution
Vous utilisez une ressource Kubernetes de type Job pour
lancer puis superviser le pod qui va réaliser les traitements
du processus par lot.

Il faut d’abord créer le manifeste Kubernetes pour le job


dans un fichier que vous appelez par exemple, counter-
batchjob.yaml :

Listing 7.1 : counter-batchjob.yaml

apiVersion: batch/v1
kind: Job
metadata:
name: counter
spec:
template:
metadata:
name: counter
spec:
containers:
- name: counter
image: busybox
command:
- "sh"
- "-c"
- "for i in 1 2 3 ; do echo $i ; done"
restartPolicy: Never

Il ne reste plus ensuite qu’à lancer le traitement et surveiller


son statut :

$ kubectl create -f counter-batch-job.yaml


job "counter" created

$ kubectl get jobs


NAME DESIRED SUCCESSFUL AGE
counter 1 1 22s

$ kubectl describe jobs/counter


Name: counter
Namespace: default
Selector: controller-uid=634b9015-7f58-
11e7-b58a-080027390640
Labels: controller-uid=634b9015-7f58-
11e7-b58a-080027390640
job-name=counter
Annotations: <none>
Parallelism: 1
Completions: 1
Start Time: Sat, 12 Aug 2017 13:18:45 +0100
Pods Statuses: 0 Running / 1 Succeeded / 0
Failed
Pod Template:
Labels: controller-uid=634b9015-7f58-
11e7-b58a-080027390640
job-name=counter
Containers:
counter:
Image: busybox
Port: <none>
Command:
sh
-c
for i in 1 2 3 ; do echo $i ; done
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
FirstSeen ... ... ... Type Reason Message
--------- ... ... ... ------ ------ -------
31s ... ... ... Normal SuccessfulCreate
Created pod: counter-0pt20
Vous vérifiez que le travail a bien été réalisé en voyant
apparaître l’indicateur d’étape de 1 à 3 :

$ kubectl logs jobs/counter


1
2
3

Dans cet exemple, vous constatez que le job nommé counter


a bien déroulé tout son traitement.

Lorsque vous n’avez plus besoin d’un tel job, vous le


supprimez au moyen de la commande suivante :

$ kubectl delete jobs/counter

Voir aussi
• Achèvement d’exécution des jobs
(https://kubernetes.io/docs/concepts/workloads/
controllers/jobs-run-to-completion/)
Exécuter une tâche planifiée
dans un pod

Problème
Vous avez besoin de faire exécuter une tâche à un moment
précis au sein d’un pod géré par Kubernetes.

Solution
Servez-vous d’un objet Kubernetes de type CronJob. Il s’agit
d’une variante spécialisée de l’objet générique Job (voir
section précédente).

Pour planifier l’exécution d’un traitement, il suffit de


rédiger un fichier manifeste comme celui présenté ci-
dessous. La section spec comporte une sous-section
schedule qui obéit au format standard crontab. La section
template sert à décrire le pod qui doit exécuter le
traitement ainsi que la commande à exécuter (ici, nous
affichons sur stdout la date et l’heure courante une fois par
heure) :

Listing 7.2 : cron-job.yaml


apiVersion: batch/v2alpha1
kind: CronJob
metadata:
name: hourly-date
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: date
image: busybox
command:
- "sh"
- "-c"
- "date"
restartPolicy: OnFailure

Voir aussi
• Documentation de CronJob
(https://kubernetes.io/docs/concepts/workloads/
control-lers/cron-jobs/)
Démarrer un démon
d’infrastructure par nœud

Problème
Vous voulez démarrer un démon d’infrastructure, tel qu’un
collecteur de logs ou un agent de supervision, en
garantissant qu’un et un seul code s’exécute par nœud.

Solution
Pour démarrer et superviser le processus démon, vous
utilisez un composant DaemonSet. Voici par exemple
comment démarrer un agent FluentD sur chaque nœud du
cluster. Vous créez un fichier avec le contenu suivant et
portant le nom fluentd-daemonset.yaml :

Listing 7.3 : fluentd-daemonset.yaml

kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
name: fluentd
spec:
template:
metadata:
labels:
app: fluentd
name: fluentd
spec:
containers:
- name: fluentd
image: gcr.io/google_containers/fluentd-
elasticsearch:1.3
env:
- name: FLUENTD_ARGS
value: -qq
volumeMounts:
- name: varlog
mountPath: /varlog
- name: containers
mountPath: /var/lib/docker/containers
volumes:
- hostPath:
path: /var/log
name: varlog
- hostPath:
path: /var/lib/docker/containers
name: containers

Vous pouvez ensuite lancer le composant DaemonSet ainsi :

$ kubectl create -f fluentd-daemonset.yaml


daemonset "fluentd" created

$ kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE
AVAILABLE NODE-SELECTOR AGE
fluentd 1 1 1 1 1
<none> 17s

$ kubectl describe ds/fluentd


Name: fluentd
Selector: app=fluentd
Node-Selector: <none>
Labels: app=fluentd
Annotations: <none>
Desired Number of Nodes Scheduled: 1
Current Number of Nodes Scheduled: 1
Number of Nodes Scheduled with Up-to-date Pods:
1
Number of Nodes Scheduled with Available Pods: 1
Number of Nodes Misscheduled: 0
Pods Status: 1 Running / 0 Waiting / 0 Succeeded
/ 0 Failed
...

Discussion
L’affichage précédent est le résultat du lancement de la
commande dans Minikube. C’est la raison pour laquelle vous
ne voyez qu’un pod dans cette configuration. Si vous
aviez 15 nœuds dans le cluster, vous verriez 15 pods, à raison
d’un pod par nœud. Vous pouvez limiter le démon à certains
nœuds au moyen d’une section nodeSelector dans la partie
spec du manifeste de DaemonSet.
Gérer des applications Stateful
et leader/suiveur

Problème
Votre application doit pouvoir s’appuyer sur des pods ayant
des caractéristiques différentes ; par exemple, une base de
données pourra être exploitée par un meneur (leader) qui
permet les lectures et les écritures, accompagné de plusieurs
suiveurs (followers), qui n’assurent que les lectures. Vous ne
pouvez pas utiliser de déploiement dans cette situation
parce qu’un déploiement ne permet de superviser que des
pods identiques. Il vous faut néanmoins un superviseur
capable de gérer un ensemble de pods, un peu comme
plusieurs espèces d’animaux domestiques en cohabitation et
non comme un troupeau.

Solution
Servez-vous d’un composant StatefulSet, qui permet de
définir des charges de travail avec des noms réseau
différents, un déploiement, une mise à l’échelle et une fin
policées et/ou un stockage persistant. Par exemple, pour
exploiter un entrepôt de données redimensionnable très
populaire CockroachDB, vous pouvez vous inspirer de
l’exemple qui suit. Cet exemple définit dans son noyau le
composant StatefulSet suivant :

Listing 7.4 : cockroachdb-statefulset.yaml

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: cockroachdb
spec:
serviceName: "cockroachdb"
replicas: 3
template:
metadata:
labels:
app: cockroachdb
spec:
initContainers:
- name: bootstrap
image: cockroachdb/cockroach-k8s-init:0.2
imagePullPolicy: IfNotPresent
args:
- "-on-start=/on-start.sh"
- "-service=cockroachdb"
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: datadir
mountPath: "/cockroach/cockroach-data"
affinity:
podAntiAffinity:

preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cockroachdb
topologyKey: kubernetes.io/hostname
containers:
- name: cockroachdb
image: cockroachdb/cockroach:v1.0.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 26257
name: grpc
- containerPort: 8080
name: http
volumeMounts:
- name: datadir
mountPath: /cockroach/cockroach-data
command:
- "/bin/bash"
- "-ecx"
- |
if [ ! "$(hostname)" == "cockroachdb-0" ]
|| \
[ -e "/cockroach/cockroach-
data/cluster_exists_marker" ]
then
CRARGS+=("--join" "cockroachdb-public")
fi
exec /cockroach/cockroach ${CRARGS[*]}
terminationGracePeriodSeconds: 60
volumes:
- name: datadir
persistentVolumeClaim:
claimName: datadir

volumeClaimTemplates:
- metadata:
name: datadir
annotations:
volume.alpha.kubernetes.io/storage-class:
anything
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 1Gi

Voici comment lancer l’exécution de ce composant :


$ curl -s -o cockroachdb-statefulset.yaml \

https://raw.githubusercontent.com/kubernetes/kub
ernetes/master/ \
examples/cockroachdb/cockroachdb-
statefulset.yaml

$ curl -s -o crex.sh \

https://raw.githubusercontent.com/kubernetes/kub
ernetes/master/ \
examples/cockroachdb/minikube.sh

$ ./crex.sh
+ kubectl delete
statefulsets,persistentvolumes,persistentvolumec
laims,services...
...
+ kubectl create -f -
persistentvolumeclaim "datadir-cockroachdb-3"
created
+ kubectl create -f cockroachdb-statefulset.yaml
service "cockroachdb-public" created
service "cockroachdb" created
poddisruptionbudget "cockroachdb-budget" created
statefulset "cockroachdb" created

N. d. T. : Le fichier de script crex.sh est fourni avec les exemples et le


fichier .yaml précédent fourni dans l’archive est beaucoup plus long
que l’extrait montré ci-dessus.
Pour vérifier que l’objet StatefulSet a été créé avec les
pods, accédez au tableau de bord de Kubernetes (Figure 7.1).

Figure 7 1 : Aperçu d’un objet StatefulSet dans le tableau de bord.

Discussion
L’ancien nom dans Kubernetes pour les objets StatefulSet
était PetSet (ensemble d’animaux domestiques), ce qui
vous donne une idée de la motivation initiale. À partir de
Kubernetes 1.7, StatefulSet est devenu une caractéristique
bêta, ce qui signifie que l’interface API n’allait plus changer
à ce niveau (seules des retouches de l’interface utilisateur
pourraient ensuite apparaître.) Un objet StatefulSet
incarne un contrôleur qui distribue des identités uniques aux
pods qu’il supervise. Précisons que par sécurité la
suppression d’un objet StatefulSet n’entraîne pas
automatiquement la suppression des volumes qui lui sont
associés.

Un autre domaine d’utilisation de StatefulSet, assez


fréquent sur le terrain, consiste à lancer l’exécution d’une
application qui n’a pas été conçue en tenant compte de
Kubernetes. On parle en général d’application héritée,
patrimoniale ou legacy dans le monde Kubernetes. Nous
parlerons aussi d’applications non natives dans le cloud en
cours de basculement. L’objet StatefulSet constitue une
bonne solution pour superviser de telles applications.

Voir aussi
• Principes de StatefulSet
(https://kubernetes.io/docs/tutorials/stateful-
applica-tion/basic-stateful-set/)

• Exécuter une application répliquée et StatefulSet


(https://kubernetes.io/docs/tasks/run-
application/run-replicated-stateful-
application/)

• Exemple de déploiement Cassandra avec les ensembles


StatefulSet
(https://kubernetes.io/docs/tutorials/stateful-
application/cassandra/)
• Kubernetes Redis en tant qu’ensemble StatefulSet
avec Sentinel
(https://github.com/corybuecker/redis-stateful-
set)

• Article d’Oleg Chunikhin concernant MongoDb et


StatefulSet
(https://www.linkedin.com/pulse/how-run-
mongodb-replica-set-kubernetes-petset-oleg-
chunikhin)

• Discussion du forum Hacker News concernant


StatefulSet (https://news.ycombinator.com/item
? id=13225183)
Influencer le comportement au
démarrage des pods

Problème
Un de vos pods a besoin de la présence d’un autre service
pour fonctionner correctement.

Solution
Vous influencez le comportement au démarrage d’un pod au
moyen d’un conteneur init.

Supposons que nous ayons besoin de démarrer un serveur


Web nginx qui a besoin d’un service de fond pour servir du
contenu. Vous devez garantir que le code nginx ne démarre
qu’une fois le service de fond en cours d’exécution.

Vous commencez par créer le service de fond dont dépend le


serveur Web :

$ kubectl run backend --


image=mhausenblas/simpleservice:0.5.0
deployment "backend" created

$ kubectl expose deployment backend --port=80 --


target-port=9876
Vous créez ensuite un manifeste nginx-init-container.yaml
pour démarrer l’instance nginx en demandant d’attendre
que le déploiement nommé backend soit prêt à servir des
données :

Listing 7.5 : nginx-init-container.yaml

kind: Deployment
apiVersion: apps/v1beta1
metadata:
name: nginx
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: webserver
image: nginx
ports:
- containerPort: 80
initContainers:
- name: checkbackend
image: busybox
command: ['sh', '-c', 'until
nslookup backend.default.svc; do echo "Waiting
for
backend to come up"; sleep 3; done; echo
"Backend is up, ready to launch web server"']

Vous pouvez ensuite lancer le déploiement nginx et


confirmer que le conteneur init a terminé son travail en
consultant les journaux (logs) du pod qui est supervisé :

$ kubectl create -f nginx-init-container.yaml


deployment "nginx" created

$ kubectl get po
NAME READY STATUS RESTARTS AGE
backend-853383893-2g0gs 1/1 Running 0 43m
nginx-2101406530-jwghn 1/1 Running 0 10m
$ kubectl logs nginx-2101406530-jwghn -c
checkbackend
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-
system.svc.cluster.local

Name: backend.default.svc
Address 1: 10.0.0.46
backend.default.svc.cluster.local
Backend is up, ready to launch web server

Vous constatez que la commande dans le conteneur init s’est


exécutée comme prévu.
CHAPITRE 8
Volumes et données de
configuration

Dans Kubernetes, un volume est un répertoire de stockage


qui est accessible à tous les conteneurs s’exécutant dans un
pod. La préservation des données est garantie malgré les
redémarrages des conteneurs individuels.

Plusieurs types de volumes sont à distinguer, selon la façon


dont le volume est supporté et le régime d’exploitation (la
sémantique) :

• les volumes locaux à un nœud, par exemple emptyDir


et hostPath ;

• les volumes génériques mis en réseau, par exemple nfs,


glusterfs ou cephfs ;

• les volumes spécifiques à un fournisseur d’accès cloud,


par exemple awsElasticBlockStore, azureDisk ou
gcePersistentDisk ;

• les volumes à usage spécial, comme secret et gitRepo


(ce dernier va devenir obsolète).
Vous choisirez le type de volume en fonction de vos besoins
exacts. Pour un espace de stockage temporaire, le type
emptyDir conviendra parfaitement. En revanche, si vos
données doivent survivre à la panne d’un nœud, vous vous
dirigerez vers les volumes réseau ou les volumes spécifiques
à un fournisseur de cloud si vous utilisez Kubernetes dans
un environnement de cloud public.
Échanger des données entre
conteneurs par un volume local

Problème
Vous utilisez un pod avec au moins deux conteneurs et
voulez pouvoir échanger des données en utilisant des
opérations du système de fichiers.

Solution
Créez un volume local de type emptyDir.

Vous commencez par créer un manifeste de code, comme le


suivant. Il doit définir deux conteneurs c1 et c2 ; chacun
doit monter le volume local nommé xchange dans le
système de fichiers au moyen de points de montage
différents :

Listing 8.1 : exchange.yaml

apiVersion: v1
kind: Pod
metadata:
name: sharevol
spec:
containers:
- name: c1
image: centos:7
command:
- "bin/bash"
- "-c"
- "sleep 10000"
volumeMounts:
- name: xchange
mountPath: "/tmp/xchange"
- name: c2
image: centos:7
command:
- "bin/bash"
- "-c"
- "sleep 10000"
volumeMounts:
- name: xchange
mountPath: "/tmp/data"
volumes:
- name: xchange
emptyDir: {}

Vous pouvez ensuite lancer votre pod (nommé exec ici),


créer des données dans un conteneur, puis les lire depuis
l’autre :

$ kubectl create -f exchangedata.yaml


pod "sharevol" created
$ kubectl exec sharevol -c c1 -i -t -- bash
[root@sharevol /]# mount | grep xchange
/dev/vda1 on /tmp/xchange type ext4
(rw,relatime,data=ordered)
[root@sharevol /]# echo 'un peu de data' >
/tmp/xchange/data
[root@sharevol /]# exit

$ kubectl exec sharevol -c c2 -i -t -- bash


[root@sharevol /]# mount | grep /tmp/data
/dev/vda1 on /tmp/data type ext4
(rw,relatime,data=ordered)

[root@sharevol /]# cat /tmp/data/data


un peu de data

Discussion
Le volume local est supporté par le nœud dans lequel
s’exécutent le code et ses conteneurs. Si ce nœud s’arrête,
ou si vous devez en assurer la maintenance (voir
Chapitre 12), le volume local se volatilise et toutes les
données sont perdues.

Ce genre de volume local volatil trouve son intérêt par


exemple comme espace de stockage temporaire ou lorsque
l’état canonique idéal peut être obtenu par ailleurs, par
exemple par un baquet (bucket) S3 dans AWS. Dans le cas
général, vous utiliserez plutôt un volume supporté par un
mécanisme de stockage réseau (voir Chapitre 8).

Voir aussi
• Documentation concernant les volumes Kubernetes
(https://kubernetes.io/docs/concepts/storage/vo
lumes/)
Passage d’une clé d’accès API à
un pod via un secret

Problème
Vous êtes administrateur et avez besoin de rendre disponible
aux développeurs une clé d’accès API de façon sécurisée,
c’est-à-dire sans la transmettre en clair dans vos
manifestes Kubernetes.

Solutions
Utilisez un volume local du type secret.

Nous supposons que vous avez besoin de donner à vos


développeurs accès un service externe au moyen d’une
phrase secrète, « ouvrir sesame ».

Vous devez d’abord créer un fichier texte portant le nom


passphrase et contenant l’expression secrète :

$ echo -n "ouvrir sesame" > ./passphrase

Vous créez ensuite le composant secret au moyen de ce


fichier :
$ kubectl create secret generic pp --from-
file=./passphrase
secret "pp" created
$ kubectl describe secrets/pp
Name: pp
Namespace: default
Labels: <none>
Annotations: <none>

Type: Opaque
Data
====
passphrase: 13 bytes

La partie du travail qui incombe à l’administrateur est


terminée. Vos développeurs peuvent consommer ce secret.
Changeons donc de rôle et supposons que nous sommes un
développeur qui a besoin d’utiliser la phrase secrète dans un
pod.

Supposons que nous ayons envie de monter ce composant


secret en tant que volume dans le pod pour pouvoir lire son
contenu comme un fichier normal. Nous commençons par
créer le pod puis montons le volume :

Listing 8.2 : ppconsumer.yaml

apiVersion: v1
kind: Pod
metadata:
name: ppconsumer
spec:
containers:
- name: shell
image: busybox
command:
- "sh"
- "-c"
- "mount | grep access && sleep 3600"
volumeMounts:
- name: passphrase
mountPath: "/tmp/access"
readOnly: true
volumes:
- name: passphrase
secret:
secretName: pp

Démarrons maintenant ce pod et consultons ses fichiers


journaux. Nous devrions voir apparaître le fichier secret
nommé pp situé au point de montage
/tmp/access/passphrase :

$ kubectl create -f ppconsumer.yaml


pod "ppconsumer" created

$ kubectl logs ppconsumer


tmpfs on /tmp/access type tmpfs (ro,relatime)
Pour lire la phrase de passe depuis le conteneur en cours
d’exécution, il suffit donc de lire le fichier passphrase qui se
trouve dans /tmp/access/ :

$ kubectl exec ppconsumer -i -t -- sh

/ # cat /tmp/access/passphrase
ouvrir sesame

Discussion
Les secrets n’existent que dans le contexte d’un espace de
noms. Il faut en tenir compte pour les configurer et les
utiliser.

Vous disposez de deux techniques pour accéder à un secret


depuis un conteneur qui s’exécute dans un pod :

• en tant que volume, comme montré dans la solution, le


contenu étant stocké dans un volume de type tmpfs ;

• en tant que variable d’environnement.

Précisons que la taille d’un secret ne peut dépasser 1 Mo.

En plus des secrets définis par vous, Kubernetes en définit


automatiquement pour les comptes des services afin
d’accéder à l’API. C’est ainsi qu’une fois Prometheus installé
(voir Chapitre 11), le tableau de bord Kubernetes permet de
voir quelque chose dans le style de la Figure 8.1.
Figure 8.1 : Aperçu du secret de compte du service Prometheus.

La commande kubectl create secret permet de gérer trois types


de secret différents. Vous choisirez en fonction de vos besoins :

• Le type docker-registry est à utiliser avec


un registre Docker.

• Le type generic est celui utilisé dans notre


solution ; il crée un secret à partir d’un fichier
local, d’un répertoire local ou d’une valeur
littérale que vous devez dans ce cas encoder en
base 64 vous-même.

• Le type tls permet de créer par exemple un


certificat SSL de sécurité pour des données
entrantes Ingress.
Par sécurité, la commande kubectl describe n’affiche pas
le contenu du secret en texte clair, pour éviter toute
indiscrétion. En revanche, vous pouvez facilement décoder le
secret manuellement car il n’est pas crypté, seulement codé
en base64 :

$ kubectl get secret pp -o yaml | \


grep passphrase | \
cut -d":" -f 2 | \
awk '{$1=$1};1' | \
base64 --decode
ouvrir sesame

La première ligne de cette longue commande récupère une


représentation au format YAML du secret. La deuxième ligne
exploite grep pour en extraire la valeur de passphrase :
b3BlbiBzZXN-hbWU= (notez l’existence d’une espace en
début de valeur). La commande cut récupère le seul
contenu et la commande awk supprime l’espace initiale.
Enfin, nous obtenons le secret dévoilé grâce à la commande
base64.

Avant la version 1.7 de Kubernetes, le serveur API stockait les secrets


en texte clair dans etcd. Dorénavant, vous pouvez les crypter en
ajoutant l’option --experimental-encryption-provider-config
lors du lancement de votre serveur kube-apiserver.

Voir aussi
• Documentation Kubernetes sur les secrets
(https://kubernetes.io/docs/concepts/confi-
guration/secret/)

• Cryptage de données secrètes


(https://kubernetes.io/docs/tasks/administer-
cluster/encrypt-data/)
Fournir des données de
configuration à une application

Problème
Vous avez besoin de fournir des données de configuration à
une application sans devoir les stocker dans l’image de
conteneur, ni les écrire en dur dans la spécification du pod.

Solution
Utilisez une mappe de configuration. C’est une ressource
Kubernetes de première classe qui permet de fournir des
données de configuration à un pod en utilisant des variables
d’environnement ou un fichier.

Supposons que nous désirions créer une configuration


utilisant la clé siseversion et la valeur 0.9. L’opération est
très simple :

$ kubectl create configmap siseconfig --from-


literal=siseversion=0.9
configmap "siseconfig" created

Vous pouvez immédiatement utiliser la mappe de


configuration dans un déploiement, par exemple dans un
fichier manifeste tel que celui-ci :

Listing 8.3 : cmapp.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: cmapp
spec:
replicas: 1
template:
metadata:
labels:
app: cmapp
spec:
containers:
- name: sise
image:
mhausenblas/simpleservice:0.5.0
ports:
- containerPort: 9876
env:
- name:
SIMPLE_SERVICE_VERSION
valueFrom:
configMapKeyRef:
name: siseconfig
key: siseversion
Cette première approche permet de transmettre la
configuration dans une variable d’environnement. Voyons
maintenant comment monter la configuration en tant que
fichier dans le pod, en utilisant un volume.

Partons du fichier de configuration suivant, portant le nom


example.cfg :

Listing 8.4 : example.cfg

debug: true
home: ⁓/abc

Vous créez ensuite votre mappe de configuration qui va


exploiter le fichier de configuration, comme ceci :

$ kubectl create configmap configfile --from-


file=example.cfg

Vous pouvez ensuite vous servir de cette mappe comme


n’importe quel autre volume. L’exemple suivant montre le
fichier manifeste d’un pod portant le nom oreilly qui
utilise l’image busybox et se contente de se mettre en veille
pendant 3 600 secondes. Dans la section volumes, vous
remarquez un volume portant le nom oreilly qui utilise
comme mappe le fichier nommé configfile que nous venons
de créer. Le volume est monté dans le chemin /oreilly à
l’intérieur du conteneur, ce qui permet au fichier d’être
accessible dans le pod :

Listing 8.5 : cmconsumer.yaml

apiVersion: v1
kind: Pod
metadata:
name: oreilly
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /oreilly
name: oreilly
volumes:
- name:
configMap:
name: configfile

Une fois que vous avez créé le pod, vous pouvez vérifier que
le fichier nommé example.cfg y a bien été inséré :

$ kubectl exec -ti oreilly -- ls -l /oreilly


total 0
lrwxrwxrwx 1 root root 18 Dec 16 19:36
example.cfg -> ..data/example.cfg
$ kubectl exec -ti oreilly -- cat
/oreilly/example.cfg
debug: true
home: ⁓/abc

Pour un exemple complet de création d’une mappe de


configuration à partir d’un fichier (voir Chapitre 11).

Voir aussi
• Configuration d’un pod pour utiliser une mappe de
configuration
(https://kubernetes.io/docs/tasks/configure-
pod-container/configure-pod-configmap/)
Utiliser un volume persistant
avec Minikube

Problème
Vous ne voulez pas que les données stockées sur un disque
utilisé par le conteneur soient perdues lors du redémarrage
du pod qui héberge ce conteneur.

Solution
Utilisez un volume persistant PV (Persistant Volume). Si vous
utilisez Minikube, vous créez un tel volume comme étant de
type hostPath et vous le montez comme un volume
classique dans le système de fichiers du conteneur.

Commencez par définir le composant PV correspondant


hostpathpv dans un fichier manifeste portant le nom
hostpath-pv.yaml :

Listing 8.6 : hostpath-pv.yaml

kind: PersistentVolume
apiVersion: v1
metadata:
name: hostpathpv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/tmp/pvdata"

Avant de créer le volume persistant, il faut préparer le


répertoire nommé /tmp/pvdata sur le nœud, c’est-à-dire sur
l’instance Minikube. Pour accéder au nœud dans lequel
s’exécute le cluster Kubernetes, vous utilisez minikube ssh
:

$ minikube ssh

$ mkdir /tmp/pvdata && \


echo 'Ce contenu persiste et signe' >
/tmp/pvdata/index.html

$ cat /tmp/pvdata/index.html
Ce contenu persiste et signe

$ exit
Une fois le répertoire préparé sur le nœud, vous pouvez
créer le volume à partir du fichier manifeste hostpath-
pv.yaml :

$ kubectl create -f hostpath-pv.yaml


persistentvolume "hostpathpv" created

$ kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY
STATUS ... ... ...
hostpathpv 1Gi RWO Retain
Available ... ... ...

$ kubectl describe pv/hostpathpv


Name: hostpathpv
Labels: type=local
Annotations: <none>
StorageClass: manual
Status: Available
Claim:
Reclaim Policy: Retain
Access Modes: RWO
Capacity: 1Gi
Message:
Source:
Type: HostPath (bare host directory
volume)
Path: /tmp/pvdata
Events: <none>
Toutes les étapes précédentes sont réalisées avec un rôle
admin. Vous créez les volumes persistants puis les rendez
disponibles aux développeurs sur le cluster Kubernetes.

Il ne reste plus alors qu’à adopter un rôle de développeur


pour utiliser le volume dans un pod. Pour ce faire, vous
définissez une réclamation de volume persistant, c’est-à-
dire une action PVC (Persistant Volume Claim). Le mot
« claim » souligne le fait que vous réclamez littéralement
un volume persistant satisfaisant à certaines
caractéristiques, notamment de taille ou de classe de
stockage.

Commencez par créer un fichier manifeste que vous


nommez pvc.yaml pour définir le PVC en demandant 200 Mo
d’espace de stockage :

Listing 8.7 : pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mypvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Mi

Vous démarrez ensuite le PVC et vérifiez son statut :

$ kubectl create -f pvc.yaml


persistentvolumeclaim "mypvc" created

$ kubectl get pv
NAME CAPACITY ACCESSMODES ... STATUS
CLAIM STORAGECLASS
hostpathpv 1Gi RWO ... Bound
default/mypvc manual

Prenez bien note du fait que le volume hostpathpv est


passé du statut Available à Bound.

Vous n’avez plus qu’à utiliser les données du PV dans un


conteneur, au moyen d’un déploiement qui va le monter
dans le système de fichiers. Nous créons un fichier portant
le nom nginx-using-pv.yaml et contenant ceci :

Listing 8.8 : nginx-using-pv.yaml

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: nginx-with-pv
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: webserver
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath:
"/usr/share/nginx/html"
name: webservercontent
volumes:
- name: webservercontent
persistentVolumeClaim:
claimName: mypvc

Il ne reste plus qu’à lancer le déploiement :

$ kubectl create -f nginx-using-pv.yaml


deployment "nginx-with-pv" created

$ kubectl get pvc


NAME STATUS VOLUME CAPACITY ACCESSMODES
STORAGECLASS AGE
mypvc Bound hostpathpv 1Gi RWO
manual 12m
Vous constatez que le volume persistant est utilisé au moyen
du PVC que nous avons défini.

Vous vérifiez que les données sont réellement arrivées en


créant un service avec un objet d’entrée ingress (voir
Chapitre 5). Vous essayez ensuite d’y accéder ainsi :

$ curl -k -s https://192.168.99.100/web
Ce contenu persiste et signe

Félicitations ! En tant qu’admin, vous avez provisionné un


volume persistant et en tant que développeur vous l’avez
obtenu par une réclamation de volume persistant, puis vous
l’avez utilisé depuis un déploiement dans un pod en le
montant dans le système de fichiers du conteneur.

Discussion
Notre solution utilisait le type hostPath pour le volume
persistant. Dans un environnement de production, vous ne
procéderez pas ainsi. Vous demanderez à votre
administrateur de cluster de provisionner un volume réseau
supporté par NFS ou un volume EBS d’Amazon afin de
garantir que les données vont survivre aux pannes d’un
nœud.

Précisons que les volumes persistants sont des ressources de niveau


cluster, donc qui ne sont pas repérées par un espace de noms. En
revanche, les réclamations de volume PVC sont définies dans
l’espace de noms. Vous pouvez donc réclamer un volume PV depuis
un espace de noms au moyen d’une action PVC.

Voir aussi
• Documentation des volumes persistants de Kubernetes
(https://kubernetes.io/docs/concepts/storage/pe
rsistent-volumes/)

• Configuration d’un pod pour utiliser un volume


persistant de stockage
(https://kubernetes.io/docs/tasks/configure-
pod-container/configure-persistent-volume-
storage/)
Maîtriser la persistance de
données sur Minikube

Problème
Vous voulez utiliser Minikube pour découvrir comment
déployer une application avec conservation d’état dans
Kubernetes. Cela correspond par exemple au déploiement
d’une base de données MySQL.

Solution
Utilisez un objet de type volume persistant PV (voir la
précédente section) dans la définition du pod ou dans le
modèle de la base de données.

Vous devez d’abord émettre une requête pour obtenir un


certain volume de stockage. Le manifeste d’exemple suivant
demande 1 Go de stockage :

Listing 8.9 : data.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

Lorsque vous réalisez votre PVC sur Minikube, vous pouvez


immédiatement voir le volume persistant être créé pour
répondre à cette réclamation :

$ kubectl create -f data.yaml

$ kubectl get pvc


NAME STATUS VOLUME
CAPACITY ... ... ...
data Bound pvc-da58c85c-e29a-11e7-ac0b-
080027fcc0e7 1Gi ... ... ...

$ kubectl get pv
NAME CAPACITY
... ... ... ... ...
pvc-da58c85c-e29a-11e7-ac0b-080027fcc0e7 1Gi
... ... ... ... ...

Vous pouvez alors utiliser cette réclamation dans le pod.


Dans la section volumes, vous définissez un volume en
fournissant son nom avec un type PVC et une référence au
PVC qui vient d’être créé. Dans le champ volumeMounts,
vous indiquez un chemin d’accès dans le conteneur comme
point de montage. Dans le cas de MySQL, cela correspondra
à /var/lib/mysql :

Listing 8.10 : pvc-mysql-consumer.yaml

apiVersion: v1
kind: Pod
metadata:
name: db
spec:
containers:
- image: mysql:5.5
name: db
volumeMounts:
- mountPath: /var/lib/mysql
name: data
env:
- name: MYSQL_ROOT_PASSWORD
value: root
volumes:
- name: data
persistentVolumeClaim:

claimName: data

Discussion
Dès le départ, Minikube est configuré avec une classe de
stockage par défaut qui définit un mécanisme
d’approvisionnement de volumes persistants. De ce fait, lors
de la création d’une réclamation de volume persistant,
Kubernetes crée de façon dynamique un volume persistant
correspondant pour répondre à la demande.

C’est ce qui s’est produit dans notre solution. Dès que vous
avez créé le PVC nommé data, Kubernetes a créé un volume
PV approprié. Si vous étudiez plus en détail la classe de
stockage par défaut de Minikube, vous pouvez voir le type
d’approvisionneur :

$ kubectl get storageclass


NAME PROVISIONER
standard (default) k8s.io/minikube-hostpath

$ kubectl get storageclass standard -o yaml


apiVersion: storage.k8s.io/v1
kind: StorageClass
...
provisioner: k8s.io/minikube-hostpath
reclaimPolicy: Delete

Cette classe de stockage spécifique utilise un


approvisionneur qui va créer des volumes persistants du
type hostPath. Vous le confirmez en regardant le manifeste
du PV qui vient d’être créé en réponse à la demande produite
juste avant :

$ kubectl get pv
NAME
CAPACITY ... CLAIM ...
pvc-da58c85c-e29a-11e7-ac0b-080027fcc0e7 1Gi
... default/foobar ...
$ kubectl get pv pvc-da58c85c-e29a-11e7-ac0b-
080027fcc0e7 -o yaml
apiVersion: v1
kind: PersistentVolume
...
hostPath:
path: /tmp/hostpath-provisioner/pvc-da58c85c-
e29a-11e7-ac0b-080027fcc0e7
type: ""
...

Vous confirmez que le volume hôte qui a été créé contient


bien la base de données nommée data en vous connectant à
Minikube et en demandant d’afficher la liste des fichiers du
répertoire :

$ minikube ssh
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ' _ '\| |/' _ '\| || , < ( ) ( )| '_'\
/'__'\
| ( ) ( ) || || ( ) || || |\'\ | (_) || |_) )(
___/
(_) (_) (_)(_)(_) (_)(_)(_)
(_)'\___/'(_,__/''\____)

$ ls -l /tmp/hostpath-provisioner/pvc-da58c85c-
e29a-11e7-ac0b-080027fcc0e7
total 28688
-rw-rw---- 1 999 999 2 Dec 16 20:02
data.pid
-rw-rw---- 1 999 999 5242880 Dec 16 20:02
ib_logfile0
-rw-rw---- 1 999 999 5242880 Dec 16 20:02
ib_logfile1
-rw-rw---- 1 999 999 18874368 Dec 16 20:02
ibdata1
drwx------ 2 999 999 4096 Dec 16 20:02 mysql
drwx------ 2 999 999 4096 Dec 16 20:03
oreilly
drwx------ 2 999 999 4096 Dec 16 20:02
performance_schema

Les données sont donc devenues persistantes. Elles resteront


disponibles, même si le pod s’écroule ou si vous le
supprimez.

En général, avec les classes de stockage, un administrateur


de clusters peut définir plusieurs types de stockage qu’il
veut proposer. Du point de vue du développeur, ces classes
constituent une abstraction du stockage, ce qui permet
d’exploiter des actions PVC sans devoir gérer les détails de
l’entité qui fournit ces ressources de stockage.

Voir aussi
• Documentation des volumes persistants de Kubernetes
(https://kubernetes.io/docs/concepts/storage/pe
rsistent-volumes/#persistentvolumeclaims)

• Documentation des classes de stockage


(https://kubernetes.io/docs/concepts/storage/st
orage-classes/)
Approvisionnement dynamique
de stockage persistant sur GKE

Problème
Au lieu d’approvisionner manuellement des volumes
persistants avec des actions de réclamation PVC (voir la
précédente section), vous voulez automatiser le processus
pour approvisionner les volumes PV de façon dynamique en
fonction d’exigences de volume ou de facturation.

Solution
Pour le cas spécifique de Google GKE, vous suivrez la
procédure décrite dans l’article de blog de Saad Ali
« Dynamic Provisioning and Storage Classes in Kubernetes »
(http://blog.kubernetes.io/2016/10/dynamic-
provisioning-and-storage-in-kubernetes.html).

Discussion
La logique générale d’approvisionnement et de réclamation
de volumes persistants est illustrée dans la Figure 8.2.
Figure 8.2 : Logique d’approvisionnement et de réclamation de volumes persistants.

Le processus montre un travail de coordination entre


admins et développeurs pour les types et capacités des
volumes disponibles. L’approche d’approvisionnement
dynamique simplifie ce travail.
CHAPITRE 9
Adaptation à la charge (scaling)

Dans Kubernetes, le mécanisme d’adaptation aux variations


de la charge de traitement (échelonnage ou scaling) prend un
sens différent selon l’utilisateur. Deux approches sont à
distinguer :

• Scaling de niveau cluster, ou de niveau infrastructure. Il


s’agit du processus automatisé d’ajout et de
suppression de nœuds de travail en fonction du taux
d’utilisation réel du cluster.

• Scaling de niveau applicatif, ou de niveau pod. Désigne


le processus automatisé de modification des
caractéristiques d’un pod en fonction de différentes
métriques. Il peut s’agir de signaux à bas niveau tels
que le taux d’utilisation du processeur ou de plus haut
niveau, comme le nombre de requêtes HTTP servies par
seconde pour un pod. Il existe deux styles de
mécanismes d’adaptation au niveau pod :

HPA (Horizontal Pod Autoscaler) : augmente ou
diminue le nombre de répliques de pod en
fonction de certaines métriques.


VPA (Vertical Pod Autoscaler) : augmente ou
diminue les demandes de ressources des
conteneurs qui s’exécutent dans un pod. Ce
mécanisme vertical VBA est encore en cours de
développement et nous ne le décrirons pas ici.
Pour en savoir plus, vous pouvez parcourir un
article de l’auteur Michael à propos des
ressources de conteneurs
(https://hackernoon.com/container-
resource-consumption-too-impor-tant-to-
ignore-7484609a3bb7).

Nous allons d’abord examiner le scaling au niveau cluster


pour AWS et GKE, puis nous verrons celui au niveau
applicatif, avec le mécanisme HPA.

N. d. T. : Le terme anglais scaling ne sera pas traduit, car la traduction


française la plus directe serait « échelonnage », qui désigne un
fractionnement d’un tout en parties, plutôt qu’un changement de
géométrie pour s’adapter à un changement de demande. En
revanche, l’action de redimensionner est assez fidèle.
Redimensionner un
déploiement

Problème
Vous avez besoin de rendre un déploiement adaptable à la
charge dans le sens horizontal, donc en faisant varier le
nombre de réplicas.

Solution
Pour adapter un déploiement à une augmentation de charge,
vous utilisez la commande kubectl scale.

Repartons du déploiement d’exemple nommé fancyapp vu


dans le Chapitre 4. Il comportait cinq réplicas. Si ce
déploiement n’est pas encore en exécution, commencez par
le créer avec la commande suivante :

kubectl create -f fancyapp.yaml

Commençons par voir comment répondre à une baisse de


charge. Nous n’avons plus besoin de cinq réplicas et trois
suffiront. Voici comment adapter le déploiement à n’utiliser
que trois réplicas :
$ kubectl get deploy fancyapp
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE
AGE
fancyapp 5 5 5 5
9m

$ kubectl scale deployment fancyapp --replicas=3


deployment "fancyapp" scaled

$ kubectl get deploy fancyapp


NAME DESIRED CURRENT UP-TO-DATE AVAILABLE
AGE
fancyapp 3 3 3 3
10m

Nous venons de redimensionner de façon manuelle, mais


vous pouvez automatiser le processus, comme nous le
verrons dans la section à venir « Redimensionnement
horizontal de pod dans GKE ».
Redimensionner
automatiquement un cluster
dans GKE

Problème
Vous voulez que votre cluster Google GKE adapte
automatiquement sa géométrie en termes de nombre de
nœuds selon la charge.

Solution
Utilisez le mécanisme de GKE Cluster Autoscaler. Dans cette
section, nous supposons que vous avez déjà installé la
commande gcloud et configuré l’environnement,
notamment en créant un projet et en activant la facturation.

Nous commençons par créer un cluster avec un nœud de


travail et vérifions que nous pouvons bien y accéder par
kubectl :

$ gcloud container clusters create --num-nodes=1


supersizeme
Creating cluster supersizeme...done.
Created
[https://container.googleapis.com/v1/projects/k8
s-cookbook/zones/...].
kubeconfig entry generated for supersizeme.
NAME ZONE MASTER_VERSION MASTER_IP ... STATUS
supersizeme europe-west2-b 1.7.8-gke.0
35.189.116.207 ... RUNNING

$ gcloud container clusters get-credentials


supersizeme \
--zone europe-west2-b --project k8s-
cookbook

Nous autorisons ensuite le mécanisme autoscaling :

$ gcloud beta container clusters update


supersizeme --enable-autoscaling \
--min-
nodes=1 --max-nodes=3 \
--
zone=europe-west2-b \
--node-
pool=default-pool

Précisons que si vous n’avez pas encore autorisé le groupe


de commandes beta, il vous sera demandé dans la présente
étape de l’installer.

Si vous regardez maintenant la console Google Cloud, vous


devez voir apparaître quelque chose dans le style de la
Figure 9.1.
Figure 9.1 : Aperçu de la console Google Cloud montrant la taille initiale du cluster pour
un nœud.

Lançons maintenant 15 pods par un déploiement pour


générer suffisamment de charge de travail afin de
déclencher le mécanisme de redimensionnement
automatique du cluster :

$ kubectl run ghost --image=ghost:0.9 --


replicas=15
Dorénavant, vous devez voir apparaître un cluster avec trois
nœuds (Figure 9.2).

Figure 9.2 : La console Google Cloud montre que le cluster a été redimensionné pour
trois nœuds.

La totalité des interactions est visible dans la Figure 9.3 :

• La session en haut à gauche montre la charge (la


création des 15 pods qui a déclenché l’événement
scaling).
• La session de l’angle supérieur droit montre la
commande gcloud qui a autorisé le
redimensionnement du cluster.

• La session de bas d’écran montre les résultats affichés


par la commande kubectl get nodes --watch qui
confirme quels nœuds sont actuellement disponibles.

Figure 9.3 : Trois fenêtres de terminal montrant un redimensionnement de cluster en


action.

Précisons que tous les nœuds du bac de nœuds (node pool)


doivent avoir la même capacité, les mêmes labels et les
mêmes pods système s’y exécutant. Pensez à vérifier que
votre quota suffira au moment de spécifier les limites
maximales du bac de nœuds.
N’oubliez pas ensuite de lancer la commande suivante
lorsque vous avez terminé pour ne pas continuer à être
facturé pour des ressources devenues inutiles :

gcloud container clusters delete supersizeme

Voir aussi
• Cluster Autoscaler dans le référentiel
kubernetes/autoscaler
(https://github.com/kubernetes/autoscaler/tree/
master/cluster-autoscaler)

• Cluster Autoscaler dans la documentation de GKE


(https://cloud.google.com/container-
engine/docs/cluster-autoscaler)
Redimensionner
automatiquement un cluster
AWS

Problème
Vous voulez que votre cluster Kubernetes fonctionnant dans
AWS EC2 adapte automatiquement sa géométrie en termes
de nombre de nœuds selon la charge.

Solution
Utilisez le mécanisme AWS Cluster Autoscaler. C’est un
paquetage Helm qui exploite les groupes autoscaling AWS. Si
vous n’avez pas encore installé Helm, voyez d’abord le début
du Chapitre 14.
Redimensionnement horizontal
de pod dans GKE

Problème
Vous voulez faire augmenter ou réduire automatiquement le
nombre de pods d’un déploiement en fonction de la charge.

Solution
Utilisez le mécanisme HPA (Horizontal Pod Autoscaler).

Commencez par créer une application (un environnement


PHP et un serveur) qui va servir de cible à HPA :

$ kubectl run appserver --


image=gcr.io/google_containers/hpa-example \
--requests=cpu=200m --
expose --port=80
service "appserver" created
│NAME ZONE MASTER_VERSION
deployment "appserver" created

Créez ensuite le mécanisme HPA en définissant un


paramètre déclencheur cpu-percent=40. Cela signifie que
l’utilisation du processeur ne doit jamais dépasser les 40 % :
$ kubectl autoscale deployment appserver --cpu-
percent=40 --min=1 --max=5
deployment "appserver" autoscaled

$ kubectl get hpa --watch


NAME REFERENCE TARGETS
MINPODS MAXPODS REPLICAS AGE
appserver Deployment/appserver <unknown> / 40%
1 5 0 14s

Ouvrez alors une autre session de terminal pour surveiller le


déroulement du déploiement :

$ kubectl get deploy appserver --watch

Dans une troisième fenêtre de terminal, lancez le générateur


de charge :

$ kubectl run -i -t loadgen --image=busybox


/bin/sh
If you don't see a command prompt, try pressing
enter.

/ # while true; do wget -q -O-


http://appserver.default.svc.cluster.local; done

Un aperçu des différentes étapes du processus peut être


visualisé en montrant les trois fenêtres de terminal
(Figure 9.4).
Figure 9.4 : Aperçu des terminaux de mise en place d’un mécanisme HPA.

La Figure 9.5 montre l’effet de HPA sur le déploiement du


serveur appserver, cette fois-ci tel que vu par le tableau de
bord de Kubernetes.
Figure 9.5 : Tableau de bord de Kubernetes montrant l’effet de HPA.

Discussion
Le redimensionnement que nous venons de décrire
correspond à une adaptation automatique du nombre de
réplicas grâce au contrôleur HPA, qui peut être influencé par
une ressource HPA. Le contrôleur (qui fait partie du
gestionnaire de contrôleurs dans le plan de contrôle)
surveille les métriques des pods grâce à des instances
cAdvisor qui s’exécutent sur chaque nœud du cluster, puis
il les agrège au moyen de Heapster. Le contrôleur HPA
calcule alors le nombre de réplicas nécessaires pour
satisfaire à la métrique cible définie dans la ressource HPA
(la communauté Kubernetes parle d’algorithme
Autoscaling). Grâce à ce calcul, le contrôleur HPA adapte le
nombre de réplicas sur la ressource cible, qui peut être par
exemple un déploiement.

Vous ne devez pas négliger le fait que le mécanisme de


scaling automatique peut être difficile à régler. L’ajustement
des métriques à bas niveau telles que le taux d’utilisation
CPU ou l’empreinte mémoire RAM peuvent ne peut pas
toujours avoir l’impact attendu. Dès que possible, choisissez
plutôt les métriques spécifiques de niveau application
(https://blog.openshift.com/kubernetes-1-8-now-
custom-metrics/).

Voir aussi
• Mécanisme HPA (Horizontal Pod Autoscaler)
(https://github.com/kubernetes/charts/tree/mast
er/stable/cluster-autoscaler)

• Présentation du HPA
(https://kubernetes.io/docs/tasks/run-
application/horizontal-pod-autoscale-
walkthrough/)

• Article de Jerzy Szczepkowski et Marcin Wielgus


(http://blog.kubernetes.io/2016/07/autoscaling-
in-kubernetes.html)
• Démonstration d’autoscaling dans GKE
(https://github.com/mhausenblas/k8s-autoscale)
CHAPITRE 10
Sécurité

Lorsqu’il s’agit d’exécuter des applications dans Kubernetes,


les développeurs et les opérateurs « devops » doivent se
partager les responsabilités pour garantir que les vecteurs
d’attaque restent les plus petits possibles, que les principes
de moindre privilège sont bien appliqués et que les accès aux
ressources sont clairement définis. Nous allons voir dans ce
chapitre des sections que vous pouvez et devez mettre en
place pour garantir la sécurité de fonctionnement de votre
cluster et de vos applications. Voici les sujets abordés :

• rôle et usage des comptes de service ;

• contrôle d’accès basé rôles RBAC (Role Based Access


Control) ;

• définition du contexte de sécurité d’un pod.


Fournir un identifiant unique
par application

Problème
Vous voulez qu’une application dispose d’une identité
unique afin de pouvoir contrôler les accès aux ressources à
un niveau très détaillé.

Solution
Vous créez un compte de service, puis l’utilisez dans une
spécification de pod.

Il faut d’abord créer le nouveau compte de service, par


exemple nommé myappsa. Vous regardez ensuite ce qui le
constitue :

$ kubectl create serviceaccount myappsa


serviceaccount "myappsa" created
$ kubectl describe sa myappsa
Name: myappsa
Namespace: default
Labels: <none>
Annotations: <none>

Image pull secrets: <none>


Mountable secrets: myappsa-token-rr6jc

Tokens: myappsa-token-rr6jc

$ kubectl describe secret myappsa-token-rr6jc


Name: myappsa-token-rr6jc
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-
account.name=myappsa
kubernetes.io/service-
account.uid=0baa3df5-c474-11e7-8f08...

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9 ...

Ce compte de service est utilisable dans un pod, comme


ceci :

Listing 10.1 : simple-pod-with-sa.yaml

kind: Pod
apiVersion: v1
metadata:
name: myapp
spec:
serviceAccountName: myappsa
containers:
- name: main
image: centos:7
command:
- "bin/bash"
- "-c"
- "sleep 10000"

Vérifiez ensuite que le compte de service est correctement


utilisé par le pod :

$ kubectl exec myapp -c main \


cat
/var/run/secrets/kubernetes.io/serviceaccount/to
ken \
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
...

Le jeton (token) de ce compte de service a été monté à


l’endroit prévu dans le pod et peut donc être utilisé.

Les comptes de service n’ont pas un grand intérêt


isolément, mais ils constituent le soubassement des
contrôles d’accès détaillés, comme nous verrons dans la
prochaine section.
Discussion
Les mécanismes d’authentification et d’autorisation ne sont
accessibles qu’à condition de pouvoir identifier clairement
l’entité concernée. Du point de vue d’un serveur API, il
existe deux genres d’entités : les utilisateurs humains et les
applications. La gestion des identités d’utilisateurs dépasse
le cadre fonctionnel de Kubernetes ; en revanche, il existe
une ressource de première classe pour incarner l’identité
d’une application, il s’agit du compte de service.

Techniquement, l’authentification d’une application est


capturée par le biais d’un jeton (token) qui est disponible
dans un fichier situé à cet emplacement :

/var/run/secrets/kubernetes.io/serviceaccount/to
ken

Cet emplacement est monté automatiquement au moyen


d’un secret. Les comptes de service sont situés dans un
espace de noms en tant que ressources, et sont représentés
ainsi :

system:serviceaccount:$NAMESPACE:$SERVICEACCOUNT

Voici par exemple la liste des comptes de service d’un espace


de noms :
$ kubectl get sa
NAME SECRETS AGE
default 1 90d
myappsa 1 19m
prometheus 1 89d

Dans cette liste, le compte nommé default est créé


automatiquement. C’est ce compte qui est associé d’office
dans l’espace de noms à un pod si vous ne lui attribuez pas
un compte de service spécifique.

Voir aussi
• Gestion des comptes de service
(https://kubernetes.io/docs/admin/service-
accounts-admin/)

• Configuration des comptes de service pour les pods


(https://kubernetes.io/docs/tasks/configure-
pod-container/configure-service-account/)

• Extraction d’une image d’un référentiel (registry) privé


(https://kubernetes.io/docs/tasks/configure-
pod-container/pull-image-private-registry/)
Lister et visualiser les
informations de contrôle d’accès

Problème
Vous avez besoin de savoir quelles actions vous sont
autorisées, par exemple la mise à jour d’un déploiement ou
l’affichage des secrets.

Solution
La solution qui suit suppose que vous avez choisi comme
méthode d’autorisation le contrôle d’accès basé rôles RBAC.

Pour savoir si une action sur une ressource est autorisée à


un utilisateur en particulier, vous utilisez la commande
kubectl auth can-i (puis-je ?). L’exemple de commande
qui suit sert à vérifier si le compte de service nommé system
: serviceaccount : sec : myappsa est autorisé à lister
les pods dans l’espace de noms sec :

$ kubectl auth can-i list pods --


as=system:serviceaccount:sec:myappsa -n=sec
yes
Pour pouvoir tester cette section dans Minikube, il faut ajouter le
paramètre suivant lors de l’exécution du binaire :

--extra-config=apiserver.Authorization.Mode=RBAC

Voici comment obtenir la liste des rôles disponibles dans un


espace de noms :

$ kubectl get roles -n=kube-system


NAME
AGE
extension-apiserver-authentication-reader 1d
system: :leader-locking-kube-controller-manager
1d
system: :leader-locking-kube-scheduler
1d
system:controller:bootstrap-signer 1d
system:controller:cloud-provider 1d
system:controller:token-cleaner 1d

$ kubectl get clusterroles -n=kube-system


NAME
AGE
admin 1d
cluster-admin 1d
edit 1d
system:auth-delegator 1d
system:basic-user 1d
system:controller:attachdetach-controller 1d
system:controller:certificate-controller
1d
system:controller:cronjob-controller 1d
system:controller:daemon-set-controller 1d
system:controller:deployment-controller 1d
system:controller:disruption-controller 1d
system:controller:endpoint-controller 1d
system:controller:generic-garbage-collector 1d
system:controller:horizontal-pod-autoscaler 1d
system:controller:job-controller 1d
system:controller:namespace-controller 1d
system:controller:node-controller 1d
system:controller:persistent-volume-binder 1d
system:controller:pod-garbage-collector 1d
system:controller:replicaset-controller 1d
system:controller:replication-controller 1d
system:controller:resourcequota-controller 1d
system:controller:route-controller 1d
system:controller:service-account-controller 1d
system:controller:service-controller 1d
system:controller:statefulset-controller 1d
system:controller:ttl-controller 1d
system:discovery 1d
system:heapster 1d
system:kube-aggregator 1d
system:kube-controller-manager 1d
system:kube-dns 1d
system:kube-scheduler 1d
system:node 1d
system:node-bootstrapper 1d
system:node-problem-detector 1d
system:node-proxier 1d
system:persistent-volume-provisioner 1d
view 1d

L’affichage indique les rôles prédéfinis qui sont directement


utilisables pour les comptes utilisateurs et de services.

Voici comment entrer dans les détails d’un rôle pour


connaître les actions autorisées :

$ kubectl describe clusterroles/view -n=kube-


system
Name: view
Labels: kubernetes.io/bootstrapping=rbac-
defaults
Annotations:
rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
Resources Non-
Resource URLs ... ...
--------- ---------
-------- --- ---
bindings []
... ...
configmaps []
... ...
cronjobs.batch []
... ...
daemonsets.extensions []
... ...
deployments.apps []
... ...
deployments.extensions []
... ...
deployments.apps/scale []
... ...
deployments.extensions/scale []
... ...
endpoints []
... ...
events []
... ...
horizontalpodautoscalers.autoscaling []
... ...
ingresses.extensions []
... ...
jobs.batch []
... ...
limitranges []
... ...
namespaces []
... ...
namespaces/status []
... ...
persistentvolumeclaims []
... ...
pods []
... ...
pods/log []
... ...
pods/status []
... ...
replicasets.extensions []
... ...
replicasets.extensions/scale []
... ...
replicationcontrollers []
... ...
replicationcontrollers/scale []
... ...
replicationcontrollers.extensions/scale []
... ...
replicationcontrollers/status []
... ...
resourcequotas []
... ...
resourcequotas/status []
... ...
scheduledjobs.batch []
... ...
serviceaccounts []
... ...
services []
... ...
statefulsets.apps []
... ...

Vous pouvez définir vos propres rôles en plus des rôles par
défaut qui sont définis dans l’espace de noms kube-system,
comme le montre la section suivante.

Si le contrôle d’accès RBAC est actif, au moment où vous tentez


d’accéder au tableau de bord Kubernetes, vous risquez dans la
plupart des environnements, y compris dans Minikube et dans GKE,
de faire face à un code de statut d’accès interdit (403) avec un
message d’erreur tel que le suivant :

User "system:serviceaccount:kube-system:default"
cannot list pods in the
namespace "sec". (get pods)

Pour pouvoir accéder au tableau de bord, vous devez


octroyer les droits appropriés au compte de service nommé
kube-system : default :

$ kubectl create clusterrolebinding


admin4kubesystem \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:default

Précisons que la commande donne beaucoup de droits au


compte de service ; elle est donc déconseillée dans un
environnement de production.

Discussion
Comme le montre la Figure 10.1, plusieurs éléments sont en
interaction lors d’une autorisation de type RBAC :
• une entité : c’est un groupe, un utilisateur ou un compte
de service ;

• une ressource : un pod, un service ou un secret ;

• un rôle : pour définir les règles des actions sur la


ressource ;

• un lien de rôle : pour appliquer un rôle à une entité.

Figure 10.1 : Concept de contrôle d’accès RBAC.

Dans les règles d’un rôle, les actions sur une ressource
correspondent à des verbes :

• get, list, watch

• create

• update/patch

• delete

Deux types de rôles sont à considérer :


• au niveau cluster : les rôles de cluster et les liens de rôle
correspondants ;

• au niveau espace de noms : les rôles et liens de rôle.

La prochaine section va montrer comment créer vos propres


règles, puis les appliquer à des utilisateurs et à des
ressources.

Voir aussi
• Présentation des autorisations Kubernetes
(https://kubernetes.io/docs/admin/authorization
/)

• Utilisation des autorisations RBAC


(https://kubernetes.io/docs/admin/authorization
/rbac/)
Contrôler l’accès aux ressources

Problème
Vous voulez pouvoir autoriser ou interdire une certaine
action pour un utilisateur ou une application identifié, par
exemple lire les secrets ou mettre à jour un déploiement.

Solution
Supposons que nous ayons besoin d’autoriser une
application à afficher les pods, c’est-à-dire à les lister et à
en extraire des détails.

Nous commençons par créer une définition de pod dans un


manifeste YAML en utilisant un compte de service dédié
nommé myappsa (voir la section « Fournir un identifiant
unique par application » de ce même chapitre) :

Listing 10.2 : pod-with-sa.yaml

kind: Pod
apiVersion: v1
metadata:
name: myapp
namespace: sec
spec:
serviceAccountName: myappsa
containers:
- name: main
image: centos:7
command:
- "bin/bash"
- "-c"
- "sleep 10000"

Nous définissons ensuite un rôle par un manifeste pour


définir les actions autorisées sur les ressources :

Listing 10.3 : pod-reader.yaml

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: podreader
namespace: sec
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]

Il reste à appliquer ce nouveau rôle podreader au compte de


service myappsa en établissant un lien de rôle (binding) dans
un fichier nommé pod-reader-binding.yaml :

Listing 10.4 : pod-reader-binding.yaml


kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: podreaderbinding
namespace: sec
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: podreader
subjects:
- kind: ServiceAccount
name: myappsa
namespace: sec

En supposant que le compte de service a déjà été créé, vous


pouvez utiliser directement les manifestes YAML pour la
création des ressources :

$ kubectl create -f pod-reader.yaml


$ kubectl create -f pod-reader-binding.yaml
$ kubectl create -f pod-with-sa.yaml

Plutôt que de créer des manifestes pour le rôle et le lien de


rôle, vous pouvez directement saisir les commandes
suivantes :

$ kubectl create role podreader \


--verb=get --verb=list \
--resource=pods -n=sec

$ kubectl create rolebinding podreaderbinding \


--role=sec:podreader \
--serviceaccount=sec:myappsa \
--namespace=sec -n=sec

Ajoutons qu’il s’agit ici d’une configuration de contrôle


d’accès dans un espace de noms, puisque nous utilisons des
rôles et des liens de rôle. Pour un contrôle d’accès au niveau
cluster, il faut utiliser les commandes correspondantes
create clusterrole et create clusterrolebinding.

Il n’est dans certains cas pas facile de décider s’il faut utiliser un
rôle classique ou un rôle au niveau cluster et/ou un lien de rôle. Voici
quelques règles qui pourraient vous y aider :

• S’il faut limiter l’accès à une ressource d’un


espace de noms, par exemple un service ou un
pod, dans un certain espace, vous utilisez comme
dans cette section un rôle et un lien de rôle.

• Lorsque vous avez besoin de réutiliser le même


rôle dans plusieurs espaces de noms, optez plutôt
pour un rôle au niveau cluster avec son lien de
rôle.

• Pour limiter l’accès à des ressources globales d’un


cluster, comme des nœuds, ou à des ressources
d’un espace de noms à travers tous les espaces de
noms, vous utiliserez un rôle de niveau cluster
avec son lien de rôle.

Voir aussi
• Configuration de RBAC dans un cluster Kubernetes
(https://docs.bitnami.com/kuber-netes/how-
to/configure-rbac-in-your-kubernetes-cluster/)

• Article d’Antoine Cotten « Kubernetes v1.7 Security in


Practice » (https://acotten.com/post/kube17-
security)
Sécuriser des pods

Problème
Vous avez besoin de définir le contexte sécuritaire d’une
application au niveau pod. C’est par exemple le cas lorsque
vous avez besoin d’exécuter l’application sous forme d’un
processus non privilégié ou de limiter les types de volumes
auxquels elle peut accéder.

Solution
Pour instaurer une police au niveau pod dans Kubernetes,
vous renseignez le champ nommé securityContext dans la
spécification du pod.

Supposons une application qui doit s’exécuter en tant


qu’utilisateur non root. Nous allons dans ce cas utiliser le
contexte de sécurité au niveau conteneur, comme l’exprime
le manifeste suivant :

Listing 10.5 : securedpod.yaml

kind: Pod
apiVersion: v1
metadata:
name: secpod
spec:
containers:
- name: shell
image: centos:7
command:
- "bin/bash"
- "-c"
- "sleep 10000"
securityContext:
runAsUser: 5000

Nous pouvons ensuite créer le code et vérifier quel


utilisateur est associé au conteneur en exécution :

$ kubectl create -f securedpod.yaml


pod "secpod" created

$ kubectl exec secpod ps aux


USER PID %CPU %MEM VSZ RSS TTY STAT START
TIME COMMAND
5000 1 0.0 0.0 4328 672 ? Ss 12:39
0:00 sleep 10000
5000 8 0.0 0.1 47460 3108 ? Rs
12:40 0:00 ps aux

Comme prévu, l’exécution correspond à l’identifiant


d’utilisateur 5000. Vous pouvez bien sûr utiliser le champ
securityContext non seulement au niveau d’un conteneur,
mais au niveau d’un pod.
Une méthode plus puissante pour instaurer des polices au
niveau pod consiste à utiliser le mécanisme PSP (Polices de
Sécurité de Pod). Ce sont des ressources de niveau cluster
pour définir toute une palette de polices, certaines
ressemblant à celles que nous venons de voir, mais
également des restrictions au niveau stockage et réseau.
Pour en savoir plus au sujet de PSP, voyez dans la
documentation Bitnami de Kubernetes.

Voir aussi
• Documentation des polices de sécurité de pod
(https://kubernetes.io/docs/concepts/policy/pod
-security-policy/)

• Configuration d’un contexte de sécurité pour un pod ou


un conteneur
(https://kubernetes.io/docs/tasks/configure-
pod-container/security-context/)

• Documentation Bitnami de Kubernetes, section « Secure


a Kubernetes Cluster with Pod Security Policies »
(https://docs.bitnami.com/kubernetes/how-
to/secure-kubernetes-cluster-psp/).
CHAPITRE 11
Supervision et journalisation
(logging)

Les sections de ce chapitre sont consacrées à la surveillance


du bon fonctionnement ou supervision, ainsi qu’à la
journalisation (logging), tant au niveau de l’infrastructure
qu’au niveau des applications. Les responsabilités des
personnes qui utilisent Kubernetes peuvent être réparties en
deux grandes catégories de rôles :

• Rôles d’administrateur : administrateurs de cluster,


responsables réseau et admin au niveau des espaces de
noms. Toute personne qui se concentre sur le niveau
infrastructure et se pose par exemple des questions du
style : Est-ce que mes nœuds sont en bon état ? Faut-il
ajouter un nouveau nœud de travail ? Quel est le taux
d’utilisation au niveau du cluster ? Est-ce que les
utilisateurs s’approchent de leur plafond d’utilisation ?

• Rôles de développeur : ces personnes se concentrent sur


leur application ; dans le mouvement actuel de
déploiement de microservices, il peut s’agir de dizaines
de logiciels. Une personne endossant un rôle de
développeur peut se poser ce genre de question : Est-ce
que j’ai assez de ressources réservées pour mon
application ? Combien de réplicas dois-je lui attribuer ?
Est-ce que j’ai bien accès aux volumes de stockage
désirés et quel est leur taux de remplissage ? Est-ce
qu’une de mes applications est en train de se bloquer et
pourquoi ?

Nous allons commencer par quelques sections à propos du


monitorat d’un cluster en profitant des sondes Kubernetes
de vitalité (liveness) et de disponibilité (readiness). Nous
verrons ensuite comment surveiller les activités avec
Heapster et Prometheus et finirons par une section
concernant la journalisation.
Accéder aux journaux d’un
conteneur

Problème
Vous avez besoin d’accéder aux journaux d’une application
qui s’exécute dans un conteneur hébergé dans un pod.

Solution
Utilisez la commande kubectl logs. Pour un rappel des
options, utilisez l’aide interne de la commande :

$ kubectl logs --help | more


Print the logs for a container in a pod or
specified resource. If the pod has only one
container, the container name is optional.

Aliases:
logs, log

Examples:
# Return snapshot logs from pod nginx with only
one container
kubectl logs nginx
...
En supposant un pod lancé par un déploiement (voir
Chapitre 4), la commande suivante permet d’afficher le
contenu de ses journaux :

$ kubectl get pods


NAME READY STATUS
RESTARTS AGE
ghost-8449997474-kn86m 1/1 Running 0
1m

$ kubectl logs ghost-8449997474-kn86m


[2017-12-16 18:44:18] INFO Creating table: posts
[2017-12-16 18:44:18] INFO Creating table: users
[2017-12-16 18:44:18] INFO Creating table: roles
[2017-12-16 18:44:18] INFO Creating table:
roles_users
...

Lorsque le pod contient plusieurs conteneurs, vous pouvez n’afficher


que les journaux d’un seul des conteneurs en ajoutant l’option -c à
la commande kubectl logs.
Récupérer d’une avarie avec une
sonde de vitalité

Problème
Vous voulez que Kubernetes redémarre automatiquement le
ou les pods dans lesquels votre application s’exécute si
celle-ci entre dans un état instable ou subit une avarie.

Solution
Servez-vous d’une sonde de vitalité liveness. Si cette sonde
renvoie un échec, le composant kubelet va
automatiquement redémarrer le code. Cette sonde est définie
dans la spécification du code, dans la section containers.
Vous pouvez associer une telle sonde à chacun des
conteneurs d’un pod.

Il existe trois types de sonde. Il peut s’agir d’une commande


exécutée dans le conteneur, d’une requête HTTP en direction
d’une route spécifique qui est servie par un serveur Web
dans le conteneur ou d’une sonde TCP plus générique.

Dans l’exemple suivant, nous utilisons une sonde HTTP


simple :
Listing 11.1 : lprobepod.yaml

apiVersion: v1
kind: Pod
metadata:
name: liveness-nginx
spec:
containers:
- name: liveness
image: nginx
livenessProbe:
httpGet:
path: /
port: 80

Vous trouverez un exemple complet dans la section


« Ajouter des sondes de vitalité et de disponibilité à un
déploiement » plus loin dans ce chapitre.

Voir aussi
• Kubernetes, « Configure Liveness and Readiness Probes »
(https://kubernetes.io/docs/tasks/configure-
pod-container/configure-liveness-readiness-
probes/#define-a-liveness-command)
• Documentation des sondes des conteneurs Kubernetes
(https://kubernetes.io/docs/concepts/workloads/
pods/pod-lifecycle/#container-probes)
Retenir le trafic vers un pod
avec une sonde de disponibilité

Problème
Vos sondes de vitalité indiquent que vos pods sont actifs et
s’exécutent correctement (voir section précédente).
Cependant, vous ne voulez envoyer du trafic vers ces pods
qu’une fois l’application prête à servir les requêtes.

Solution
Vous ajoutez des sondes de disponibilité (readiness) dans la
spécification des pods. Ces sondes de disponibilité peuvent
prendre trois formes (comme celles de vitalité décrites plus
haut). Voici un exemple simple consistant à exécuter un pod
avec l’image Docker nginx. La sonde de disponibilité lance
une requête HTTP vers le port 80 :

Listing 11.2 : rprobepod.yaml

apiVersion: v1
kind: Pod
metadata:
name: liveness-nginx
spec:
containers:
- name: liveness
image: nginx
livenessProbe:
httpGet:
path: /
port: 80

Discussion
La sonde de disponibilité de notre exemple est la même que
celle de vitalité de la section « Récupérer d’une avarie avec
une sonde de vitalité », mais elle devrait en réalité être
différente puisqu’elles ont comme objectif de rapporter des
informations différentes au sujet de l’application. La sonde
de vitalité teste si le processus de l’application est actif,
même s’il n’est pas encore prêt à accepter les requêtes. La
sonde de disponibilité vérifie que l’application est prête à
servir des requêtes. Un pod ne peut donc faire partie du
service qu’une fois que sa sonde de disponibilité le confirme
(voir le Chapitre 5).

Voir aussi
• Cycle de vie d’un conteneur et sondes de conteneur
(https://kubernetes.io/docs/concepts/workloads/
pods/pod-lifecycle/#container-probes)

• Définition de sondes readiness


(https://kubernetes.io/docs/tasks/configure-
podcontainer/configure-liveness-readiness-
probes/#define-readiness-probes)
Ajouter des sondes de vitalité et
de disponibilité à un
déploiement

Problème
Vous voulez pouvoir vérifier de façon automatique que
l’application est en bonne santé et faire en sorte que
Kubernetes agisse dans le cas contraire.

Solution
Pour que Kubernetes se soucie de la santé de votre
application, vous lui ajoutez des sondes de vitalité et de
disponibilité, comme nous allons le voir.

Nous commençons par le manifeste de déploiement :

Listing 11.3 : webserver.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: webserver
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
livenessProbe:
initialDelaySeconds: 2
periodSeconds: 10
httpGet:
path: /
port: 80
readinessProbe:
initialDelaySeconds: 2
periodSeconds: 10
httpGet:
path: /
port: 80

Les deux types de sondes livenessProbe et


readinessProbe sont définies dans la section containers
du manifeste du pod. Voyez aussi les exemples des deux
précédentes sections. Notez les deux nouveaux paramètres
temporels qui sont spécifiés pour les sondes.

Vous pouvez ensuite lancer le code et essayer vos sondes :


$ kubectl create -f webserver.yaml

$ kubectl get pods


NAME READY STATUS
RESTARTS AGE
webserver-4288715076-dk9c7 1/1 Running 0
2m

$ kubectl describe pod/webserver-4288715076-


dk9c7
Name: webserver-4288715076-dk9c7
Namespace: default
Node: node/172.17.0.128
...
Status: Running
IP: 10.32.0.2
Controllers: ReplicaSet/webserver-4288715076
Containers:
nginx:
...
Ready: True
Restart Count: 0
Liveness: http-get http://:80/ delay=2s
timeout=1s period=10s #...
...

Vous remarquez que l’affichage de la commande kubectl


describe a été réduit dans notre exemple. Nous avons
supprimé les nombreuses informations sans intérêt pour
notre discussion.
Discussion
Kubernetes propose toute une panoplie de mécanismes pour
s’assurer de la bonne santé d’un conteneur dans un pod et
de sa disponibilité à servir du trafic réseau. Les sondes
(probes) sont définies au niveau d’un conteneur et non au
niveau du pod, et sont animées par deux composants
différents :

• Sur chaque nœud de travail, un composant kubelet


exploite la directive livenessProbe du manifeste pour
savoir quand il faut redémarrer un conteneur. Ce genre
de sonde de vitalité peut aider à corriger les problèmes
de montée en charge ou les interblocages.

• Un équilibreur de charge de services entre plusieurs


pods va utiliser la directive readinessProbe du
manifeste pour vérifier qu’un pod est prêt à recevoir du
trafic. Si ce n’est pas le cas, ce pod est exclu du stock de
points d’extrémité du service. Précisons qu’un pod est
considéré comme prêt si tous ses conteneurs le sont.

Comment choisir quelle sonde utiliser et à quel moment ?


Tout dépend du comportement des conteneurs. Vous vous
servirez d’une sonde de vitalité (liveness) avec un paramètre
restartPolicy valant soit Always, soit OnFailure lorsque
le conteneur peut et doit être supprimé puis redémarré en
cas d’échec de la sonde. Lorsque vous ne voulez envoyer du
trafic à un pod qu’une fois qu’il est bien prêt à en recevoir,
vous utiliserez une sonde de disponibilité. Notez que dans ce
cas, cette sonde peut être la même que la sonde de vitalité.

Voir aussi
• Configuration des sondes liveness et readiness
(https://kubernetes.io/docs/tasks/confi-gure-
pod-container/configure-liveness-readiness-
probes/)

• Documentation du cycle de vie d’un pod


(https://kubernetes.io/docs/concepts/worklo-
ads/pods/pod-lifecycle/)

• Documentation des containers init (stables à partir de la


version 1.7)
(https://kubernetes.io/docs/concepts/workloads/
pods/init-containers/)
Activer Heapster sur Minikube
pour surveiller des ressources

Problème
Vous voulez surveiller l’utilisation des ressources dans
Minikube au moyen de la commande kubectl top, mais
vous constatez que l’extension Heapster n’est pas active :

$ kubectl top pods


Error from server (NotFound): the server could
not find the requested resource (get services
http:heapster:)

Solution
Les versions récentes de la commande minikube sont
livrées avec un gestionnaire d’extensions qui permet
d’activer Heapster ainsi que quelques autres compléments,
par exemple un contrôleur Ingress, au moyen d’une seule
commande :

$ minikube addons enable heapster

Le fait d’activer l’extension Heapster provoque la création


de deux pods dans l’espace de noms kube-system. un pod
sert à exécuter Heapster et l’autre à exécuter une base de
données de séries temporelles InfluxDB avec un tableau de
bord Grafana.

Après avoir patienté quelques minutes, le temps que


quelques mesures aient été prises, la commande kubectl
top va montrer des métriques de ressources, comme vous
vous y attendez :

$ kubectl top node


NAME CPU(cores) CPU% MEMORY(bytes)
MEMORY%
minikube 187m 9% 1154Mi 60%

$ kubectl top pods --all-namespaces


NAMESPAC NAME
CPU(cores) MEMORY(bytes)
default ghost-2663835528-fb044 0m
140Mi
kube-system kube-dns-v20-4bkhn 3m
12Mi
kube-system heapster-6j5m8 0m
21Mi
kube-system influxdb-grafana-vw9x1 23m
37Mi
kube-system kube-addon-manager-minikube 47m
3Mi
kube-system kubernetes-dashboard-scsnx 0m
14Mi
kube-system default-http-backend-75m71 0m
1Mi
kube-system nginx-ingress-controller-p8fmd 4m
51Mi

Vous pouvez dorénavant accéder au tableau de bord Grafana


et même le personnaliser selon vos besoins :

$ minikube service monitoring-grafana -n kube-


system
Waiting, endpoint for service is not ready
yet...
Waiting, endpoint for service is not ready
yet...
Waiting, endpoint for service is not ready
yet...
Waiting, endpoint for service is not ready
yet...
Opening kubernetes service kube-
system/monitoring-grafana in default browser...

Cette commande doit provoquer le lancement automatique


du navigateur par défaut pour montrer quelque chose dans
le style de la Figure 11.1.
Figure 11.1 : Tableau de bord de Grafana avec les métriques Minikube.

À partir de cette étape, vous pouvez plonger dans les détails


des métriques dans Grafana.
Utiliser Prometheus sur
Minikube

Problème
Vous voulez pouvoir visualiser et interroger les métriques du
système et des applications d’un cluster de façon
centralisée.

Solution
Servez-vous de l’outil Prometheus ainsi :

1. Créez d’abord une mappe de configuration pour


Prometheus.

2. Créez un compte de service pour Prometheus et


attribuez des droits d’accès RBAC à ce compte (voir
Chapitre 10). Vous devez pouvoir accéder à toutes les
métriques.

3. Créez pour Prometheus une application comportant un


déploiement, un service et une ressource Ingress afin
de pouvoir accéder avec un navigateur de l’extérieur du
cluster.
Vous commencez par préparer la configuration Prometheus
au moyen d’un objet ConfigMap (nous avons présenté ce
genre d’objets dans la section « Fournir des données de
configuration à une application » du Chapitre 8). Nous nous
en servirons plus loin dans l’application Prometheus. Créez
un fichier portant le nom prometheus.yml pour recevoir la
configuration, avec le contenu suivant (attention, le suffixe
est bien yml ici) :

Listing 11.4 : prometheus.yml

global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
- job_name: 'kubernetes-nodes'
scheme: https
tls_config:
ca_file:
/var/run/secrets/kubernetes.io/serviceaccount/ca.
crt
server_name: 'gke-k8scb-default-
pool-be16f9ee-522p'
insecure_skip_verify: true
bearer_token_file:
/var/run/secrets/kubernetes.io/serviceaccount/tok
en
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex:
__meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-cadvisor'
scheme: https
tls_config:
ca_file:
/var/run/secrets/kubernetes.io/serviceaccount/ca.
crt
bearer_token_file:
/var/run/secrets/kubernetes.io/serviceaccount/tok
en
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex:
__meta_kubernetes_node_label_(.+)
- target_label: __address__
replacement:
kubernetes.default.svc:443
- source_labels:
[__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement:
/api/v1/nodes/${1}:4194/proxy/metrics
Nous pouvons nous servir de ce fichier pour créer une
mappe de configuration :

$ kubectl create configmap prom-config-cm --


from-file=prometheus.yml

Nous préparons ensuite le compte de service Prometheus et


les droits d’accès appropriés sous forme de liens de rôle,
tout cela dans un fichier manifeste portant le nom
prometheus-rbac.yaml :

Listing 11.5 : prometheus-rbac.yaml

# apiVersion: rbac.authorization.k8s.io/v1beta1
# kind: ClusterRole
# metadata:
# name: prometheus
# rules:
# - apiGroups: [""]
# resources:
# - nodes
# - nodes/proxy
# - services
# - endpoints
# - pods
# verbs: ["get", "list", "watch"]
# - nonResourceURLs: ["/metrics"]
# verbs: ["get"]
# ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: prometheus
namespace: default

Ce manifeste permet maintenant de créer le compte de


service et les liens de rôle :

$ kubectl create -f prometheus-rbac.yaml

La préparation étant achevée au niveau configuration et


accès, nous pouvons nous intéresser à Prometheus.
Rappelons qu’il faut réunir un déploiement, un service et
une ressource Ingress et exploiter la mappe de
configuration et le compte de service que nous venons de
créer.

Nous commençons par définir le manifeste de l’application


Prometheus en donnant au fichier le nom
prometheus.app.yaml :

Listing 11.6 : prometheus.app.yaml

kind:
Deployment
apiVersion:
extensions/v1beta1
metadata:
name: prom
namespace: default
labels:
app: prom
spec:
replicas: 1
selector:
matchLabels:
app: prom
template:
metadata:
name: prom
labels:
app: prom
spec:
serviceAccount:
prometheus
containers:
- name: prom
image:
prom/prometheus
imagePullPolicy: Always
volumeMounts:
- name:
prometheus-volume-1
mountPath:
"/prometheus"
- name: prom-
config-volume
mountPath:
"/etc/prometheus/"
volumes:
- name:
prometheus-volume-1
emptyDir: {}
- name: prom-
config-volume
configMap:
name: prom-
config-cm
defaultMode: 420
---
kind: Service
apiVersion: v1
metadata:
name: prom-svc
labels:
app: prom
spec:
ports:
- port: 80
targetPort: 9090
selector:
app: prom
type:
LoadBalancer
externalTrafficPolicy: Cluster
---
kind: Ingress
apiVersion:
extensions/v1beta1
metadata:
name: prom-
public
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host:
http:
paths:
- path: /
backend:
serviceName: prom-svc
servicePort: 80
Il ne reste qu’à créer l’application à partir du manifeste :

$ kubectl create -f prometheus-app.yaml

Félicitations, vous venez de créer une application complète !


Vous pouvez maintenant accéder à Prometheus via
$MINISHIFT_IP/graph (par exemple, avec
https://192.168.99.100/graph). Vous devriez voir
quelque chose dans le style de la Figure 11.2.

Figure 11.2 : Session de travail dans Prometheus.

Discussion
Prometheus constitue un système de monitorat et d’alerte
puissant et souple. Il vous permet, ainsi que les nombreuses
librairies d’instrumentation disponibles, de créer vos
propres rapports d’application avec des métriques de haut
niveau, par exemple le nombre de transactions réalisées,
selon les mêmes modalités utilisées par un kubelet pour
indiquer le taux d’utilisation CPU.

Malgré les atouts de Prometheus, vous aurez sans doute


envie d’utiliser un autre outil pour visualiser les métriques.
L’approche générique consiste à créer une connexion avec
Grafana.

Il y a un problème connu pour utiliser Prometheus avec les


versions 1.7.0 jusqu’à 1.7.2 de Kubernetes. En effet, le comportement
du kubelet pour exposer les métriques des conteneurs avait changé
dans la version 1.7.0.

Précisons que la configuration de Prometheus montrée dans la


solution est valable pour les versions 1.7.0 à 1.7.2. Si vous utilisez
une version plus récente, vérifiez le fichier de configuration
d’exemple et retouchez les détails impactés.

La solution que nous décrivons ici n’est pas limitée à


Minikube. À partir du moment où vous êtes en mesure de
créer le compte de service, c’est-à-dire que vous avez assez
de droits pour octroyer ceux requis par Prometheus, vous
pouvez appliquer la même solution à différents
environnements, et notamment à GKE, ACS et OpenShift.
Voir aussi
• Instrumentation dans la documentation Prometheus
(https://prometheus.io/docs/practices/instrumen
tation/)

• Grafana avec Prometheus dans la documentation


Prometheus
(https://prometheus.io/docs/visualization/grafa
na/)
Utiliser EFK sur Minikube

Problème
Vous voulez visualiser et analyser les journaux de toutes les
applications du cluster de façon centralisée.

Solution
Utilisez le trio EFK (Elasticsearch - Fluentd - Kibana)
comme nous allons le montrer.

Vérifiez d’abord que Minikube dispose de suffisamment de


ressources. Stipulez par exemple --CPU=4 et --memory=4000
et vérifiez que l’extension ingress est activée, comme ceci :

$ minikube start
Starting local Kubernetes v1.7.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Starting cluster components...
Connecting to cluster...
Setting up kubeconfig...
Kubectl is now configured to use the cluster.
$ minikube addons list | grep ingress
- ingress: enabled

Si l’extension n’est pas activée, vous l’activez maintenant :

$ minikube addons enable ingress

Créez ensuite le fichier manifeste contenant ceci :

Listing 11.7 : efk-logging.yaml

kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: kibana-public
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: null
http:
paths:
- path: /
backend:
serviceName: kibana
servicePort: 5601
---
kind: Service
apiVersion: v1
metadata:
labels:
app: efk
name: kibana
spec:
ports:
- port: 5601
selector:
app: efk
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: kibana
spec:
replicas: 1
template:
metadata:
labels:
app: efk
spec:
containers:
- env:
- name: ELASTICSEARCH_URL
value:
http://kibana:changeme@elasticsearch:9200
name: kibana
image:
docker.elastic.co/kibana/kibana:5.5.1
ports:
- containerPort: 5601
---
kind: Service
apiVersion: v1
metadata:
labels:
app: efk
name: elasticsearch
spec:
ports:
- port: 9200
selector:
app: efk
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: es
spec:
replicas: 1
template:
metadata:
labels:
app: efk
spec:
initContainers:
- name: init-sysctl
image: busybox
imagePullPolicy: IfNotPresent
command:
- sysctl
- '-w'
- vm.max_map_count=262144
securityContext:
privileged: true
containers:
- name: es
image:
docker.elastic.co/elasticsearch/elasticsearch:5.
5.1
ports:
- containerPort: 9200
env:
- name: ES_JAVA_OPTS
value: '-Xms512m -Xmx512m'
---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
name: fluentd
spec:
template:
metadata:
labels:
app: efk
name: fluentd
spec:
containers:
- name: fluentd
image:
gcr.io/google_containers/fluentd-
elasticsearch:1.3
env:
- name: FLUENTD_ARGS
value: -qq
volumeMounts:
- name: varlog
mountPath: /varlog
- name: containers
mountPath:
/var/lib/docker/containers
volumes:
- hostPath:
path: /var/log
name: varlog
- hostPath:
path: /var/lib/docker/containers
name: containers

Démarrez ensuite votre pilote EFK :

$ kubectl create -f efk-logging.yaml

Une fois que tout a démarré, ouvrez une session Kibana avec
les crédentiels suivants :

Username: kibana
Password: changeme

Affichez l’onglet Discover, à l’adresse https :


//$IP/app/kibana#/discover?_g=() et commencez à
explorer les journaux à partir de là.
Pour nettoyer ou redémarrer la pile EFK, procédez ainsi :

$ kubectl delete deploy/es && \


kubectl delete deploy/kibana && \
kubectl delete svc/elasticsearch && \
kubectl delete svc/kibana && \
kubectl delete ingress/kibana-public && \
kubectl delete daemonset/fluentd

Discussion
Le transfert des journaux peut également être réalisé avec
l’outil Logstash, mais nous avons opté pour Fluentd dans la
solution parce qu’il s’agit d’un projet CNCF qui rencontre de
plus en plus de succès.

N’oubliez pas que Kibana a besoin d’un peu de temps pour


se mettre en route. Il vous faudra peut-être recharger
l’application Web plusieurs fois avant de voir la
configuration être prise en compte.

Voir aussi
• Article de Manoj Bhagwat « To Centralize Your Docker
Logs with Fluentd and ElasticSearch on Kubernetes »
(https://medium.com/@manoj.bhagwat60/to-
centralize-your-docker-logs-with-fluentd-and-
elasticsearch-on-kubernetes-42d2ac0e8b6c)
• Description EFK Stack pour AWS
(https://eksworkshop.com/logging/)

• elk-kubernetes (https://github.com/kayrus/elk-
kubernetes)

• FluentD (https://www.fluentd.org/)

• Kibana (https://www.elastic.co/products/kibana)
CHAPITRE 12
Maintenance et dépannage

Ce chapitre réunit plusieurs sections concernant la


maintenance au niveau applicatif et au niveau cluster. Nous
verrons comment déboguer un pod ou un conteneur, tester
la connectivité d’un service, comprendre le statut d’une
ressource et réaliser la maintenance d’un nœud. Nous
verrons enfin comment bien gérer le composant de stockage
du plan de contrôle etcd. Ce chapitre s’adresse autant aux
administrateurs de cluster qu’aux développeurs
d’applications.
Activer l’autocomplétion de
kubectl

Problème
Il devient vite fastidieux de devoir saisir en entier les noms
des commandes et des paramètres de l’outil kubectl. Un
mécanisme de complétion automatique serait apprécié.

Solution
Il suffit d’activer l’autocomplétion de kubectl.

Sous Linux, avec l’interpréteur shell Bash, vous activez


l’autocomplétion dans la session d’interpréteur en cours, au
moyen de la commande suivante :

$ source <(kubectl completion bash)

Pour les autres interpréteurs et autres systèmes


d’exploitation, voyez la documentation
(https://kubernetes.io/docs/tasks/tools/install-
kubectl/#enabling-shell-autocompletion).

Voir aussi
• Présentation de kubectl
(https://kubernetes.io/docs/user-guide/kubectl-
overview/)

• Guide rapide de kubectl


(https://kubernetes.io/docs/user-guide/kubectl-
cheatsheet/)
Retirer un pod d’un service

Problème
Vous avez correctement défini un service (voir le Chapitre 5)
qui s’appuie sur plusieurs pods, mais l’un d’entre eux
montre un comportement erratique. Vous voulez l’écarter de
la liste des points d’extrémité (endpoints) afin d’étudier ce
qui ne va pas plus tard.

Solution
Il y a lieu de changer le label du pod au moyen de l’option --
overwrite. Cela permet ensuite de modifier la valeur du
label run de ce pod. En écrasant le label, vous êtes certain
que le pod ne sera pas sélectionné par le sélecteur de
services (Voir la première section du Chapitre 5) et
disparaîtra donc de la liste des points d’extrémité. En
parallèle, le mécanisme de l’ensemble de réplicas qui
surveille ces pods va détecter qu’un des pods a disparu et va
d’office démarrer une nouvelle réplica.

Voyons cela par un exemple. Vous commencez par un


déploiement classique que vous générez avec kubectl run
(voir Chapitre 4) :
$ kubectl run nginx --image nginx --replicas 4

Si nous demandons la liste des pods avec le label run, nous


voyons apparaître quatre pods ayant la valeur nginx (en
effet, run=ngnx est le label généré d’office par la commande
kubectl run) :

$ kubectl get pods -Lrun


NAME READY STATUS RESTARTS
AGE RUN
nginx-d5dc44cf7-5g45r 1/1 Running 0 1h
nginx
nginx-d5dc44cf7-l429b 1/1 Running 0 1h
nginx
nginx-d5dc44cf7-pvrfh 1/1 Running 0 1h
nginx
nginx-d5dc44cf7-vm764 1/1 Running 0 1h
nginx

Vous pouvez exposer ce déploiement avec un service, puis


vérifier les points d’extrémité qui correspondent aux
adresses IP des différents pods :

$ kubectl expose deployments nginx --port 80

$ kubectl get endpoints


NAME ENDPOINTS
AGE
nginx
172.17.0.11:80,172.17.0.14:80,172.17.0.3:80 + 1
more... 1h

Vous retirez le premier pod du service en changeant son


label avec cette commande :

$ kubectl label pods nginx-d5dc44cf7-5g45r


run=notworking --overwrite

Vous pouvez connaître l’adresse IP d’un pod en affichant son


manifeste JSON en lançant une requête JQuery :

$ kubectl get pods nginx-d5dc44cf7-5g45r -o json | \ jq -


r .status.podIP172.17.0.3

Vous allez alors voir apparaître un nouveau pod portant le label


run=nginx. Le pod que vous avez retiré existe toujours mais n’est
plus présent dans la liste des points d’extrémité de service :

$ kubectl get pods -Lrun


NAME READY STATUS RESTARTS
AGE RUN
nginx-d5dc44cf7-5g45r 1/1 Running 0 21h
notworking
nginx-d5dc44cf7-hztlw 1/1 Running 0 21s
nginx
nginx-d5dc44cf7-l429b 1/1 Running 0 5m
nginx
nginx-d5dc44cf7-pvrfh 1/1 Running 0 5m
nginx
nginx-d5dc44cf7-vm764 1/1 Running 0 5m
nginx

$ kubectl describe endpoints nginx


Name: nginx
Namespace: default
Labels: run=nginx
Annotations: <none>
Subsets:
Addresses:
172.17.0.11,172.17.0.14,172.17.0.19,172.17.0.7
...
Accéder à un service ClusterIP
de l’extérieur du cluster

Problème
Un service interne vous cause des soucis et vous voulez
pouvoir tester son fonctionnement au niveau local sans
exposer le service de façon externe.

Solution
Utilisez un proxy local vers le serveur API au moyen de
kubectl proxy.

Supposons que vous avez créé un déploiement et un service


comme indiqué dans la section « Retirer un pod d’un
service ». Lorsque vous listez les services, vous voyez
apparaître un service nommé nginx :

$ kubectl get svc


NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
nginx ClusterIP 10.109.24.56 <none>
80/TCP 22h
Ce service n’est pas accessible de l’extérieur du cluster
Kubernetes. En revanche, vous pouvez exécuter un proxy
dans un autre terminal puis y accéder via localhost.

Vous commencez par démarrer le proxy dans une autre


fenêtre de terminal :

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

Pour indiquer le port sur lequel le proxy doit fonctionner, vous


utilisez l’option --port.

Vous pouvez ensuite accéder à l’application exposée par


votre service dans le terminal de départ en ouvrant votre
navigateur ou avec l’outil curl. Remarquez que le chemin
d’accès au service contient une partie /proxy. Sans cet
élément, vous obtenez l’objet JSON qui incarne le service :

$ curl
http://localhost:8001/api/v1/proxy/namespaces/de
fault/services/nginx/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Précisons que vous pouvez dorénavant accéder à la totalité de l’API
Kubernetes via localhost au moyen de curl.
Comprendre et analyser les
statuts des ressources

Problème
Vous voulez réagir en prenant connaissance du statut d’une
ressource, par exemple un pod, au moyen d’un script ou
dans un autre environnement d’automatisation comme un
pipeline CI/ CD.

Solution
Utilisez la commande kubectl get $KIND/$NAME -o json
puis analysez le résultat au format JSON au moyen d’une des
deux méthodes que nous présentons.

Si l’utilitaire de requête JSON nommé jq est installé, vous


pouvez vous en servir pour analyser le statut de la ressource.
Supposons un pod portant le nom jump ; nous voulons
connaître la classe de qualité de service (QoS) dans laquelle
se situe ce pod :

$ kubectl get po/jump -o json | jq --raw-output


.status.qosClass
BestEffort
Notez que le paramètre --raw-output de jq montre la
valeur brute. L’expression .status.qosClass désigne le
sous-champ désiré.

Vous pouvez faire une autre requête de statut à partir des


événements ou des transitions de statut :

$ kubectl get po/jump -o json | jq


.status.conditions
[
{
"lastProbeTime": null,
"lastTransitionTime": "2017-08-28T08:06:19Z",
"status": "True",
"type": "Initialized"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2017-08-31T08:21:29Z",
"status": "True",
"type": "Ready"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2017-08-28T08:06:19Z",
"status": "True",
"type": "PodScheduled"
}
]
Ces requêtes ne sont pas limitées aux pods ; vous pouvez les
appliquer à n’importe quelle autre ressource. Voici par
exemple comment interroger les révisions d’un
déploiement :

$ kubectl get deploy/prom -o json | jq


.metadata.annotations
{
"deployment.kubernetes.io/revision": "1"
}

Vous pouvez de même dresser la liste de tous les points


d’extrémité ep qui incarnent un service :

$ kubectl get ep/prom-svc -o json | jq


'.subsets'
[
{
"addresses": [
{
"ip": "172.17.0.4",
"nodeName": "minikube",
"targetRef": {
"kind": "Pod",
"name": "prom-2436944326-pr60g",
"namespace": "default",
"resourceVersion": "686093",
"uid": "eee59623-7f2f-11e7-b58a-
080027390640"
}
}
],
"ports": [
{
"port": 9090,
"protocol": "TCP"
}
]
}
]

Nous venons de voir comment procéder avec jq. Découvrons


une autre méthode qui ne réclame pas d’outils
complémentaires, puisque c’est une caractéristique interne
qui exploite les modèles du langage de programmation Go.

En effet, le langage Go définit des modèles (templates) dans le


paquetage portant le nom text/ template. Il est ainsi possible
de réaliser des transformations de texte ou de données, et
kubectl les supporte dès le départ. Voici par exemple
comment dresser la liste de toutes les images de conteneurs
actuellement utilisées dans l’espace de noms courant :

$ kubectl get pods -o go-template \


--template="{{range .items}}{{range
.spec.containers}}{{.image}} \
{{end}}{{end}}"
busybox prom/prometheus
Voir aussi
• Manuel de jq
(https://stedolan.github.io/jq/manual/)

• Bac à sable de jq pour tester des requêtes sans installer


(https://jqplay.org/)

• Modèles de paquetages dans la documentation de Go


(https://golang.org/pkg/text/template/)

• Article dans Medium « What are Quality of Service (QoS)


Classes in Kubernetes » (https://medium.com/google-
cloud/quality-of-service-class-qos-in-
kubernetes-bb76a89eb2c6)
Déboguer un pod

Problème
Vous faites face à un pod qui soit ne démarre pas comme
prévu, soit retombe au bout d’un certain temps.

Solution
Adoptez la technique basée sur une boucle OODA pour
découvrir et réparer le problème de façon systématique :

1. Observer. Que disent les journaux du conteneur ? Quels


événements sont survenus ? Quel est l’état de la
connectivité réseau ?

2. Orienter. Formulez une série d’hypothèses probables, en


gardant l’esprit ouvert, et ne tirez pas de conclusions
trop vite.

3. Décider. Choisissez une de vos hypothèses.

4. Agir. Testez l’hypothèse. Si elle est confirmée, vous avez


fini. Dans le cas contraire, revenez à l’Étape 1 et refaites
un cycle.

En guise d’exemple concret, voyons le cas d’un pod qui


tombe en panne. Commencez par créer un manifeste portant
le nom unhappy-pod.yaml avec ce contenu :

Listing 12.1 : unhappy-pod.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: unhappy
spec:
replicas: 1
template:
metadata:
labels:
app: nevermind
spec:
containers:
- name: shell
image: busybox
command:
- "sh"
- "-c"
- "echo I will just print something here
and then exit"

Si vous lancez le déploiement, puis regardez les produits qui


vont être créés, vous confirmez que cela ne se passe pas
correctement :
$ kubectl create -f unhappy-pod.yaml
deployment "unhappy" created

$ kubectl get po
NAME READY STATUS
RESTARTS AGE
unhappy-3626010456-4j251 0/1
CrashLoopBackOff 1 7s

$ kubectl describe po/unhappy-3626010456-4j251


Name: unhappy-3626010456-4j251
Namespace: default
Node: minikube/192.168.99.100
Start Time: Sat, 12 Aug 2017 17:02:37 +0100
Labels: app=nevermind
pod-template-hash=3626010456
Annotations: kubernetes.io/created-by=
{"kind":"SerializedReference","apiVersion":
"v1","reference":
{"kind":"ReplicaSet","namespace":"default","name
":
"unhappy-3626010456","uid":
"a9368a97-7f77-11e7-b58a-080027390640"...
Status: Running
IP: 172.17.0.13
Created By: ReplicaSet/unhappy-3626010456
Controlled By: ReplicaSet/unhappy-3626010456
...
Conditions:
Type Status
Initialized True
Ready False
PodScheduled True
Volumes:
default-token-rlm2s:
Type: Secret (a volume populated by a
Secret)
SecretName: default-token-rlm2s
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: <none>
Events:
FirstSeen ... Reason Message
--------- ... ------ -------
25s ... Scheduled
Successfully assigned
unhappy-
3626010456-4j251 to minikube
25s ... SuccessfulMountVolume
MountVolume.SetUp succeeded for
volume
"default-token-rlm2s"
24s ... Pulling pulling
image "busybox"
22s ... Pulled
Successfully pulled image "busybox"
22s ... Created Created
container
22s ... Started Started
container
19s ... BackOff Back-off
restarting failed container
19s ... FailedSync Error
syncing pod

Kubernetes considère que ce pod n’est pas prêt à servir du


trafic, car il a fait l’objet d’une erreur de synchronisation
« error syncing pod ».

Vous pouvez détecter cette situation dans le tableau de bord


Kubernetes en visualisant le déploiement (Figure 12.1). Vous
pouvez aussi visualiser l’ensemble de réplicas que vous
supervisez ainsi que le pod (Figure 12.2).

Figure 12.1 : Aperçu d’un déploiement en statut d’erreur.


Figure 12.2 : Aperçu d’un pod en statut d’erreur.

Discussion
Qu’il s’agisse d’un pod qui tombe en panne ou d’un
comportement erratique, les causes peuvent être
nombreuses. Voici quelques points à vérifier avant de
suspecter un bogue logiciel :

• Est-ce que le manifeste est correct ? Vérifiez en vous


aidant du schéma JSON de Kubernetes.

• Est-ce que le conteneur peut s’exécuter en autonomie


locale (autrement dit, en dehors de Kubernetes) ?

• Est-ce que Kubernetes peut accéder au registre du


conteneur et réellement en récupérer l’image ?
• Est-ce que les nœuds peuvent dialoguer entre eux ?

• Est-ce que les nœuds peuvent accéder au maître ?

• Est-ce que le service DNS est disponible dans le


cluster ?

• Est-ce que les nœuds disposent d’assez de ressources ?

• Avez-vous limité l’usage de ressources du conteneur ?

Voir aussi
• Documentation de dépannage des applications
Kubernetes
(https://kubernetes.io/docs/tasks/debug-
application-cluster/debug-application/)

• Introspection et débogage des applications


(https://kubernetes.io/docs/tasks/debug-
application-cluster/debug-application-
introspection/)

• Débogage des pods et des contrôleurs de réplication


(https://kubernetes.io/docs/tasks/debug-
application-cluster/debug-pod-replication-
controller/)

• Débogage des services


(https://kubernetes.io/docs/tasks/debug-
application-cluster/debug-service/)

• Dépannage des clusters


(https://kubernetes.io/docs/tasks/debug-
application-cluster/debug-cluster/)
Obtenir une vue détaillée du
statut d’un cluster

Problème
Vous avez besoin d’obtenir un instantané détaillé du statut
global d’un cluster pour prendre une décision, faire de
l’audit ou dépanner.

Solution
Utilisez la commande kubectl cluster-info. Voici par
exemple comment créer un vidage du statut de cluster dans
le sous-répertoire cluster-state-2019-10-13 :

$ kubectl cluster-info dump --all-namespaces \


--output-directory=$PWD/cluster-state-2017-08-
13
$ tree ./cluster-state-2019-10-13

.
├── default
│ ├── cockroachdb-0
│ │ └── logs.txt
│ ├── cockroachdb-1
│ │ └── logs.txt
│ ├── cockroachdb-2
│ │ └── logs.txt
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── jump-1247516000-sz87w
│ │ └── logs.txt
│ ├── nginx-4217019353-462mb
│ │ └── logs.txt
│ ├── nginx-4217019353-z3g8d
│ │ └── logs.txt
│ ├── pods.json
│ ├── prom-2436944326-pr60g
│ │ └── logs.txt
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kube-public
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kube-system
│ ├── daemonsets.json
│ ├── default-http-backend-wdfwc
│ │ └── logs.txt
│ ├── deployments.json
│ ├── events.json
│ ├── kube-addon-manager-minikube
│ │ └── logs.txt
│ ├── kube-dns-910330662-dvr9f
│ │ └── logs.txt
│ ├── kubernetes-dashboard-5pqmk
│ │ └── logs.txt
│ ├── nginx-ingress-controller-d2f2z
│ │ └── logs.txt
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
└── nodes.json
Ajouter des nœuds de travail
Kubernetes

Problème
Vous voulez ajouter un nœud de travail (worker node) à votre
cluster Kubernetes.

Solution
Commencez par préparer ou approvisionner une nouvelle
machine en fonction des besoins de votre environnement.
Dans un contexte de machines nues Bare Metal, vous
installez physiquement le nouveau serveur dans une baie.
Dans un environnement de cloud public, vous créez une
nouvelle machine virtuelle. Une fois la préparation réalisée,
vous installez les trois composants qui constituent un nœud
de travail Kubernetes :

kubelet

• C’est le gestionnaire de nœuds et le superviseur de tous


les pods, qu’ils soient contrôlés par API Server ou qu’ils
fonctionnent localement (comme c’est le cas des pods
statiques). Sachez que Kubler est l’arbitre en dernier
ressort de ce que peuvent ou ne peuvent pas faire les
pods sur un nœud. Il prend en charge les tâches
suivantes :

• Il rend compte du statut des nœuds et des pods au


serveur API.

• Il exécute périodiquement les sondes de vitalité


(liveness).

• Il assure le montage des volumes des pods et le


téléchargement des secrets.

• Il contrôle le module d’exécution du conteneur (voir la


suite).

Container runtime

• C’est le composant qui télécharge les images de


conteneurs, et en lance l’exécution. Dans les premières
versions, il était intégré directement au moteur Docker.
De nos jours, c’est un système en extension enfichable
qui se fonde sur l’interface d’exécution CRI (Container
Runtime Interface), ce qui permet notamment d’utiliser
CRI-O à la place de Docker.

kube-proxy

• Ce processus se charge de configurer de façon


dynamique les règles iptables concernant le nœud pour
permettre l’abstraction du service Kubernetes (pour
rediriger l’adresse virtuelle VIP vers les points
d’extrémité qui correspondent à plusieurs pods
incarnant le service).

La procédure d’installation de ces composants dépend


fortement de l’environnement et de la méthode choisie (par
le cloud, par kubeadm, etc.). Pour connaître les options
disponibles, consultez la référence de kubelet et celle de
kube-proxy.

Discussion
Contrairement à d’autres ressources Kubernetes, et
notamment les déploiements et services, les nœuds de
travail ne sont pas créés directement par le plan de contrôle
Kubernetes, car ce dernier se contente de les administrer.
Autrement dit, lorsque Kubernetes crée un nœud, il ne crée
en fait qu’un objet qui va représenter le travail. Il valide ce
nœud par un test de santé en se fondant sur le champ
metadata.name du nœud. Si le nœud est considéré comme
valable, c’est-à-dire que tous ses composants s’exécutent
bien, il peut être considéré comme faisant partie du cluster.
Dans le cas contraire, le nœud est ignoré en termes
d’activité du cluster, et ce jusqu’à ce qu’il devienne valide.

Voir aussi
• Description des nœuds Kubernetes dans le document
d’architecture
(https://github.com/kubernetes/community/blob/m
aster/contributors/design-
proposals/architecture/ architecture.md#the-
kubernetes-node)

• Communications entre maître et nœuds


(https://kubernetes.io/docs/concepts/architectu
re/master-node-communication/)

• Pods statiques
(https://kubernetes.io/docs/tasks/administer-
cluster/static-pod/)
Retirer un nœud pour
maintenance

Problème
Vous avez besoin de faire la maintenance d’un nœud, par
exemple pour lui appliquer un correctif de sécurité ou pour
mettre à jour le système.

Solution
Vous utilisez la commande kubectl drain. Voici par
exemple comment basculer en mode maintenance le nœud
nommé 123-worker :

$ kubectl drain 123-worker

Lorsque vous êtes prêt à remettre le nœud en service, vous


utilisez la commande suivante qui va permettre de rendre le
nœud à nouveau planifiable :

$ kubectl uncordon 123-worker

Discussion
Dans tous les cas, la commande kubectl drain commence
par marquer le nœud mentionné comme devenant non
planifiable, afin d’empêcher l’arrivée de nouveaux pods (ce
qui équivaut à kubectl cordon). Dans un deuxième temps,
la commande procède à une éviction des pods si le serveur
API supporte cette opération. Dans le cas contraire, la
commande applique une opération de suppression kubectl
delete pour supprimer les pods. La documentation de
Kubernetes décrit précisément la séquence d’opérations
(nous la reproduisions dans la Figure 12.3).
Figure 12.3 : Scénario de mise à l’écart d’un nœud de travail.

La commande kubectl drain procède donc soit à


l’éviction, soit à la suppression de tous les pods sauf les
pods miroirs qui ne peuvent pas être supprimés via le
serveur API. En ce qui concerne les pods supervisés par un
composant DaemonSet, la commande drain ne les traite que
si elle détecte l’option --ignore-daemonsets. Dans tous les
cas, elle ne supprime jamais un pod géré par DaemonSet,
puisque ce genre de pod serait immédiatement remplacé par
le contrôleur DaemonSet, ce dernier ne tenant pas compte du
marquage comme implanifiable.

L’opération patiente jusqu’à ce que les processus se soient terminés


proprement. Vous ne devez donc pas travailler sur le nœud tant que
la commande drain n’a pas terminé. Notez que vous pouvez
procéder à l’éviction d’un pod qui n’est pas géré par un RC, un RS,
un job, un DaemonSet ou un StatefulSet avec la commande
suivante :

kubectl drain $NODE --force

Voir aussi
• Purge de nœud avec respect des SLO applicatifs
(https://kubernetes.io/docs/tasks/administer-
cluster/safely-drain-node/)

• Documentation de référence de kubectl


(https://kubernetes.io/docs/reference/gene-
rated/kubectl/kubectl-commands#drain)

• Éviction des pods


(https://kubernetes.io/docs/concepts/workloads/
pods/disruptions/)
Gérer le composant etcd

Problème
Vous avez besoin d’accéder à la base etcd pour effectuer
une sauvegarde ou vérifier directement le statut du cluster.

Solution
Pour accéder à etcd et lancer une requête, vous pouvez
utiliser curl ou etcdctl. Voici par exemple comment
procéder dans le contexte de Minikube, si l’outil jq est
installé :

$ minikube ssh

$ curl 127.0.0.1:2379/v2/keys/registry | jq .
{
"action": "get",
"node": {
"key": "/registry",
"dir": true,
"nodes": [
{
"key":
"/registry/persistentvolumeclaims",
"dir": true,
"modifiedIndex": 241330,
"createdIndex": 241330
},
{
"key": "/registry/apiextensions.k8s.io",
"dir": true,
"modifiedIndex": 641,
"createdIndex": 641
},
...

Vous pouvez utiliser cette technique dans tous les


environnements dans lesquels etcd est exploité avec l’API
v2.

Discussion
Dans Kubernetes, etcd est un des composants du plan de
contrôle. Rappelons que le serveur API (voir le Chapitre 6)
est sans conservation d’état (stateless) et constitue le seul
composant de Kubernetes qui communique directement avec
etcd, qui est le composant de stockage distribué qui gère le
statut du cluster. En fait, etcd est une base contenant des
paires clé/valeur. Dans la version etcd2, les clés étaient
présentées sous forme hiérarchique, mais cette approche a
été remplacée dans etcd3 par un modèle à plat, tout en
maintenant la rétrocompatibilité avec les clés hiérarchisées.
Jusqu’à la version 1.5.2 de Kubernetes, on utilisait etcd2, puis le
basculement s’est fait vers etcd3. Dans Kubernetes 1.5.x, etcd3
reste utilisé dans le mode API v2. L’évolution vise l’API v3 etcd en
remplacement de la v2. Du point de vue du développeur, cela n’a pas
d’impact, car le serveur API se charge d’abstraire les interactions. En
tant qu’administrateur en revanche, vous ferez attention à la version
de etcd que vous utilisez et dans quel mode API.

Il est en général de la responsabilité de l’admin du cluster de


gérer etcd, c’est-à-dire de le mettre à jour et d’en assurer
les sauvegardes. Dans les environnements dans lesquels la
gestion du plan de contrôle a été déléguée, par exemple dans
Google Kubernetes Engine, il n’y a plus de possibilité
d’accéder directement à etcd. Cette approche est prévue dès
le départ, et il n’y a pas de moyen de la contourner.

Voir aussi
• Guide d’administration de cluster etcd
v2 (https://coreos.com/etcd/docs/latest/v2/admi
n_guide.html)

• Guide de récupération sur avarie d’etcd


v3 (https://coreos.com/etcd/docs/latest/op-
guide/recovery.html)

• Exploitation de clusters etcd pour Kubernetes


(https://kubernetes.io/docs/tasks/admi-nister-
cluster/configure-upgrade-etcd/)
• Documentation de Minikube sur l’accès aux ressources
Localkube depuis un pod
(https://github.com/kubernetes/minikube/blob/ma
ster/docs/accessing_etcd.md)

• Article de Stefan Schimanski et Michael Hausenblas,


« Kubernetes Deep API Server – Part 2 »
(https://blog.openshift.com/kubernetes-deep-
dive-api-server-part-2/)

• Article de Michael Hausenblas « Notes on Moving from


etcd2 to etcd3 » (https://hackernoon.com/notes-on-
moving-from-etcd2-to-etcd3-dedb26057b90)
CHAPITRE 13
Développer pour Kubernetes

Nous savons maintenant installer, gérer et exploiter


Kubernetes pour assurer le déploiement et l’administration
des applications. Voyons comment adapter Kubernetes à vos
besoins et comment corriger d’éventuels bogues. Il devient
nécessaire d’installer l’environnement de développement du
langage Go et de pouvoir accéder au code source de
Kubernetes (disponible sur GitHub). Nous verrons comment
compiler Kubernetes de façon globale, mais aussi comment
compiler un composant en particulier, par exemple le client
kubectl. Nous verrons comment programmer en langage
Python pour dialoguer avec le serveur API de Kubernetes et
comment enrichir Kubernetes en créant une définition de
ressources personnalisée (CRD).
Compiler les sources

Problème
Vous voulez constituer votre propre paquetage de binaire
Kubernetes à partir du code source, au lieu de télécharger les
binaires officiels d’une release ou des recompilations
d’autres sources (voir Chapitre 2).

Solution
Vous dupliquez par clonage le référentiel GitHub de
Kubernetes puis vous recompilez depuis les sources.

Si vous êtes sur un hôte Docker, vous pouvez spécifier la


cible quick-release du fichier de construction makefile
principal root, comme ceci :

$ git clone
https://github.com/kubernetes/kubernetes
$ cd kubernetes
$ make quick-release

Pour cette construction sur base Docker, vous devez disposer d’au
moins 4 Go de mémoire RAM. Vérifiez que le démon Docker a accès
au minimum à cet espace. Sous macOS, vous ouvrez les préférences
de Docker for Mac pour augmenter l’espace mémoire alloué.
Les binaires vont être disponibles dans le répertoire
_output/release-stage et un paquetage complet vous attendra
dans le répertoire _output/release-tars.

Si vous disposez d’un environnement Golang correctement


configuré, vous pouvez utiliser la cible par défaut release
dans le même fichier makefile principal :

$ git clone
https://github.com/kubernetes/Kubernetes
$ cd kubernetes
$ make

Dans ce cas, les binaires seront stockés dans le répertoire


_output/bin.

Voir aussi
• Guides de développement détaillé de Kubernetes
(https://github.com/kubernetes/community/tree/m
aster/contributors/devel)

• Langage Go (http://golang.org)

• Sources de Kubernetes
(https://github.com/kubernetes/kubernetes)
Compiler un seul composant

Problème
Vous voulez reconstruire un composant de Kubernetes à
partir des fichiers source, mais pas l’ensemble du projet. Par
exemple, vous ne voulez recompiler que le client kubectl.

Solution
Au lieu de choisir comme cible, make quick-release ou de
spécifier make comme dans la section précédente, vous
spécifiez par exemple, make kubectl.

Le fichier makefile racine prévoit des cibles pour chacun des


composants principaux. Voici par exemple comment
compiler les trois composants kubectl, kubeadm et
hyperkube :

$ make kubectl
$ make kubeadm
$ make hyperkube

Les binaires seront stockés dans le répertoire _output/bin.


Interagir avec l’APIKubernetes
depuis un client Python

Problème
Voulez utiliser le langage Python pour créer des scripts
exploitant l’API Kubernetes.

Solution
Vous installez le module de Python nommé kubernetes. Il
est en cours de développement dans l’incubateur
Kubernetes. Vous pouvez l’installer depuis les sources ou
depuis le site d’index des paquetages Python PyPi :

$ pip install kubernetes

Vous êtes ainsi prêt à utiliser ce module Python pour


dialoguer avec l’API Kubernetes, à condition qu’il existe un
cluster Kubernetes accessible dans le contexte kubectl par
défaut. Le script de l’exemple suivant permet d’obtenir la
liste de tous les pods en affichant leur nom :

Listing 13.1 : podlister.py


#!/usr/bin/env python2

from kubernetes import client, config

config.load_kube_config()
v1 = client.CoreV1Api()
res = v1.list_pod_for_all_namespaces(watch=False)

for pod in res.items:


print(pod.metadata.name)

L’appel à config.load_kube_config() provoque le


chargement de vos crédentiels et points d’extrémité
Kubernetes depuis le fichier de configuration de kubectl.
Par défaut, les éléments utilisés sont ceux de votre contexte
actuel.

Discussion
Le client Python a été créé en conformité avec la
spécification OpenAPI de l’API Kubernetes. Il est toujours à
jour et généré automatiquement. Il permet d’accéder à tous
les éléments API depuis ce client.

Chacun des groupes API correspond à une classe spécifique.


Ainsi, pour appeler une méthode d’un objet API faisant
partie du groupe /api/v1, vous devez créer une instance de la
classe nommée CoreV1Api. Pour pouvoir utiliser des
déploiements, il faut créer une instance de la classe
extensionsV1beta1Api. Vous trouverez toutes les méthodes
et toutes les instances de groupes API correspondantes dans
le fichier README autogénéré.

Voir aussi
• Incubateur Kubernetes
(https://github.com/kubernetes-
incubator/client-python)

• Site Python PyPi (https://pypi.python.org/pypi)

• Exemples dans le référentiel du projet


(https://github.com/kubernetes-
incubator/client-python/tree/master/examples)
Enrichir l’API par des définitions
de ressources personnalisées
(CRD)

Problème
Vous avez à gérer une charge de travail spécifique, mais
aucune des ressources existantes, Deployment, Job ou
StatefulSet, ne convient vraiment. Vous aimeriez donc
pouvoir enrichir l’API Kubernetes en définissant une
nouvelle ressource pour incarner votre charge de travail tout
en utilisant kubectl de façon normale.

Solution
Vous créez une définition de ressource CRD (Custome
Resource Definition), comme nous allons voir.

Supposons que vous vouliez définir une ressource


personnalisée du type Function qui va représenter une
ressource de type Job à courte durée, un peu comme les
AWS Lambda. Cela correspond à une fonction en tant que
service FaaS (Function as-a Service), parfois qualifiée
improprement « sans serveur », serverless.
Pour une solution à FaaS de niveau production dans Kubernetes,
voyez la section « Déployer des fonctions avec kubeless » du
prochain chapitre.

Nous commençons par définir la ressource CRD dans un


fichier manifeste que nous appelons functions-crd.yaml :

Listing 13.2 : functions-crd.yaml

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: functions.example.com
spec:
group: example.com
version: v1
scope: Namespaced
names:
kind: Function
plural: functions

Il faut ensuite soumettre cette nouvelle définition CRD au


serveur API, en prenant soin de patienter quelques minutes,
le temps que la définition soit enregistrée :

$ kubectl create -f functions-crd.yaml


customresourcedefinition "functions.example.com"
created
Votre ressource personnalisée de type Function est
maintenant définie et le serveur API en a connaissance. Vous
pouvez donc l’instancier au moyen d’un manifeste que vous
appelez par exemple myfaas.yaml :

Listing 13.3 : myfaas.yaml

apiVersion: example.com/v1
kind: Function
metadata:
name: myfaas
spec:
code: "http://src.example.com/myfaas.js"
ram: 100Mi

Vous pouvez alors créer votre ressource myfaas de la façon


habituelle :

$ kubectl create -f myfaas.yaml


function "myfaas" created

$ kubectl get crd functions.example.com -o yaml


apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: 2017-08-13T10:11:50Z
name: functions.example.com
resourceVersion: "458065"
selfLink:
/apis/apiextensions.k8s.io/v1beta1/customresourc
edefinitions
/functions.example.com
uid: 278016fe-81a2-11e7-b58a-080027390640
spec:
group: example.com
names:
kind: Function
listKind: FunctionList
plural: functions
singular: function
scope: Namespaced
version: v1
status:
acceptedNames:
kind: Function
listKind: FunctionList
plural: functions
singular: function
conditions:
- lastTransitionTime: null
message: no conflicts found
reason: NoConflicts
status: "True"
type: NamesAccepted
- lastTransitionTime: 2017-08-13T10:11:50Z
message: the initial names have been accepted
reason: InitialNamesAccepted
status: "True"
type: Established
$ kubectl describe functions.example.com/myfaas
Name: myfaas
Namespace: default
Labels: <none>
Annotations: <none>
API Version: example.com/v1
Kind: Function
Metadata:
Cluster Name:
Creation Timestamp: 2017-08-
13T10:12:07Z
Deletion Grace Period Seconds: <nil>
Deletion Timestamp: <nil>
Resource Version: 458086
Self Link:
/apis/example.com/v1/namespaces/default
/functions/myfaas
UID: 316f3e99-81a2-11e7-
b58a-080027390640
Spec:
Code: http://src.example.com/myfaas.js
Ram: 100Mi
Events: <none>

Pour découvrir les définitions CRD, il suffit d’accéder au


serveur API. Vous pouvez par exemple utiliser kubectl
proxy pour accéder localement au serveur et requérir
l’espace de clés, qui est dans notre exemple example.com/v1 :
$ curl 127.0.0.1:8001/apis/example.com/v1/ | jq
.
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "example.com/v1",
"resources": [
{
"name": "functions",
"singularName": "function",
"namespaced": true,
"kind": "Function",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch"
]
}
]
}

Vous constatez la présence de votre ressource avec les verbes


autorisés.
Lorsque vous n’avez plus besoin de cette instance de
ressources, vous pouvez la supprimer :

$ kubectl delete functions.example.com/myfaas


function "myfaas" deleted

Discussion
Nous venons de voir que la création d’une définition CRD est
facile. Du point de vue de l’utilisateur final, les CRD
proposent une API cohérente et ne se distinguent quasiment
pas des ressources natives que sont les pods et jobs. Toutes
les commandes habituelles fonctionnent comme prévu, par
exemple, kubectl get ou kubectl delete.

Cela dit, la création d’une CRD n’est qu’une petite partie du


travail à prévoir pour vraiment enrichir l’API Kubernetes.
Une définition CRD ne permet que de stocker et de récupérer
des données spécifiques via le serveur API dans etcd. Il reste
à écrire un contrôleur spécifique capable d’interpréter ces
données qui expriment les intentions de l’utilisateur ; ce
n’est qu’ainsi que vous pouvez mettre en place une boucle
de contrôle qui va comparer l’état actuel avec l’état prévu, et
tenter de réconcilier les deux.

Jusqu’à la version 1.7, les définitions CRD étaient appelées des


ressources de tierce partie ou TPR (Third Party Resource). Si vous
utilisez une TPR, vous êtes fortement invité à migrer vers une CRD.
Les principales contraintes des CRD sont celles qui peuvent
vous pousser à adopter un serveur API utilisateur dans
certains cas. Les voici :

• Vous ne pouvez utiliser qu’une seule version par CRD,


même s’il est possible de disposer de plusieurs versions
par groupe API. En conséquence, vous ne pouvez pas
effectuer une conversion entre des représentations
différentes de vos CRD.

• Dans les versions jusqu’à la 1.7, les CRD n’acceptent pas


l’affectation de valeurs par défaut.

• La validation des champs définis dans les spécifications


CRD n’est possible qu’à partir de la version 1.8.

• Il est impossible de définir des sous-ressources, par


exemple des ressources status.

Voir aussi
• Extension de l’API Kubernetes avec les CRD
(https://kubernetes.io/docs/tasks/access-
kubernetes-api/extend-api-custom-resource-
definitions/)

• Article de Stefan Schimanski et Michael Hausenblas


« Kubernetes Deep Dive: API Server – Part 3a »
(https://blog.openshift.com/kubernetes-deep-
dive-api-server-part-3a/)

• Article d’Aaron Levy « Writing a Custom Controller:


Extending the Functionality of Your Cluster »,
KubeCon 2017 (https://www.youtube.com/watch?
v=_BuqPMlXfpE)

• Article de Tu Nguyen « A Deep Dive into Kubernetes


Controllers »
(https://engineering.bitnami.com/articles/a-
deep-dive-into-kubernetes-controllers.html)

• Article de Yaron Haviv « Extend Kubernetes 1.7 with


Custom Resources » (https://thenewstack.io/extend-
kubernetes-1-7-custom-resources/)
CHAPITRE 14
L’écosystème Kubernetes

Dans ce dernier chapitre, nous allons prendre du recul et


considérer l’écosystème global de Kubernetes, c’est-à-dire
les logiciels dans l’incubateur et les projets associés, comme
Helm, kompose, kubicorn et kubeless.
Installer le gestionnaire de
paquetages Helm

Problème
Vous voulez éviter de devoir écrire à la main tous les
manifestes Kubernetes. Vous aimeriez pouvoir faire chercher
un paquetage dans un référentiel pour télécharger et
installer le contenu via l’interface sur ligne de commande.

Solution
Adoptez l’outil Helm, qui est le gestionnaire de paquetages
de Kubernetes. Il définit un paquetage comme un ensemble
de manifestes accompagné de quelques métadonnées. Les
manifestes sont en fait des modèles : les valeurs réelles n’y
sont injectées que lors de la création d’une instance du
paquetage par Helm. Un paquetage traité par Helm porte le
nom de charte.

La partie incarnant l’interface cliente de Helm porte le nom


helm. La partie serveur porte le nom tiller. Vous
interagissez avec vos chartes au moyen du client, alors que
le serveur tiller est exécuté dans votre cluster Kubernetes
comme un déploiement Kubernetes normal.
Vous pouvez construire l’outil à partir des sources ou le
télécharger depuis la page de release GitHub. Dans ce cas,
vous procédez à l’extraction d’archive puis vous déplacez
tous les binaires de helm dans votre chemin d’accès $PATH.
Voici par exemple comment procéder sous macOS pour la
version 2.7.2 :

$ wget
https://storage.googleapis.com/kubernetes-helm/
\
helm-v2.7.2-darwin-amd64.tar.gz

$ tar -xvf helm-v2.7.2-darwin-amd64.tar.gz

$ sudo mv darwin-amd64/64 /usr/local/bin

$ helm version

Une fois que la commande helm est accessible par $PATH,


vous vous en servez pour démarrer la partie serveur tiller
sur votre cluster Kubernetes. Notre exemple utilise
Minikube :

$ kubectl get nodes


NAME STATUS AGE VERSION
minikube Ready 4m v1.7.8

$ helm init
$HELM_HOME has been configured at
/Users/sebgoa/.helm.

Tiller (the helm server side component) has been


installed into your Kubernetes Cluster. Happy
Helming!
$ kubectl get pods --all-namespaces | grep
tiller
kube-system tiller-deploy-1491950541-4kqxx 0/1
ContainerCreating 0 1s

Vous pouvez maintenant installer l’un des plus


de 100 paquetages disponibles.
Utiliser Helm pour installer des
applications

Problème
Vous avez installé l’outil helm (voir section précédente).
Vous voulez maintenant chercher des chartes et les déployer.

Solution
Helm est livré en standard avec plusieurs référentiels de
chartes préconfigurées et maintenues par la communauté.
Vous pouvez en apprendre plus sur GitHub. Il y en a plus
de 100 !

Supposons que vous ayez besoin de déployer Redis. Il suffit


de chercher le mot redis dans les référentiels de Helm puis
de l’installer. Helm va utiliser la charte correspondante pour
créer une instance qui s’appelle une release.

Nous vérifions d’abord que le serveur tiller fonctionne et


que les référentiels par défaut sont bien configurés :

$ kubectl get pods --all-namespaces | grep


tiller
kube-system tiller-deploy-1491950541-4kqxx
1/1 Running 0 3m
$ helm repo list
NAME URL
stable
http://storage.googleapis.com/kubernetes-charts

Nous pouvons maintenant lancer la recherche du paquetage


Redis :

$ helm search redis


NAME VERSION DESCRIPTION
stable/redis 0.5.1 Open source,
advanced key-value store. It ...
testing/redis-cluster 0.0.5 Highly available
Redis cluster with...
testing/redis-standalone 0.0.1 Standalone Redis
Master
stable/sensu 0.1.2 Sensu monitoring
framework backed by ...
testing/example-todo 0.0.6 Example Todo
application backed by Redis

Pour créer une release, nous utilisons la commande helm


install :

$ helm install stable/redis

Helm se charge de créer tous les objets Kubernetes qui sont


définis dans la charte, par exemple un secret (voir la section
« Passage d’une clé d’accès API à un pod via un secret » du
Chapitre 8), une réclamation de volume PVC (voir section
« Maîtriser la persistance de données sur Minikube » du
Chapitre 8), un service (voir section « Créer un service pour
y exposer votre application » du Chapitre 5) et/ou un
déploiement. Tous ces objets constituent ensemble une
release Helm que vous pouvez administrer comme un
ensemble unique.

Le résultat est que vous allez disposer d’un pod d’exécution


redis :

$ helm ls
NAME REVISION UPDATED
STATUS CHART ...
broken-badger 1 Fri May 12 11:50:43 2017
DEPLOYED redis-0.5.1 ...

$ kubectl get pods


NAME READY
STATUS RESTARTS AGE
broken-badger-redis-4040604371-tcn14 1/1
Running 0 3m

Pour d’autres détails au sujet des chartes et apprendre


comment en créer une, voyez la section suivante.
Créer votre charte pour
empaqueter une application
avec Helm

Problème
Vous avez créé une application qui utilise plusieurs
manifestes Kubernetes. Vous aimeriez la transformer en un
paquetage sous forme d’une charte Helm.

Solution
Servez-vous des commandes helm create et helm
package.

La commande create sert à générer le squelette de charte.


Lorsque vous spécifiez la commande dans le terminal, vous
indiquez le nom de la charte. Voici par exemple comment
créer la charte nommée oreilly :

$ helm create oreilly


Creating oreilly

$ tree oreilly/
oreilly/
├── Chart.yaml
├── charts
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yaml
2 directories, 7 files

Si tous vos fichiers manifestes sont prêts, vous pouvez


directement les copier dans le sous-répertoire /templates,
puis supprimer les éléments d’attente créés par la charte.
Pour transformer vos manifestes en modèles, il faut écrire
les valeurs qui seront injectées dans les manifestes dans un
fichier nommé values.yaml. Vous devez modifier le fichier de
métadonnées Chart.yaml. Si vous utilisez des chartes
dépendantes, vous les placez dans le répertoire /charts.

Vous pouvez ensuite tester localement votre charte ainsi :

$ helm install ./oreilly

Pour produire le paquetage, vous utilisez helm package


oreilly/. Vous obtiendrez un fichier compressé tarball de la
charte qui sera copié dans un référentiel de charte local.
L’outil génère ensuite un nouveau fichier index.yaml pour ce
référentiel local. Si vous regardez le contenu du répertoire
⁓/.helm, vous devriez voir quelque chose dans le style
suivant :

$ ls -l ⁓/.helm/repository/local/
total 16
-rw-r--r-- 1 sebgoa staff 379 Dec 16 21:25
index.yaml
-rw-r--r-- 1 sebgoa staff 1321 Dec 16 21:25
oreilly-0.1.0.tgz

Si vous lancez une recherche par helm search, vous devriez


dorénavant voir votre charte locale :

$ helm search oreilly


NAME VERSION DESCRIPTION
local/oreilly 0.1.0 A Helm chart for
Kubernetes

Voir aussi
• Création d’une première charte Helm dans la
documentation Bitnami de Kubernetes
(https://docs.bitnami.com/kubernetes/how-
to/create-your-first-helm-chart/)

• Guide des meilleures pratiques de charte dans la


documentation de Helm
(https://docs.helm.sh/chart_best_practices/)
Convertir un fichier compose
Docker en manifeste
Kubernetes

Problème
Vous utilisez déjà des conteneurs avec Docker et vous avez
créé quelques fichiers compose Docker pour définir une
application multiconteneur. Vous voulez maintenant
basculer vers Kubernetes, mais vous vous demandez s’il est
possible de réutiliser vos fichiers compose Docker et
comment.

Solution
Adoptez l’outil kompose qui sert à convertir des fichiers
compose Docker en manifestes Kubernetes.

Pour commencer, téléchargez l’outil kompose depuis la page


de publication GitHub et déplacez-le dans un chemin
d’accès $PATH pour plus de confort.

Voici par exemple comment procéder sous macOS :

$ wget https://github.com/kubernetes-
incubator/kompose/releases/download/ \
v1.6.0/kompose-darwin-amd64

$ sudo mv kompose-darwin-amd64
/usr/local/bin/kompose

$ sudo chmod +x /usr/local/bin/kompose

$ kompose version
1.6.0 (ae4ef9e)

Partons du fichier compose Docker suivant qui sert à


démarrer un conteneur Redis :

version: '2'
services:
redis:
image: redis
ports:
- "6379:6379"

Voici comment convertir automatiquement ce fichier en


manifestes Kubernetes :

$ kompose convert --stdout

Les manifestes vont apparaître sur la sortie standard stdout


et vous verrez apparaître un service Kubernetes et un
déploiement. Pour créer automatiquement ces objets, vous
pouvez vous servir de la commande up compatible avec
compose de Docker :

$ kompose up

Certaines des directives compose ne peuvent pas être converties vers


Kubernetes. Dans ce cas, l’outil kompose affiche un avertissement
pour vous prévenir que la conversion n’a pas pu être réalisée.

En général, cela ne pose pas problème, mais il reste possible que la


conversion ne produise pas un manifeste exploitable dans
Kubernetes. Ce genre de transformation ne peut en effet pas être
parfaite. L’outil réalise une grande partie du travail menant à un
manifeste Kubernetes efficace, mais vous devrez notamment vous
charger manuellement de la gestion des volumes et de l’isolation
réseau.

Discussion
Les trois principales commandes de l’outil kompose sont
convert, up et down. Pour plus de détails, utilisez l’option -
-help.

Par défaut, l’outil kompose convertit les services Docker en


un déploiement Kubernetes avec les services associés. Vous
pouvez demander d’utiliser un objet DeamonSet (voir
Chapitre 7) ou des objets spécifiques OpenShift, par exemple
un objet DeploymentConfiguration.
Créer un cluster Kubernetes
avec kubicorn

Problème
Vous avez besoin de créer un cluster Kubernetes sur AWS.

Solution
Pour créer et gérer des clusters Kubernetes sur AWS, vous
utilisez l’outil kubicorn. Pour l’instant, il ne fournit pas de
releases binaires ; vous devez donc avoir installé le langage
Go pour que les exemples qui suivent fonctionnent.

Vous installez d’abord kubicorn en vérifiant que Go est


accessible et en version 1.8 au minimum. Dans l’exemple,
nous sommes dans un environnement CentOs.

$ go version
go version go1.8 linux/amd64

$ yum group install "Development Tools" \


yum install ncurses-devel

$ go get github.com/kris-nova/kubicorn
...
Create, Manage, Image, and Scale Kubernetes
infrastructure in the cloud.

Usage:
kubicorn [flags]
kubicorn [command]
Available Commands:
adopt Adopt a Kubernetes cluster into a
Kubicorn state store
apply Apply a cluster resource to a cloud
completion Generate completion code for bash
and zsh shells.
create Create a Kubicorn API model from a
profile
delete Delete a Kubernetes cluster
getconfig Manage Kubernetes configuration
help Help about any command
image Take an image of a Kubernetes
cluster
list List available states
version Verify Kubicorn version
Flags:
-C, --color Toggle colorized logs
(default true)
-f, --fab Toggle colorized logs
-h, --help help for kubicorn
-v, --verbose int Log level (default 3)

Use "kubicorn [command] --help" for more


information about a command.
Une fois la commande kubicorn installée, vous créez les
ressources de cluster en choisissant un profil et en vérifiant
que les ressources sont correctement définies :

$ kubicorn create --name k8scb --profile aws


2017-08-14T05:18:24Z [✔] Selected [fs] state
store
2017-08-14T05:18:24Z [✿] The state
[./_state/k8scb/cluster.yaml] has been...

$ cat _state/k8scb/cluster.yaml
SSH:
Identifier: ""
metadata:
creationTimestamp: null
publicKeyPath: ⁓/.ssh/id_rsa.pub
user: ubuntu
cloud: amazon
kubernetesAPI:
metadata:
creationTimestamp: null
port: "443"
location: us-west-2
...

Nous utilisons ici un profil de ressource par défaut qui suppose que
vous disposez d’une paire de clés dans ⁓/.ssh qui portent le nom
id_rsa et id_rsa.pub (clé publique). Vous modifierez donc ces
paramètres si ce n’est pas le cas. Notez que la région par défaut
choisie est ici l’Oregon, us-west-2.
Vous devez disposer pour continuer d’un compte utilisateur
AWSIAM (Identity and Access Management) dans lequel les
droits suivants sont actifs : AmazonEC2FullAccess,
AutoScalingFullAccess et AmazonVPCFullAccess. Si vous
ne disposez pas d’un tel compte IAM, le moment est venu
d’en créer un.

Un dernier préparatif pour que kubicorn puisse fonctionner


consiste à définir les crédentiels pour le compte utilisateur
IAM concerné sous forme de variables d’environnement,
comme ceci :

$ export
AWS_ACCESS_KEY_ID=***************************
$ export
AWS_SECRET_ACCESS_KEY=**************************
***************

Nous sommes maintenant prêts à créer le cluster à partir


des définitions de ressources précédentes et de l’accès AWS
fourni :

$ kubicorn apply --name k8scb


2017-08-14T05:45:04Z [✔] Selected [fs] state
store
2017-08-14T05:45:04Z [✔] Loaded cluster: k8scb
2017-08-14T05:45:04Z [✔] Init Cluster
2017-08-14T05:45:04Z [✔] Query existing
resources
2017-08-14T05:45:04Z [✔] Resolving expected
resources
2017-08-14T05:45:04Z [✔] Reconciling
2017-08-14T05:45:07Z [✔] Created KeyPair [k8scb]
2017-08-14T05:45:08Z [✔] Created VPC [vpc-
7116a317]
2017-08-14T05:45:09Z [✔] Created Internet
Gateway [igw-e88c148f]
2017-08-14T05:45:09Z [✔] Attaching Internet
Gateway [igw-e88c148f] to VPC ...
2017-08-14T05:45:10Z [✔] Created Security Group
[sg-11dba36b]
2017-08-14T05:45:11Z [✔] Created Subnet [subnet-
50c0d919]
2017-08-14T05:45:11Z [✔] Created Route Table
[rtb-8fd9dae9]
2017-08-14T05:45:11Z [✔] Mapping route table
[rtb-8fd9dae9] to internet gate...
2017-08-14T05:45:12Z [✔] Associated route table
[rtb-8fd9dae9] to subnet ...
2017-08-14T05:45:15Z [✔] Created Launch
Configuration [k8scb.master]
2017-08-14T05:45:16Z [✔] Created Asg
[k8scb.master]
2017-08-14T05:45:16Z [✔] Created Security Group
[sg-e8dca492]
2017-08-14T05:45:17Z [✔] Created Subnet [subnet-
cccfd685]
2017-08-14T05:45:17Z [✔] Created Route Table
[rtb-76dcdf10]
2017-08-14T05:45:18Z [✔] Mapping route table
[rtb-76dcdf10] to internet gate...
2017-08-14T05:45:19Z [✔] Associated route table
[rtb-76dcdf10] to subnet ...
2017-08-14T05:45:54Z [✔] Found public IP for
master: [34.213.102.27]
2017-08-14T05:45:58Z [✔] Created Launch
Configuration [k8scb.node]
2017-08-14T05:45:58Z [✔] Created Asg
[k8scb.node]
2017-08-14T05:45:59Z [✔] Updating state store
for cluster [k8scb]
2017-08-14T05:47:13Z [✿] Wrote kubeconfig to
[/root/.kube/config]
2017-08-14T05:47:14Z [✿] The [k8scb] cluster has
applied successfully!
2017-08-14T05:47:14Z [✿] You can now 'kubectl
get nodes'
2017-08-14T05:47:14Z [✿] You can SSH into your
cluster ssh -i ⁓/.ssh/id_rsa ...

Les différentes couleurs ne sont pas visibles dans la version


imprimée, mais les quatre dernières lignes sont affichées en
vert pour confirmer que tout s’est bien déroulé. Vous pouvez
le vérifier en vous rendant à la console EC2 Amazon depuis
votre navigateur (Figure 14.1).
Figure 14.1 : Console Amazon EC2 montrant deux nœuds créés par kubicorn.

Il ne reste plus qu’à se connecter par SSH comme suggéré


dans la dernière ligne de la commande précédente :

$ ssh -i ⁓/.ssh/id_rsa ubuntu@34.213.102.27


The authenticity of host '34.213.102.27
(34.213.102.27)' can't be established.
ECDSA key fingerprint is
ed:89:6b:86:d9:f0:2e:3e:50:2a:d4:09:62:f6:70:bc.
Are you sure you want to continue connecting
(yes/no)? yes
Warning: Permanently added '34.213.102.27'
(ECDSA) to the list of known hosts.
Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-
1020-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

Get cloud support with Ubuntu Advantage Cloud


Guest:
http://www.ubuntu.com/business/services/cloud

75 packages can be updated.


32 updates are security updates.

To run a command as administrator (user "root"),


use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ip-10-0-0-52:⁓$ kubectl get all -n kube-
system
NAME
READY STATUS
po/calico-etcd-qr3f1
1/1 Running
po/calico-node-9t472
2/2 Running
po/calico-node-qlpp6
2/2 Running
po/calico-policy-controller-1727037546-f152z
1/1 Running
po/etcd-ip-10-0-0-52
1/1 Running
po/kube-apiserver-ip-10-0-0-52
1/1 Running
po/kube-controller-manager-ip-10-0-0-52
1/1 Running
po/kube-dns-2425271678-zcfdd
0/3 ContainerCreating
po/kube-proxy-3s2c0
1/1 Running
po/kube-proxy-t10ck
1/1 Running
po/kube-scheduler-ip-10-0-0-52
1/1 Running

NAME CLUSTER-IP EXTERNAL-IP


PORT(S) AGE
svc/calico-etcd 10.96.232.136 <none>
6666/TCP 4m
svc/kube-dns 10.96.0.10 <none>
53/UDP,53/TCP 4m

NAME DESIRED CURRENT


UP-TO-DATE AVAILABLE AGE
deploy/calico-policy-controller 1 1
1 1 4m
deploy/kube-dns 1 1
1 0 4m

NAME DESIRED
CURRENT READY AGE
rs/calico-policy-controller-1727037546 1
1 1 4m
rs/kube-dns-2425271678 1
1 0 4m
Une fois cela fait, pensez à supprimer le cluster Kubernetes
qui vous a servi à faire vos essais, en n’oubliant pas que cela
peut prendre quelques minutes :

$ kubicorn delete --name k8scb


2017-08-14T05:53:38Z [✔] Selected [fs] state
store
Destroying resources for cluster [k8scb]:
2017-08-14T05:53:41Z [✔] Deleted ASG
[k8scb.node]
...
2017-08-14T05:55:42Z [✔] Deleted VPC [vpc-
7116a317]

Discussion
Le projet kubicorn est assez récent, mais il fonctionne
correctement, et permet même de créer des clusters sur
Azure et sur Digital Ocean.

Vous devez avoir installé le langage Go, car le projet ne


fournit pas de binaires pour l’instant. L’outil est très souple
en termes de configuration et assez intuitif à utiliser,
surtout si vous avez une certaine expérience
d’administrateur.

Voir aussi
• Guide AWS Identity and Access Management « Creating an
IAM User in Your AWS Account »
(http://docs.aws.amazon.com/IAM/latest/UserGuid
e/id_users_create.html)

• Configuration de Kubernetes dans AWS dans la


documentation kubicorn
(http://kubicorn.io/documentation/aws-
walkthrough.html)

• Présentation vidéo de Lachlan Evenson


(https://www.youtube.com/watch?v=XpxgSZ3dspE)
Stockage de secrets cryptés
dans un versionneur

Problème
Vous voulez stocker tous vos manifestes Kubernetes dans un
système de contrôle de version (un versionneur) en étant
certain qu’ils sont partagés en toute sécurité, même de
façon publique, et y compris les secrets.

Solution
Servez-vous du contrôleur Kubernetes Sealed-secrets qui
permet de décrypter dans un seul sens des secrets cryptés en
créant des objets de type Secret internes au cluster, des
secrets scellés (voir Chapitre 8).

Vos informations confidentielles sont cryptées dans un objet


de type SealedSecret qui est une ressource CRD spécifique
(voir section « Enrichir l’API par des définitions de
ressources personnalisées (CRD) » du Chapitre 13). Ce genre
d’objet peut être stocké en toute sécurité dans un système de
contrôle de version et même partagé de façon publique. Une
fois que l’objet est créé sur le serveur API Kubernetes, c’est
le contrôleur qui le décrypte et crée un objet de type Secret
qui n’est que codé en base64.

Commencez par télécharger la plus récente version binaire


de l’outil kubeseal afin de pouvoir crypter vos secrets :

$ GOOS=$(go env GOOS)

$ GOARCH=$(go env GOARCH)

$ wget https://github.com/bitnami/sealed-
secrets/releases/download/v0.5.1/
kubeseal-$GOOS-$GOARCH

$ sudo install -m 755 kubeseal-$GOOS-$GOARCH


/usr/local/bin/kubeseal

Créez ensuite votre définition de secret scellé CRD puis


lancez le contrôleur :

$ kubectl create -f
https://github.com/bitnami/sealed-
secrets/releases/
download/v0.5.1/sealedsecret-crd.yaml

$ kubectl create -f
https://github.com/bitnami/sealed-
secrets/releases/
download/v0.5.1/controller.yaml
Vous disposez alors d’une nouvelle ressource spécifique et
d’un nouveau pod qui s’exécute dans l’espace de noms
kube-system :

$ kubectl get customresourcedefinitions


NAME AGE
sealedsecrets.bitnami.com 34s

$ kubectl get pods -n kube-system | grep sealed


sealed-secrets-controller-867944df58-l74nk 1/1
Running 0 38s

À partir de ce moment, vous pouvez utiliser les secrets


scellés. Commencez par générer un manifeste de secret
générique :

$ kubectl create secret generic oreilly --from-


literal=password=root -o json --dry-run >
secret.json

$ cat secret.json
{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "oreilly",
"creationTimestamp": null
},
"data": {
"password": "cm9vdA=="
}
}

Vous pouvez faire créer un manifeste « à blanc », c’est-à-dire sans


créer l’objet sur le serveur API. Il suffit d’ajouter l’option --dry-
run. Le manifeste sera affiché sur stdout. Si vous voulez le format
YAML, ajoutez l’option -o yaml et si vous voulez le format JSON,
ajoutez -o json.

Vous pouvez alors utiliser la commande kubeseal pour


générer votre nouvel objet spécifique SealedSecret :

$ kubeseal < secret.json > sealedsecret.json

$ cat sealedsecret.json
{
"kind": "SealedSecret",
"apiVersion": "bitnami.com/v1alpha1",
"metadata": {
"name": "oreilly",
"namespace": "default",
"creationTimestamp": null
},
"spec": {
"data":
"AgDXiFG0V6NKF8e9k1NeBMc5t4QmfZh3QKuDORAsFNCt50w
TwRhRLRAQOnz0sDk..."
}
}
Vous pouvez enregistrer le fichier sealedsecret.json dans votre
système de contrôle de version. Le seul moyen de le
décrypter est d’utiliser la clé privée qui est stockée dans le
contrôleur de secrets scellés. Une fois que vous avez créé
l’objet SealedSecret, le contrôleur va le détecter, le
décrypter et générer le secret correspondant :

$ kubectl create -f sealedsecret.json


sealedsecret "oreilly" created

$ kubectl get sealedsecret


NAME AGE
oreilly 5s

$ kubectl get secrets


NAME TYPE DATA AGE
...
oreilly Opaque 1 5s

Voir aussi
• Référentiel sealed-secrets
(https://github.com/bitnami/sealed-secrets)

• Article d’Angus Lee « Sealed Secrets: Protecting Your


Passwords Before They Reach Kubernetes »
(https://engineering.bitnami.com/articles/seale
d-secrets.html)
Déployer des fonctions avec
kubeless

Problème
Vous voulez déployer dans Kubernetes des fonctions écrites
en Python, en Node.js, en Ruby ou en Powershell sans avoir
à construire d’abord un conteneur Docker. Vous voulez
également pouvoir appeler ces fonctions via HTTP ou leur
envoyer des événements via un bus de messages.

Solution
Profitez de la solution sans serveur native à Kubernetes
nommée kubeless.

L’outil kubeless se sert d’une définition de ressource CRD,


de type CustomResourceDefinition (voir section
« Enrichir l’API par des définitions de ressources
personnalisées (CRD) » du Chapitre 13) pour définir des
objets de type Function ainsi qu’un contrôleur servant à
déployer ces fonctions dans des pods d’un cluster
Kubernetes.

Les possibilités qui s’ouvrent ainsi supposent certaines


compétences, mais nous allons montrer un exemple simple
dans cette section : il consiste à déployer une fonction
Python qui va renvoyer une charge utile JSON que vous lui
transmettez.

Nous commençons par créer un espace de noms kubeless


puis nous lançons le contrôleur. Nous pouvons récupérer le
manifeste qui est fourni avec chaque version dans la page de
release sur GitHub. Dans cette même page, nous pouvons
télécharger les binaires de kubeless :

$ kubectl create ns kubeless

$ curl -sL
https://github.com/kubeless/kubeless/releases/do
wnload/v0.3.1/ \
kubeless-rbac-v0.3.1.yaml | kubectl create -f -

$ wget
https://github.com/kubeless/kubeless/releases/do
wnload/v0.3.1/ \
kubeless_darwin-amd64.zip

$ sudo cp bundles/kubeless_darwin-amd64/kubeless
/usr/local/bin

L’espace de noms nommé kubeless présente trois pods : le


contrôleur qui va surveiller les points d’extrémité
spécifiques Function et les deux pods nommés Kafka et
Zookeeper. Ces deux pods ne sont utiles qu’aux fonctions
déclenchées par événements. Celles déclenchées par un flux
HTTP ne réclament que le contrôleur en statut exécution
(Running) :

$ kubectl get pods -n kubeless


NAME READY
STATUS RESTARTS AGE
kafka-0 1/1
Running 0 6m
kubeless-controller-9bff848c4-gnl7d 1/1
Running 0 6m
zoo-0 1/1
Running 0 6m

Pour tester kubeless, écrivons une fonction Python


minimale que nous stockons dans un fichier baptisé post.py :

def handler(context):
print context.json
return context.json

Pour déployer cette fonction dans Kubernetes, nous utilisons


l’interface kubeless. La commande nommée function
deploy réclame plusieurs paramètres facultatifs. L’option -
-runtime sert à choisir le langage qui a servi à créer la
fonction. L’option --http-trigger déclare que la fonction
sera déclenchée par un appel HTTP ou HTTPS. Enfin,
l’option --handler indique le nom de la fonction, le préfixe
correspondant au nom de base du fichier dans lequel la
fonction est stockée. Enfin, l’option --from-file précise le
nom du fichier dans lequel la fonction a été stockée :

$ kubeless function deploy post-python --


trigger-http \
--runtime
python2.7 \
--handler
post.handler \
--from-
file post.py
INFO[0000] Deploying function...
INFO[0000] Function post-python submitted for
deployment
INFO[0000] Check the deployment status executing
'kubeless function ls post-python'

$ kubeless function ls
NAME NAMESPACE HANDLER
RUNTIME TYPE TOPIC
post-python default hellowithdata.handler
python 2.7 HTTP
$ kubectl get pods
NAME READY STATUS
RESTARTS AGE
post-python-5bcb9f7d86-d7nbt 1/1 Running 0
6s
Le contrôleur kubeless va détecter le nouvel objet
Function et créer un déploiement correspondant. Le code
de la fonction est stocké dans une mappe de configuration
(voir Chapitre 8) puis injecté dans le pod actif pendant
l’exécution. La fonction devient alors appelable via HTTP.
Voici les quelques objets concernés :

$ kubectl get functions


NAME AGE
post-python 2m

$ kubectl get cm
NAME DATA AGE
post-python 3 2m

$ kubectl get deployments


NAME DESIRED CURRENT UP-TO-DATE
AVAILABLE AGE
post-python 1 1 1 1
2m

Vous pouvez appeler la fonction au moyen de la commande


kubeless function call :

$ kubeless function call post-python --data


'{"oreilly":"function"}'
{"oreilly": "function"}
Vous pouvez vous servir de kubeless pour d’autres fonctions que
celles déclenchées par HTTP. Découvrez les autres possibilités au
moyen de l’option --help.

Voir aussi
• Référentiel de kubeless
(https://github.com/kubeless/kubeless)

• Exemples kubeless
(https://github.com/kubeless/kubeless/tree/mast
er/examples)

• kubeless sur Azure Container Service


(https://azure.microsoft.com/fr-fr/blog/kuber-
netes-now-generally-available-on-azure-
container-service/)
ANNEXE A
Ressources

Pour chercher les articles déjà francisés, ouvrez dans le site


Web le menu de choix de langue en haut à droite, juste à
gauche du rappel du numéro de version.
Ressources générales
• Documentation de Kubernetes
(https://kubernetes.io/docs/home/)

• Référentiel GitHub de Kubernetes


(https://github.com/kubernetes/kubernetes/)

• Communauté Kubernetes sur GitHub


(https://github.com/kubernetes/community/)
Tutoriels et exemples
• Kubernetes par l’exemple
(http://kubernetesbyexample.com)

• Application d’essai Kubernetes Katacoda


(https://www.katacoda.com/courses/kubernetes/pl
ayground)
Sommaire

Couverture
Guide pratique de Kubernetes - L'art de
construire des conteneurs d'applications -
collection O'Reilly
Copyright
Introduction

Lectorat principal

Motivation des auteurs

Structure du livre

À propos des versions de Kubernetes

Environnement technique présupposé

Fichiers source des exemples

Conventions typographiques

Réutilisation des exemples

Pour nous contacter

CHAPITRE 1. Pour débuter avec Kubernetes


Utiliser Kubernetes sans l’installer

Installer kubectl (le client Kubernetes)

Installer Minikube pour utiliser une instance locale


de Kubernetes

Développer localement avec Minikube

Démarrer une application sur Minikube

Accéder au tableau de bord Minikube

CHAPITRE 2. Créer un cluster Kubernetes

Installer kubeadm pour créer un cluster Kubernetes

Amorcer un cluster Kubernetes avec kubeadm

Télécharger une version de Kubernetes depuis


GitHub

Télécharger les binaires client et serveur

Utiliser une image hyperkube pour exécuter un


nœud maître Kubernetes avec Docker

Écrire un fichier unité systemd pour exécuter des


composants Kubernetes

Créer un cluster Kubernetes dans GKE


Créer un cluster Kubernetes dans ACS (Azure
Container Service)

CHAPITRE 3. Apprendre à utiliser le client


Kubernetes

Obtenir la liste des ressources

Supprimer des ressources

Surveiller les changements de ressources avec


kubectl

Éditer des ressources avec kubectl

Demander une description des ressources des


champs à kubectl

CHAPITRE 4. Créer et modifier des charges de


travail simples

Créer un déploiement avec kubectl

Créer des objets à partir d’un fichier manifeste

Rédiger un manifeste de pod

Lancer un déploiement via un manifeste

Actualiser un déploiement

CHAPITRE 5. Utiliser les services

Créer un service pour y exposer votre application


Vérifier l’entrée DNS d’un service

Changer le type d’un service

Déployer un contrôleur Ingress sur Minikube

Rendre des services accessibles de l’extérieur du


cluster

CHAPITRE 6. Explorer l’API Kubernetes et les


métadonnées clés

Découvrir les points d’extrémité API d’un serveur


API

Structure d’un manifeste Kubernetes

Créer un espace de noms pour éviter les collisions

Définir des quotas dans un espace de noms

Étiqueter un objet avec un label

Utiliser des labels dans des requêtes

Ajouter une annotation et une ressource

CHAPITRE 7. Gérer des charges de travail


spécialisées

Exécuter un job de traitement par lot

Exécuter une tâche planifiée dans un pod


Démarrer un démon d’infrastructure par nœud

Gérer des applications Stateful et leader/suiveur

Influencer le comportement au démarrage des pods

CHAPITRE 8. Volumes et données de


configuration

Échanger des données entre conteneurs par un


volume local

Passage d’une clé d’accès API à un pod via un secret

Fournir des données de configuration à une


application

Utiliser un volume persistant avec Minikube

Maîtriser la persistance de données sur Minikube

Approvisionnement dynamique de stockage


persistant sur GKE

CHAPITRE 9. Adaptation à la charge (scaling)

Redimensionner un déploiement

Redimensionner automatiquement un cluster dans


GKE

Redimensionner automatiquement un cluster AWS


Redimensionnement horizontal de pod dans GKE

CHAPITRE 10. Sécurité

Fournir un identifiant unique par application

Lister et visualiser les informations de contrôle


d’accès

Contrôler l’accès aux ressources

Sécuriser des pods

CHAPITRE 11. Supervision et journalisation


(logging)

Accéder aux journaux d’un conteneur

Récupérer d’une avarie avec une sonde de vitalité

Retenir le trafic vers un pod avec une sonde de


disponibilité

Ajouter des sondes de vitalité et de disponibilité à un


déploiement

Activer Heapster sur Minikube pour surveiller des


ressources

Utiliser Prometheus sur Minikube

Utiliser EFK sur Minikube

CHAPITRE 12. Maintenance et dépannage


Activer l’autocomplétion de kubectl

Retirer un pod d’un service

Accéder à un service ClusterIP de l’extérieur du


cluster

Comprendre et analyser les statuts des ressources

Déboguer un pod

Obtenir une vue détaillée du statut d’un cluster

Ajouter des nœuds de travail Kubernetes

Retirer un nœud pour maintenance

Gérer le composant etcd

CHAPITRE 13. Développer pour Kubernetes

Compiler les sources

Compiler un seul composant

Interagir avec l’APIKubernetes depuis un client


Python

Enrichir l’API par des définitions de ressources


personnalisées (CRD)

CHAPITRE 14. L’écosystème Kubernetes


Installer le gestionnaire de paquetages Helm

Utiliser Helm pour installer des applications

Créer votre charte pour empaqueter une application


avec Helm

Convertir un fichier compose Docker en manifeste


Kubernetes

Créer un cluster Kubernetes avec kubicorn

Stockage de secrets cryptés dans un versionneur

Déployer des fonctions avec kubeless

ANNEXE A. Ressources

Ressources générales

Tutoriels et exemples

Vous aimerez peut-être aussi