Gardons le cap avec Kubernetes

Publié le 25 février 2021 par Clément Alexandre | ops

Kubernetes est désormais omniprésent pour orchestrer les infrastructures privées ou les clouds publics. Établissons un premier contact avec cette solution incontournable.

Je vous propose d’embarquer à bord et d’en apprendre un peu plus sur notre ténébreux Kubernetes (barreur en langue grecque), dont la mission sera d’assurer le bon fonctionnement de notre système d’information face aux aléas du métier.

coup-de-tabac_crop-1

Mon site internet est présenté en direct au journal télévisé (allégorie).
Image : Tintin et l'Étoile mystérieuse. © Hergé - Moulinsart 2021

Avertissement

Les exemples getting started Kubernetes sont parfois un peu trop abstraits ou disparates à mon goût.
N’ayant pour autant aucune prétention à propos d’un sujet aussi touffu, je vous propose ici une approche assez naïve mais, je l’espère, plus digeste.
Tout aussi touffu barbu, c’est Jonathan, Directeur Technique de Synbioz et dirigeant de Plunge.cloud qui sera à même d’apporter son expertise et les détails passionnants sur notre usage de Kubernetes.
Il proposera d’ailleurs très prochainement une réponse à cet article pour approfondir les thèmes que je vais simplement survoler ici.

Kubernetes, c’est quoi ?

Kubernetes est issu d’un projet interne de Google, proposé en Open Source en partenariat avec la Fondation Linux, via la Cloud Native Computing Foundation.

Je vous propose de résumer Kubernetes comme étant un contrat entre un prestataire de service et un client.
Le client envoie une spécification de ses besoins et l’orchestrateur se charge de la mise en œuvre.

Si l’on souhaite devenir prestataire Kubernetes ou bien utiliser Kubernetes sur notre propre parc informatique, les outils de base sont plutôt abstraits ; il s’agira de les compléter par des implémentations concrètes (et souvent virtualisées) choisies en fonction des contraintes de notre environnement technique.

Une API pour les gouverner tous

Le Certified Kubernetes Conformance Program est une certification garantissant qu’une infrastructure est compatible avec le client API Kubernetes ; elle est délivrée par la Cloud Native Computing Foundation. On peut consulter la liste des clouds certifiés ici

Libre à vous de créer une infrastructure de A à Z et de devenir le prochain géant de l’hébergement web : vous n’avez qu’à répondre à quelques prérogatives.

Par exemple, lorsque votre premier client souhaitera mettre en place un routage pour accéder à un service via une URL donnée, il est de votre responsabilité de mettre tout ça en œuvre.
Vous pourriez décider de créer un point d’entrée avec NGINX, Apache ou votre propre reverse-proxy maison. Mais un routage doit pouvoir être mis à disposition à la demande.

C’est précisément ce qu’il se passe lorsqu’on utilise Kubernetes sur Amazon AWS, Google Cloud Platform, Scaleway ou encore OVH Cloud. Grossièrement si on demande un routage, celui-ci est mis à notre disposition de manière transparente : les requêtes entrantes seront alors dirigées vers un ensemble de services de votre choix.
Et peu importe comment ce routage est concrètement implémenté dans l’infrastructure (Elastic Load Balancer chez AWS par exemple). En deux mots : on s’en fiche, ON VEUT UN ROUTAGE.

C’est là tout l’esprit de Kubernetes et c’est ce qui rend les déploiements interopérables. On ne fait que spécifier l’état de l’infrastructure que l’on souhaite atteindre, tandis que le prestataire de service se met en quatre pour y parvenir.

Ainsi, on peut imaginer faire un gros fichier de spécifications et l’envoyer sur plusieurs les clouds compatibles. Le résultat en termes de services mis à disposition serait alors le même, alors que l’implémentation technique pourrait être complètement différente d’une plateforme à l’autre.

Des outils

