Le DevOps est l’un des concepts d’agilité et d’automatisation les plus utilisés depuis maintenant quelques années et précisément depuis que Patrick Debois a introduit ce terme à l’occasion du DevopsDays à Gand en Belgique en 2009. Aujourd’hui, un autre concept fait son apparition, qui est en phase avec le DevOps : c’est le GitOps. Le GitOps est la continuité de la démarche DevOps, son point fort est d’assurer la fiabilité et la sécurité du Continuous Delivery en se focalisant sur le code de l’infrastructure et d’applications. Vous trouverez tous les détails de ce concept dans cet article avec un exemple mis en pratique vous permettant de vous convertir au GitOps sereinement.
Introduction
Un référentiel Git est un gestionnaire de révision permettant de versionner votre code. Le code peut être du code source (Java, Python, Go…) d’une application ou encore du code d’infrastructure (Terraform, Ansible, Puppet...). Qu’est-ce qu’un code d’infrastructure ?
Un code d’infrastructure connu sous le terme Infrastructure As Code (ou IaC) permet de décrire l’environnement à déployer (machines virtuelles, réseaux, load balancer, base de données…) aussi bien que la configuration correspondante.
Un code d’infrastructure peut être également des fichiers déclaratifs pour créer des ressources dans un cluster Kubernetes, ces fichiers sont appelés des manifestes. Ces fichiers doivent être versionnés pour suivre l’évolution de l’infrastructure ou encore pour assurer le déploiement continu lors d’un cycle de développement logiciel.
Des questions qui se posent : comment assurer la synchronisation entre le code d’infrastructure déclaré dans Git et le provisionning des ressources associées ? Autrement dit, dans le cas d’un cluster Kubernetes, est-ce que l’état souhaité des ressources qui existent dans le cluster coïncide avec ceux déclarés dans les manifestes publiés dans Git ?
Un changement au niveau des fichiers de déploiement dans Git, changement de version de l’image de conteneur par exemple, pourrait-il mettre à jour les conteneurs qui s’exécutent dans l’environnement Kubernetes d’une manière automatique ?
Comment assurer un déploiement continu (livraison continue) et automatisé à chaque fois qu’on pousse des fichiers de déploiement dans un dépôt Git ?
Des ressources Kubernetes peuvent être des pods, services, configmaps, secrets, IngressController, etc.
La réponse à toutes ces questions est simple : adopter une approche GitOps.
Dans cet article, je démontrerai comment remédier aux différentes problématiques citées plus haut en mettant en œuvre le concept GitOps et en s’appuyant sur un outil considéré comme le plus pertinent au moment de la rédaction de l’article : Flux CD.
1. Le GitOps, qu’est-ce que c’est ?
Le GitOps désigne un ensemble de pratiques et des concepts de gestion d’infrastructure et de configurations d’applications qui reposent sur l’utilisation de Git. En effet, Git est supposé être dans ce concept l’unique source de vérité pour la formalisation déclarative de l’infrastructure et des applications.
Comme évoqué dans l’introduction de l’article, GitOps fait en sorte que l’approvisionnement et le déploiement de l’infrastructure ou des services applicatifs sont gérés automatiquement par des requêtes pull dans Git. Le code dans Git décrit l’état de l’environnement ou de la configuration applicative, cet état doit être conforme et synchronisé avec les environnements d’infrastructure.
Bien évidemment, l’infrastructure doit pouvoir être gérée de façon déclarative. Dans le cas de ressources Kubernetes, les ressources sont déclarées dans des fichiers YAML (Yet Another Markup Language).
Tout changement dans ces fichiers YAML dans Git engendre un déploiement automatique des ressources. C’est pourquoi les outils de GitOps sont généralement dédiés pour faire du déploiement et de la livraison continue.
1.1 Les quatre piliers de GitOps
1. Écrire des configurations 100 % déclaratives
Vous commencez par écrire du code déclaratif (par exemple, des manifestes YAML) pour configurer votre infrastructure. C'est la raison pour laquelle GitOps est limité aux déploiements Kubernetes principalement. Tout dans Kubernetes peut être défini comme du code, depuis les déploiements d'applications jusqu'aux stratégies de déploiement, en passant par les équilibreurs de charge et les politiques de réseau. Et tout cela peut être défini à l'aide d'un langage déclaratif simple.
2. Stocker l'état souhaité dans Git
Vous stockez ensuite tous ces manifestes sous forme de code déclaratif dans Git. Essentiellement, l'état complet de votre système peut être créé, et même recréé en cas de sinistre, à partir d'un référentiel Git. Cela vous permettrait de déployer, ainsi que de revenir en arrière à l'aide de workflows familiers basés sur git (par exemple git revert, reset, pull requests, etc.).
3. Appliquer automatiquement les modifications approuvées
Une fois les modifications validées dans git et approuvées (à l'aide de révisions de code basées sur des pull request), elles doivent être automatiquement appliquées à l'environnement Kubernetes. Les développeurs n'ont pas besoin de s'inquiéter de l'exécution de commandes supplémentaires ou de la configuration des informations d'identification pour appliquer les modifications au cluster. Il devrait s'agir d'un processus entièrement automatisé. La mise en œuvre de ce processus pourrait se faire avec le dernier principe de GitOps.
4. Mettre en place un agent pour assurer l’état désiré et gérer des alertes en cas de divergence
Vous exécutez un agent logiciel tel que le contrôleur Flux à l'intérieur du cluster Kubernetes pour extraire en continu les modifications du référentiel Git, détecter de nouveaux commits, comparer l'état souhaité (Git) à l'état actuel (Kubernetes), puis alerter en cas de divergence, ou corrigez-le en vous assurant que l'état actuel correspond à celui désiré (en appliquant les manifestes de Git).
1.2 La biographie du GitOps
L’histoire du GitOps commence chez Weaveworks, qui a créé l’un des plugins réseau les plus populaires (plugins CNI) pour Kubernetes, appelé Weave Net. Weave Net est basé sur VXLAN, est très flexible et a été l'un des choix de plugins les plus populaires.
Avec le succès de Weave, la société a décidé d’aller au-delà de la mise en réseau de conteneurs et a pensé à résoudre des problèmes plus importants, l'un d'entre eux étant de savoir comment parvenir à une livraison continue sur Kubernetes.
La première tentative de livraison continue sur Kubernetes consistait à implémenter un proxy réseau utilisateur (niveau application), à l’instar d’un Ingress Controller, qui assure également du déploiement progressif. Ce projet a ensuite été abandonné par Weaveworks.
Leur deuxième tentative a conduit à l'approche GitOps. Étant donné que le déploiement en tant qu'objet Kubernetes n'existait pas à ce moment-là, ils ont tenté de configurer des déploiements automatisés avec des contrôleurs de réplication via une synchronisation bidirectionnelle.
Au moment de leur mise au point de l’outil Flux, qui a largement utilisé le workflow basé sur git, ils ont inventé le mot GitOps et défini ses principes.
Entre 2018 et 2019, ces principes ont été adoptés par les principaux fournisseurs de cloud, ce qui a également conduit à la création de services de déploiement managés.
Alors que Weaveworks a créé Flux CD v1, en parallèle, Intuit a proposé un autre outil vaguement basé sur des principes similaires. Ils l'ont appelé ArgoCD.
Weaveworks a ensuite créé la boîte à outils GitOps (GitOps Toolkit) en décomposant Flux en plusieurs composants, chacun étant un microservice. Il offre également une approche modulaire et extensible. C’est à cette époque que Flux v2 est né. C'était aussi leur tentative d’utiliser des technologies modernes telles que les CRD (Custom Resource Definition), Kubebuilder, etc. Avec Flux CD, vous pouviez également créer vos propres contrôleurs. Flux v2 prend également en charge les outils de gestion des manifestes tels que Kustomize et Helm v3.
1.3 Découverte de Flux CD
Flux CD est un outil de GitOps. Il permet de synchroniser les clusters Kubernetes avec les sources de configuration (comme les référentiels Git) et d’automatiser les mises à jour de la configuration lorsqu’il s’agit d’un nouveau code à déployer. C’est la définition issue du site officiel du Flux (voir référence en fin d’article).
Flux CD s’interface entre le dépôt Git et un cluster Kubernetes pour assurer une synchronisation entre les manifestes créés dans Git et les ressources provisionner dans Kubernetes et tout ça d’une manière automatisée. Flux CD est aussi qualifié comme outil de Continuous Deployment.
Il possède les critères suivants :
- Simple à mettre en œuvre : l'installation de Flux est une question de minutes avec un document d'une page (voir référence en fin d’article). Il est également facile à prendre en main, surtout si vous êtes déjà familiarisé avec les objets Kubernetes et le code YAML qui y est associé. Flux est mis en œuvre en étendant l'infrastructure Kubernetes via des ressources personnalisées (CRD) et des contrôleurs associés.
- Rapide : Flux offre une accélération des déploiements et des restaurations rapides.
- Léger : en comparaison avec certains outils de déploiement dédiés tels que Spinnaker et Harness, qui nécessitent des tonnes de ressources pour simplement installer les composants du CD, les services qui composent Flux CD sont des microservices ultralégers, déployés nativement sur Kubernetes. Et comme les composants Flux sont modulaires, vous pouvez choisir d'installer uniquement les composants qui vous intéressent.
- Sécurisé : pour assurer une livraison continue, Flux utilise les principes GitOps et offre une sécurité renforcée via :
- RBAC (Kubernetes) ;
- responsabilité (signature Git) pour la protection des données ;
- audit.
On découvrira plus de détails sur Flux dans la suite de l’article. Ne soyez pas pressé !
2. Préparation de l’environnement
Pour la suite de l’article, nous aurons besoin de quelques outils à installer. La première chose à mettre en place est le cluster Kubernetes. Me concernant, j’ai déployé un cluster Kubernetes sur DigitalOcean avec deux nœuds. Pour que vous puissiez reproduire toutes les manipulations réalisées, je vous montre comment installer Kubernetes en local en utilisant l’outil Kind.
2.1 Installation des outils (Git, Docker, kubectl) sous CentOS
L’installation de Docker dans ce paragraphe est nécessaire pour le déploiement d’un cluster Kubernetes Kind.
Installation de git :
Installation de docker :
Installation de kubectl :
Installation du client flux :
2.2 Installation de kind
Installation de la partie cliente kind :
Déploiement du cluster Kubernetes avec kind :
Vérifier que le cluster est opérationnel :
Le cluster gitops-cluster dispose d’un seul nœud qui représente à la fois un worker et un control-plane.
Pour plus de détails sur l’utilisation du kind, je vous laisse fouiller sur le site officiel : https://kind.sigs.k8s.io.
Dans la suite, je continue avec l’utilisation de mon propre cluster.
3. Installation de Flux CD
L’installation de flux est appelée bootstrap, amorçage en français. L’installation cependant nécessite quelques prérequis :
- la présence d’un cluster Kubernetes ;
- la création d’un token d’accès dans son propre dépôt GitHub, ce token est nécessaire pour la création de l’infrastructure Flux lors de l’opération d’amorçage ;
- exporter le token et le nom d’utilisateur GitHub dans les deux variables d’environnement suivantes : GITHUB_TOKEN et GITHUB_USER.
Le cluster Kubernetes est déjà installé dans le paragraphe précédent et il ne reste plus qu‘à déployer le token dans GitHub.
Pour créer ce token, il faut se connecter sur votre compte GitHub, depuis le profil aller sur Settings puis cliquer sur Developer Settings, puis dans le volet gauche de la fenêtre choisir Personal access tokens. Enfin, créer le token en attribuant un nom de votre choix et cocher les cases read:repo_hook et repo. Une fois le token créé, copiez-le dans un endroit sûr pour créer ensuite les variables d’environnement citées plus haut.
Exportez les variables dans bash comme suit :
À présent, c’est le moment d’amorcer notre Flux CD, mais avant cette étape commençons par une check-list de notre environnement :
Vérifions l’état de mon cluster Kubernetes :
Allons-y, passons l’installation de Flux dans le cluster Kubernetes :
La sortie de la commande est la suivante :
La commande bootstrap permet de :
- créer le dépôt GitHub s’il n’existe pas déjà, d’où la nécessité de créer les variables d’environnement de connexion au compte ;
- générer les manifestes de déploiement de Flux dans le dépôt git flux-infra dans le dossier clusters/dev/flux-system ;
- créer une clé de déploiement (Figure 1) dans GitHub pour la synchronisation du dépôt flux-infra avec le cluster Kubernetes, cette clé est également enregistrée comme secret dans Kubernetes.
Création des composants nommés GitOps Toolkit dans le namespace flux-system :
Chaque composant expose un CRD (Custom Resource Definition) :
Le détail de ces composants appelés contrôleurs sera évoqué dans le paragraphe qui suit.
Vérifions l’installation de FluxCD :
Le détail de ces composants appelés contrôleurs sera évoqué dans le paragraphe qui suit.
4. Les composants GitOps Toolkit
Flux est décomposé en microservices appelés contrôleurs, sachant que la version 1 de Flux était monolithique. Flux dispose de cinq contrôleurs déployés en tant que Pod dans Kubernetes, en plus de la création des CRD associés.
La figure 2 décrit l’ensemble de ces contrôleurs et des CRD correspondants.
Dans cet article, c’est le source controller et le kustomization controller qui vont être détaillés. Ce sont les contrôleurs de base pour la mise en œuvre de GitOps.
Pour obtenir plus de détails sur la totalité des composants, visitez le lien suivant : https://fluxcd.io/flux/components.
4.1 Source Controller
Le source controller est un opérateur Kubernetes, spécialisé dans l'acquisition d'artefacts à partir de sources externes telles que Git, les référentiels Helm et les buckets S3. Le contrôleur source implémente l'API source.toolkit.fluxcd.io et est considéré comme étant le composant central du GitOps Toolkit.
La figure 3 décrit l’architecture de ce contrôleur et son interaction avec l’ensemble des composants et ressources (CRD).
C’est ce contrôleur qui se charge de la synchronisation avec le dépôt Git pour refléter les changements dans les manifestes et transmettre les modifications vers le Kustomization controller.
4.2 Kustomization controller
Ce contrôleur est le composant qui va déployer les manifestes récupérés à partir des artefacts mis à disposition par le source controller après avoir passé l’étape de validation. Mis à part le déploiement des ressources, il supervise les changements dans le cluster Kubernetes (à chaque nouvelle révision du code et donc à chaque nouveau commit) pour s’assurer que l’état du cluster (applications, ressources déployées à partir de Git) respecte l’état désiré issu du dépôt Git (Desired State).
Git est le référentiel unique de vérité pour le Kustomization Controller.
Si une modification est réalisée dans le dépôt Git, le Kustomization Controller réagit en déployant les nouvelles modifications dans Kubernetes. Cette tâche s’appelle réconciliation pour établir l’état désiré dans le cluster.
La figure 4 décrit l’architecture de ce contrôleur et son interaction avec l’ensemble des composants et ressources (CRD).
5. Mise en place du workflow GitOps
Jusqu’ici on a tous les composants nécessaires pour la mise en œuvre du GitOps, permettant à nos futurs services d’être déployés dans un processus de livraison continue. Pourquoi je parle de services ? Parce qu’on déploiera un site marchand architecturé en microservices.
Notre code de déploiement (les manifestes Kubernetes) est décrit dans un dépôt Git. Ce dépôt Git sera la référence de base pour le provisionning des ressources dans Kubernetes vis-à-vis des contrôleurs Flux.
Le principe est plus simple que vous l’imaginez : le contrôleur Source Controller se synchronise périodiquement avec le dépôt Git pour détecter les éventuels changements. S’il détecte un changement issu d’un nouveau commit, il alerte le Kustomization Controller en lui fournissant un artefact (au format tar.gz) du nouveau code poussé dans Git. L’intérêt est bien sûr d’établir le desired state (reflète le changement dans Git par la mise à jour des ressources dans Kubernetes), et c’est à ce moment que l’opération de réconciliation est réalisée. Le Kustomization Controller est prêt pour appliquer les changements automatiquement.
Concrètement, comment peut-on mettre ce workflow en place ? À partir de deux étapes distinctes :
- Informer le source controller de l’existence de notre dépôt Git, pour contrôler les changements d’une manière périodique qu’on définit lors de la création de la ressource CRD GitRepository ;
- Prévenir le Kustomization controller de l’existence de ce dépôt Git pour exécuter la réconciliation avec le cluster Kubernetes après avoir récupéré les artefacts qui contiennent les nouveaux manifestes de déploiement.
Déclaration du dépôt Git dans Flux : la commande flux permet de créer une ressource de type GitRepository :
La commande est intuitive, on crée une source de type git, en fournissant l’URL du dépôt, la branche et l’intervalle de synchronisation.
Si l’on passe le paramètre –export à cette commande, on génère la ressource GitRepository :
Vérifions la création de la ressource :
Notre source a bien été créée, maintenant le source controller peut se synchroniser avec le dépôt https://github.com/imejri/argocd-example-apps.git toutes les 30 secondes.
La dernière étape maintenant est de pointer notre Kustomization controller vers cette source.
Le Kustomisation controller s’intéressera particulièrement au code dans le répertoire sock-shop du dépôt Git, d’où le paramètre path dans la commande. Il faut aussi créer le namespace sock-shop avant l’exécution de la commande.
Bravo ! La mise en place de notre workflow GitOps arrive à sa fin. Cela implique la création des ressources de notre application sock-shop (vente de chaussettes en ligne, pas mal comme site avec l’arrivée de l’hiver).
Vérifions la création des ressources dans le namespace sock-shop créé précédemment :
C’est magique, non ? Aucune commande n’a été tapée pour déployer les ressources. C’est Flux CD qui réalise la livraison continue. C’est très intéressant aussi du point de vue de la sécurité de l’infrastructure, car il n’y a aucune interaction pour les développeurs de l’application avec le cluster Kubernetes.
Pour les curieux, notre site est accessible via le front-end qui expose le port 3001 en NodePort.
L’accès à l’URL http://IP_PUBLIC:30001 permet d’accéder au site marchand et commencer notre shopping. Malheureusement, on n’est pas en période de solde pour en profiter ;-)
À présent, faisons en sorte de changer la configuration du service front-end dans Git pour voir comment Flux va assurer le desired state en mettant à jour le service au sein du cluster Kubernetes.
Changeons par exemple le type de service de NodePort à LoadBalancer. Ce qui a pour conséquence :
- de provisionner dans mon infra cloud (DigitalOcean) un load balancer (équilibreur de charge) ;
- de refléter les changements au niveau du service front-end en l’exposant en externe à l’aide de cet équilibreur de charge.
Voici le code de la ressource service mis à jour :
Comme prévu, mon équilibreur de charge est provisionné dans mon espace cloud (Figure 5).
Le service a été mis à jour :
Merci Flux pour les travaux réalisés !
Conclusion
FluxCD ne se limite pas aux fonctionnalités décrites dans cet article. Les composants notification controller permettant de créer des alertes dans plusieurs providers à l’instar de slack, et image automation controller qui met à jour les manifestes Kubernetes de déploiement avec les images récupérées à partir d’un registry sont très pratiques pour gérer la livraison d’une nouvelle version d’image de conteneur suite à un processus de Build. Ces deux contrôleurs seront évoqués dans un prochain article.
Malgré la réputation de Flux, il lui existe un concurrent : c’est ArgoCD qui nécessitera un article dédié !