Kubernetes est probablement la technologie la plus complexe sur laquelle j'ai pu travailler cette année, à tel point que j'ai longtemps été réticent à m'y frotter. J'ai tout de même choisi de me faire violence et de persévérer car ce domaine est très valorisant et très valorisé. Utiliser un outil dans le cadre d'un réel besoin est le meilleur moyen pour apprendre, c'est pourquoi j'ai décidé de migrer mon blog et d'autres sites. Voici une vue d'ensemble du fonctionnement du blog dans Kubernetes, en sachant que le pod est composé d'un container OpenSMTPD (pour le formulaire de contact) et bien entendu d'un container Pluxml :
Note : Fait avec Diagrams (schema as code). L'Ingress Controller est Traefik (j'en parle plus bas).
Le changement n'a pas été instantané, loin de là, à vrai dire j'avais ce projet de côté depuis plus de 1 an. Mon idée de départ était de créer un cluster a plusieurs nœuds mais cela ajoute une contrainte au niveau du stockage, en effet il faut des volumes accessibles en réseau. La question des coût s'est posée également car mon blog n'est pas suffisamment important pour justifier 4 serveurs (3 noeuds + 1 NAS). J'ai donc fait des concessions et accepté d'utiliser un cluster Kubernetes à 1 nœud, avec la Storage Class par défaut de k3s (local-path). C'est donc un premier pas timide mais l'objectif est de me familiariser avec Kubernetes.
J'ai utilisé k3s, le Kubernetes Lightweight de Rancher. Cette distribution a l'avantage d'être facile à installer (une commande curl) et d'avoir une emprunte mémoire assez limitée (même si ça reste beaucoup plus élevé que Docker). Par contre elle n'est pas miraculeuse et le côté "lightweight" s'est fait en excluant ou limitant certaines features. Par exemple pour la gestion des Ingress on a droit à un Traefik built-in en version 1.7 (donc legacy) absolument pas documenté pour la gestion des certificats Let's Encrypt. J'ai donc désactivé cette version et installé le Traefik 2.x de containous grâce à Helm. Cette version de Traefik est implémentée en tant que CustomResourceDefinition (aka CRD) et est documentée sur le site officiel. J'avoue quand même avoir passé 3-4 jours à faire fonctionner ces satanés certificats Let's Encrypt mais j'y suis finalement parvenu et j'ai beaucoup appris.
Un autre point sur lequel k3s est limité est la liste de Storage Classes. Dans Kubernetes, une Storage Class, est en quelques sortes un driver qui peut provisionner à la volée des PersistentVolumes (PV). Dans mon cas je comptais utiliser AzureFiles pour provisionner des partages depuis un Storage account sur Azure mais il semble que k3s ne l'implémente pas. En lisant cette documentation je crois comprendre que k3s ne propose qu'une Storage Class locale, c'est à dire sur le nœud qui exécute le pod. Quand aux backends supportés, je ne trouve pas de liste même si j'ai pu valider que NFS fonctionne bien.
Migrer le blog dans Kubernetes m'a déjà permis d'apprendre beaucoup de choses. Rien de plus gratifiant que le sentiment "j'ai compris !!!" après avoir passé des heures à essayer en vain de faire fonctionner une ressource. J'espère me motiver un jour à ajouter d'autres nœuds dans mon cluster, quand j'aurai résolu le problème du stockage.
Attention, cet article va exploser le compteur de buzzwords.
J'utilise Docker en standalone depuis bientôt 3 ans, d'ailleurs mon blog tourne dessus à l'aide de deux images : PluXml et OpenSMTPD - pour le formulaire de contact - que j'ai moi-même réalisé. Sans dire que je maîtrise Docker, j'ai un niveau bien avancé. A côté de cela je travaille régulièrement avec OpenShift (la distribution Kubernetes commerciale de Red Hat) et même si je suis bien moins à l'aise qu'avec Docker, j'ai tout de même pas mal de connaissances.
Docker seul est un peu limité, on ne peut pas faire de clustering, ce qui est bien dommage car les containers et les micro-services s'y prêtent fortement. Il existe heureusement Swarm qui étend Docker au support d'une infrastructure à plusieurs nœuds.
D'un autre côté, le monde entier a les yeux rivés sur Kubernetes, le grand concurrent à Docker conçu dès le départ pour les clusters et la haute disponibilité. Ce dernier est très puissant mais aussi beaucoup plus complexe que Docker Swarm (surtout les RBAC dont je ne parlerai pas). Il faut aussi comprendre qu'on ne télécharge pas Kubernetes, on télécharge une distribution Kubernetes, et il en existe plusieurs. Dans le test de performances plus bas dans cet article je vais utiliser k3s, fourni par Rancher et grandement allégé et simplifié.
Alors, faut-il utiliser Swarm ou Kubernetes ?
Architecture
Docker Swarm
Le cluster se compose de nodes manager et workers. Les premiers sont chargés de piloter le cluster, les second exécutent les containers. Les commandes doivent être exécutées sur un des managers. Un système d'encapsulation UDP est en place entre les nodes, il permet aux réseaux des containers de se propager.
Dans Swarm on déclare des services, soit en cli soit en yaml (docker-compose). Le scheduler va ensuite provisionner les containers nécessaires au service sur un ou plusieurs workers, selon le nombre de replica demandé. Exemple :
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
sb40x4z1zqkb httpd replicated 1/1 httpd:latest
owq6yurtagjg traefik replicated 1/1 traefik:2.1 *:80->80/tcp, *:443->443/tcp
On peut augmenter ou diminuer manuellement le nombre de replicas, il y a un load balancer interne qui va répartir le trafic de manière transparente. Un système d'affinités permet de lier les containers à une node en particulier, très pratique. Les services peuvent être mis à jour en rolling update, c'est à dire qu'on ne restart pas tous les containers d'un coup mais les uns après les autres, ce qui permet de ne pas interrompre le service. Un rollback est possible.
Et... c'est à peu près tout. Simple et efficace, mais aussi un peu limité il faut l'avouer. Swarm est un bon choix pour un usage personnel de part sa simplcité. Par contre on va voir que pour les cas d'usage avancés, Kubernetes est plus adapté.
Kubernetes
Accrochez-vous. Commençons avec l'architecture Infra.
Le cluster est composé de nodes Control Plane (les masters ou managers) ainsi que des workers. Les Control Planes pilotent le cluster et l'API Kubernetes, à l'aide d'un système de configuration centralisé, qui est souvent basé sur etcd (selon les distributions) mais pas toujours. Par exemple, k3s utilise sqlite. Les workers ont un agent (kubelet) qui reçoit les instructions du scheduler. Là encore une encapsulation UDP est prévue entre les nodes pour permettre la propagation des réseaux des containers.
Attaquons maintenant le fonctionnement des ressources. Dans le cluster Kubernetes, tout est objet, tout est yaml ou json. C'est avec ça que l'on contrôle comment nos containers sont déployés et fonctionnent. Les types (kind) d'objets les plus courants sont :
Pod : Un espace logique qui exécute un ou plusieurs containers.
Service : Sert à exposer un ou plusieurs ports pour un pod, attaché à une IP privée ou publique.
DeploymentConfig : Défini ce qu'on déploie. Typiquement l'image à utiliser, le nombre de replica, les services à exposer, les volumes...
ReplicaSet : un contrôleur qui va vérifier que le nombre de pods en place correspond au nombre de replicas attendu. Scaling automatique possible.
PV, PVC : système d'attribution (automatique ou pas) de volumes persistants.
ConfigMap : objet permettant de stocker de la configuration, qui peut être ensuite lue et utilisée par les containers.
Namespace : Séparation logique des ressources, seules les ressources affectées au namespace en cours sont visibles.
Exemple d'utilisation :
$ kubectl -n web get all
NAME READY STATUS RESTARTS AGE
pod/svclb-httpd-rw7k5 1/1 Running 0 5s
pod/httpd-8647457dd7-s2j4d 1/1 Running 0 5s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/httpd LoadBalancer 10.43.80.117 10.19.2.73 80:30148/TCP 5s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/svclb-httpd 1 1 1 1 1 5s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/httpd 1/1 1 1 5s
NAME DESIRED CURRENT READY AGE
replicaset.apps/httpd-8647457dd7 1 1 1 5s
Par souci de simplification je ne parle pas du système de RBAC qui permet une granularité dans la gestion des droits, au prix d'une complexité (très) importante.
Kubernetes n'est pas aussi simple que Swarm mais il permet de faire beaucoup plus de choses. En fait on peut presque dire qu'on peut tout faire avec. Les namespaces sont une fonctionalité très pratique, tout comme le scaling auto et la possibilité d'éditer les objets avec la commande edit. En environnement pro, où vous allez gérer plusieurs clients, beaucoup d'applications et de fortes charges, Kubernetes est quasiment indispensable.
Fonctionnalités
Swarm
Kubernetes
Configuration yaml ou json
Oui
Oui
Commandes Docker
Oui
Non
CLI distant
Oui
Oui
Network inter-containers & DNS
Oui
Oui
Replicas
Oui
Oui
Scaling manuel
Oui
Oui
Auto-scaling
Non
Oui
Health probes
Non
Oui
Modification d'objets en ligne
Non
Oui
RBAC
Oui (EE)
Oui
Namespaces
Non
Oui
Volumes self-service
Non
Oui
Kubernetes est indéniablement plus complet, mais à quel point ces fonctionnalités sont-elles indispensables, surtout pour un usage en perso ? Et bien il y a à mon sens deux points que je trouve excellents dans Kubernetes et qui me manquent dans Swarm:
La modification d'objets en ligne. J'entends par là que la commande kubectl edit type/object permet de faire une modification à la volée, par exemple changer un port ou une version d'image dans un DeploymentConfig. Cela n'est à ma connaissance pas possible dans Docker, sauf avec docker-compose (stack dans le cas de Swarm) à condition d'avoir encore les fichiers yaml et que ceux-ci soient à jour.
Les namespaces. Pour éviter de mélanger plusieurs ressources de projets qui n'ont rien à voir, Kubernetes propose un système de namespaces. Par exemple je peux créer un namespace utux dans lequel je vais déployer mes images PluXml et OpenSMTPD, ce qui permet de s'y retrouver mais aussi de tout supprimer plus facilement si besoin. Les namespaces sont aussi très utiles lorsque vous partagez ou louez votre Cluster Kubernetes, chaque utilisateur a ainsi son espace dans lequel il ne voit que ses ressources.
Cependant Docker et Docker Swarm sont beaucoup plus simples et n'utilisent que des composant upstream.
Consommation de ressources
Tests effectués sur des instances DEV-1S de chez Scaleway (2 vcpu, 2GiB RAM, no swap). Le système d'Exploitation est Debian 10 Buster x86_64.
Système seul: RAM 70.9M/1.94G
Swarm seul: RAM 155M/1.94G
Swarm + Traefik + PluXml (apache): 209M/1.94G
k3s seul: RAM 619M/1.94G
k3s + Traefik + PluXml (apache): 678M/1.94G
Si vous comptez monter un cluster de Raspberry Pi avec 1GB de mémoire, vous pouvez oublier k3s/Kubernetes qui en consomme déjà presque 75% à vide. Si vous avez les moyens de payer des serveurs de calcul un peu plus costauds, avec 16 ou 32GB de mémoire, la différence sera alors négligeable.
Les pré requis pour certaines distributions comme OpenShift sont beaucoup plus importants: 4 vcpus et 16GiB de ram pour chaque master (x3), 2 vpus et 8GiB de ram pour les workers (à multiplier par 4 si vous montez l'infra de logging ElasticSearch). C'est la raison pour laquelle je ne l'ai pas utilisé dans ce comparatif, il est hors compétition.
Exemple
Docker Swarm
Exemple simple avec la création d'un container apache vide :
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
8pxc580r3yh6 test_web replicated 1/1 httpd:latest *:8080->80/tcp
$ curl http://127.0.0.1:8080
<html><body><h1>It works!</h1></body></html>
Kubernetes
Reprenons notre container apache vide :
apiVersion: v1
kind: Service
metadata:
name: web
labels:
app: web
spec:
ports:
- port: 80
selector:
app: web
tier: frontend
type: LoadBalancer
---
apiVersion: apps/v1 #
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
selector:
matchLabels:
app: web
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: web
tier: frontend
spec:
containers:
- image: httpd:latest
name: web
ports:
- containerPort: 80
name: web
Chargons ces objets :
$ kubectl create namespace test
$ kubectl -n test create -f web.yaml
Vérifions :
# kubectl -n test get all
NAME READY STATUS RESTARTS AGE
pod/svclb-web-jqs6j 0/1 Pending 0 7m17s
pod/web-774f857549-dstkz 1/1 Running 0 7m17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/web LoadBalancer 10.43.109.52 80:30452/TCP 7m17s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/svclb-web 1 1 0 1 0 7m17s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 1/1 1 1 7m17s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-774f857549 1 1 1 7m17s
$ curl http://10.43.109.52
<html><body><h1>It works!</h1></body></html>
Conclusion
Kubernetes est plus complet que Swarm mais aussi plus compliqué. Il n'est pas toujours facile de choisir entre les deux, mais je vous donne les conseils suivants :
Pour un usage personnel, Docker standalone ou Docker Swarm sera votre choix par défaut, même si rien ne vous empêche d'essayer Kubernetes :)
Si vous êtes pragmatique ou que vous souhaitez travailler sur les containers, alors ce sera Kubernetes. C'est l'outil en vogue plébiscité par tout le monde et c'est ce que vous devez avoir sur votre CV.
Pour un usage en entreprise, Kubernetes sera incontournable car il y a de nombreuses offres dans le Cloud et On-Premise. Aks ou OpenShift sur Azure, Eks chez Aws, pour ne citer que les plus connus. Tout l'écosystème des containers est focalisé sur Kubernetes et les namespaces vous serons indispensables dans un contexte multi-client.
Pour finir, un court retour d'expérience sur l'utilisation de Kubernetes en entreprise. Tout d'abord, soyez conscient que votre cluster, vos clients et vos utilisateurs vous poseront de nombreux défis. Ne pensez pas que Kubernetes va résoudre magiquement tous vos problèmes et que vos admins vont se tourner les pouces, bien au contraire. Dans le cas d'une plateforme On-Premise, prévoyez 2 admins à temps plein au minimum. Ensuite, vous devez disposer de compétences dans quasiment tous les domaines : réseau, stockage, système, middlewares et surtout avoir une bonne maitrîse de la Elastic Stack (ELK ou EFK) puisque vous aurez à gérer les logs de vos nodes et containers.
Avec cet article j'espère avoir bien présenté les différences entre Docker Swarm et Kubernetes et fourni quelques pistes à ceux qui hésitent :)
Mon blog fonctionne sous Pluxml dans un container Docker. Jusqu'à présent l'accès http/https se faisait par une instance Nginx frontale faisant office de reverse-proxy et SSL offloading. Je viens de le remplacer par Traefik.
Qu'est-ce que Traefik ? Il se qualifie de Edge Router, un terme qui vous sera peut-être familier si vous travaillez avec Kubernetes ou Openshift. En fait c'est un reverse-proxy adaptatif :
Gère les certificats Let's Encrypt comme un grand.
Route le trafic http/https vers vos backends applicatifs dans Docker.
S'auto-configure, l'ajout d'un container Docker correctement labelisé va déclencher la création d'un certificat Let's Encrypt et d'une route.
Scalable
Je reviendrais un peu plus tard sur la mise en place de Traefik !
Après 1 an à travailler sur Azure (sur des périmètres Linux ou serverless), j'ai enfin eu l'occasion de passer ma certification ! Il en existe plusieurs et cela bouge régulièrement. J'ai retenu la AZ-103 (Microsoft Azure Administrator) car accessible pour une première approche et très en rapport avec ce que je fais en ce moment.
L'examen se planifie en ligne et ne se déroule pas chez Microsoft mais dans des centres qui ont reçu l'agrément. Après avoir présenté deux pièces d'identité, vidé nos poches et mis nos affaires dans un casier sécurisé, nous nous rendons dans une pièce filmée où un client léger connecté en TSE nous permet de dérouler les questions de la certification. Dans mon cas j'ai eu :
62 questions (tout type compris).
En majorité du QCM type "code de la route".
2 labs (accès au portail Azure avec une liste de tâches à effectuer).
Plusieurs case study (une page qui décrit un contexte, puis du QCM).
Temps limité de 3 h (j'ai terminé avec une marge de... 4 min !)
Le résultat est donné dès la sortie, positif pour moi car j'ai eu plus de 800 points alors que le minimum est 700.
Mes conseils pour bien se préparer à la certification AZ-103 sont les suivants:
Faire les examens à blanc AZ-103 chez Whizlabs. Oui c'est payant (€15,95 au moment où j'écris) mais les questions proposées sont très proches (voire identiques) à celles du vrai examen, je pense que je n'aurais pas réussi sans Whizlabs. Prenez une semaine pour faire et refaire les questions.
12 labs AZ-103 par Microsoft (sources). Vous ne pourrez pas faire les points qui évoquent la synchronisation A.D ou demandent une subscription P2, mais c'est quand même une bonne base.
Bien potasser l'Azure Active Directory, la synchronisation avec les A.D On Premise, la protection des identités (MFA), la migration des Data vers du Blob Storage car beaucoup de questions s'y rapportent.
Avoir déjà provisionné des machines virtuelles, disques managés, availability sets, scalesets, VNET, peerings. Vous aurez des questions sur les SLA des VMs, et sur les niveaux de tiering des comptes de stockage.
Avoir de bonnes bases en réseau (masques, VPN, DNS, firewall), des bases en Powershell (bien que pas indispensable).
Bonne chance si vous aussi vous visez la AZ-103 ou tout autre certification :)
J'ai une relation d'amour et de haine avec Terraform. Cet outil est formidable pour discuter avec les providers de cloud et faire de la remédiation, en revanche je me casse régulièrement les dents sur le langage hcl que je trouve extrêmement limité et frustrant. Trois exemples :
Les variables : Toute variable doit être déclarée, typée et contenir une valeur, même si on ne s'en sert pas. Une variable ne peut pas être égale à une autre. Pas de dictionnaire.
Pas de if, pas de loop : On doit ruser avec count mais on perd grandement en souplesse et en lisibilité.
pas DRY: (Don't Repeat Yourself) on ne peut pas utiliser du code commun et envoyer des variables en fonction de l'environnement, donc on duplique pas mal de code. Ce problème est réglé par Terragrunt que je recommande.
C'est principalement pour ces deux raisons que je considère qu'il est très difficile voire impossible de faire du code générique sur Terraform. On ne peut pas créer un module qui gère tous les cas possibles à l'aide des variables, on devra obligatoirement imposer des choix.
Terraform 0.12 est une version qui était très attendue depuis près de 1 an car promettant de lever pas mal de limitations du hcl. Et en effet la nouvelle version du langage nous apporte des nouveautés très appréciables. Mes 3 préférées sont :
First-class expression syntax :
${var.foo} devient var.foo
${var.foo["key"]} devient var.foo.key
${var.foo[count.index]} devient var.foo (mon préféré)
Generalized type system :Les variables peuvent être des "object" composés de clés de différents types. Les objets eux-mêmes peuvent faire partie d'une liste: exemple. C'est peut-être le plus gros progrès de cette version.
Iteration constructs : Arrivée des boucles for et des boucles dynamiques, semble en pratique plus limité que ce qu'on pourrait croire mais c'est bon à prendre. Exemple.
Terraform 0.12 couplé à Terragrunt offre une plus grande souplesse et évite de se répéter. J'ai entamé la migration de mes projets pour profiter de ces nouveautés, et j'en suis très content. Merci Terraform :)