Concrètement si on avait ouvert une boîte de Kubernetes à Noël, on aurait pu être quelque peu décontenancé. Kubernetes n’offre guère que des outils très abstraits :

  • etcd : un registre clé-valeur pour le stockage des données de l’infrastructure et de son état.

  • kube-apiserver : le serveur API qui va nous permettre de nous connecter et piloter l’infrastructure à distance.

  • kube-scheduler : l’ordonnanceur qui va affecter des services à déployer sur les ressources disponibles, tout en respectant les contraintes décrites par l’utilisateur.

  • kube-controller-manager : le service qui garde un œil tous les contrôleurs du cluster. Ces derniers assurent le pilotage de ressources concrètes, telles que les nœuds ou les pods (voir ci-après).

  • cloud-controller-manager : la passerelle pour assurer le lien entre les contrôleurs décrits ci-dessus et leur implémentation dans le monde «  physique  » de notre infrastructure.
    Si nous devions créer notre propre cloud Kubernetes maison, c’est cette brique qui concentrerait la plus grande partie de notre attention.

Un service nommé kubelet est également mis à disposition pour chaque machine qui compose le cluster. Il s’assure de la bonne santé du nœud sur lequel il est installé, ainsi que du bon fonctionnement des conteneurs qui ont été déployés sur chaque machine.

Enfin, kube-proxy est en charge de la communication des services sur chaque machine du cluster et de la mise en réseau des conteneurs qui y sont déployés.

Bon… autant dire que si on souhaite lancer notre cloud maison, on va avoir un peu de travail…
Peut-être pourrions-nous commencer avec un jeu d’outils concrets ; c’est-à-dire qui aura implémenté un cloud-controller-manager qui va exploiter ma machine avec un ensemble de services virtualisés pour une mise en route rapide ?

Les microclusters

Utiliser Kubernetes sur sa machine est possible, on pourra faire appel à une solution microcluster telle que Microk8s, k3d, kind ou minikube.

On va ici parler de Microk8s car il offre un bon rapport fonctionnalités / facilité de démarrage (et c’est ce qu’on lui demande pour aujourd’hui).

Il propose en outre un container registry pour envoyer les images de notre application et un ingress controller, le fameux point d’entrée pour que le cluster puisse recevoir et traiter les requêtes entrantes.

Pour l’installer le seul prérequis est d’avoir une distribution Linux qui support les paquets snap.

Installation et mise en route

L’installation se fait en une ligne :
$ sudo snap install microk8s --classic

Le démarrage du cluster ne demande pas plus d’effort :
$ microk8s start

Le cluster sera alors toujours démarré avec votre système. Il faut donc penser à faire microk8s stop lorsque vous avez fini de travailler pour ne pas gaspiller trop d’électrons.

En prévision de la suite où je vous propose d’en faire le minimum, on ne va même pas activer les addons suggérés pour l’instant.

Déployons !

Avant toute chose, il faut activer un ingress controller sur notre cluster.
C’est le module qui va permettre d’acheminer le trafic entrant dans votre cluster ; par défaut avec Microk8s, c’est un container NGINX qui va s’en charger.
Pour l’activer, il faut lancer la commande suivante :
$ microk8s enable ingress.

C’est parti ! Sans aucun détour, créons un fichier de description de déploiement :

# fichier stack.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mon-app-deployment
spec:
  # Ici on va décrire les pods qui sont concernés par le déploiement
  # (pour les retrouver lorsqu'on voudra les arrêter par exemple)
  replicas: 2 # le nombre de pods qu'on souhaite voir fonctionner simultanément
  selector:
    matchLabels:
      projet: mon-app # Le déploiement concerne les pods répondant
                      # au label "project: mon-app"
  # =================
  # Ci-dessous on retrouve ce qui sera appliqué aux nouveaux pods créés
  template:
    metadata:
      labels:
        projet: mon-app # Il est important d'appliquer le bon label aux nouveaux pods
    spec:
      containers:
      - name: mon-app-serveur-image
        image: clmntlxndr/ruby-hello-ip # image à récupérer (ici, sur le Hub Docker)
        ports:
        - containerPort: 8080 # dans l'image, le serveur écoute sur le port 8080,
                              # il faut donc l'exposer
--- 
apiVersion: v1
kind: Service
metadata:
  name: mon-app-service
spec:
  type: ClusterIP # c'est déjà la valeur par défaut
  selector:
    projet: mon-app # Ce service expose les pods ayant pour label "project: mon-app"
  ports:
    - port: 8080        # Le port à disposition sur le service
      #targetPort: 8080 # port vers lequel on va arriver dans le pod.
                        # Par défaut, c'est le même que "port" (juste au-dessus)
--- 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mon-app-http-ingress
spec:
  rules:
  - http:          # On route le trafic entrant http (i.e. port 80)
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: mon-app-service
            port:
              number: 8080 # On envoie le trafic sur le port 8080 du service

Une fois le fichier créé, on l’envoie à Kubernetes avec la commande suivante :
$ microk8s.kubectl apply -f stack.yml

Microk8s simplifie l’authentification auprès de notre microcluster ; c’est pourquoi on fait microk8s.kubectl et non pas kubectl tout court.

Mais qu’avons-nous donc fait là ?

  • La première partie du fichier décrit un déploiement. Cela consiste à lancer un ou plusieurs (replicas) pods, en l’occurrence des conteneurs issus de notre registre d’images Docker.

  • La deuxième partie décrit un service. Celui-ci permet de mettre à disposition un déploiement sur le cluster. Le mode CluserIP attribue une IP privée (interne au cluster Kubernetes) à notre déploiement.

  • Dans la dernière partie, on définit un ingress. C’est lui qui va permettre d’acheminer le trafic externe au cluster vers le service adéquat. Sans lui, notre service serait inaccessible puisque l’adresse IP de celui-ci n’est accessible qu’au sein du cluster.

Avec quelques commandes, nous pouvons obtenir les informations confirmant le déploiement :

$ microk8s.kubectl get deployments   # raccourci : get deploy

NAME                READY   UP-TO-DATE   AVAILABLE   AGE
mon-app-deployment  2/2     2            2           2m11s

Chouette, deux pods fonctionnent !

Également, on devrait admirer le détail des pods concernés :

$ microk8s.kubectl get pods

NAME                                  READY   STATUS    RESTARTS   AGE
mon-app-deployment-7f57d64744-zls2h   1/1     Running   0          3m4s
mon-app-deployment-7f57d64744-q2p8s   1/1     Running   0          3m4s

So far, so good.

De même, regardons les services en cours de fonctionnement :

$ microk8s.kubectl get services   # raccourci : get svc

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.152.183.1    <none>        443/TCP          5d7h
mon-app-service  ClusterIP   10.152.183.94   <none>        8080/TCP   9s

Fascinant.

Enfin, jetons un œil aux ingresses :

$ microk8s.kubectl get ingress

NAME                   CLASS    HOSTS   ADDRESS     PORTS   AGE
mon-app-http-ingress   <none>   *       127.0.0.1   80      157m

Incroyable !

Job done !

Si l’on se rend sur localhost, une fantastique application devrait alors apparaître (le port 80 de la machine hôte devait bien entendu être libre).
Si l’on s’acharne suffisamment sur le rafraîchissement de la page (ou si on utilise curl en boucle), on devrait constater que l’adresse IP indiquée change de temps à autre.
En effet, le trafic est réparti sur un pod ou l’autre (étant donné que nous avons 2 réplicats dans le déploiement).

Un point intéressant à mentionner, c’est que les ressources s’identifient par des labels (qui peuvent contenir un peu tout et n’importe quelle valeur). Par exemple, un déploiement pourrait mettre à disposition 92 réplicats à destination de pods portant le label projet: mon-app, environnement: production, alors que le les pods libellés projet: mon-app, environnement: demo ne seraient pas impactés.

Il y a des enjeux assez classiques pour une mise en cluster plus sérieuse, parmi eux, la persistance des fichiers sur les nœuds distincts par le biais de volumes.

Que ça soit Jonathan ou moi, nous vous apporterons des exemples de mise en œuvre un peu plus pertinents bien assez tôt !


L’équipe Synbioz.
Libres d’être ensemble.