Mise en place d'une chaîne d'intégration continue GitOps Cloud-Native sur OpenShift/Kubernetes

Le modèle GitOps est devenu incontournable ces dernières années, de nombreux produits autour de ce paradigme ont émergés pour répondre à ces nouveaux besoins. Red Hat, à travers sa plateforme OpenShift Container Plateform, a étoffé sa distribution grâce aux intégrations de produits open source tels que ArgoCD (OpenShift GitOps) et Tekton (OpenShift Pipelines). Dans ce blogpost, nous verrons les bénéfices liés à ces nouveaux outils et les usages associés à travers un cas pratique. Nous détaillerons les fonctionnalités essentielles de chacune des solutions et leurs complémentarités dans le cadre d’une chaîne d’intégration Cloud Native CI/CD GitOps.
Ce blogpost se décomposera en deux parties :
- Présentation des concepts fondamentaux des outils OpenShift Pipelines et OpenShift GitOps
- Mise en place desd’une chaîne d’intégration Cloud Native CI/CD GitOps sur la plateforme OpenShift
OpenShift Pipelines
Présentation
OpenShift Pipelines est une solution d’intégration et de livraison continue Cloud Native permettant de construire des pipelines CI/CD. Il repose notamment sur le projet open-source Tekton. Ce framework CI/CD Kubernetes native permet d’automatiser les déploiements sur plusieurs plateformes (OpenShift, Kubernetes, Machine virtuelle, serverless…).
Cloud-Native CI/CD
Un pipeline CI/CD Cloud Native repose sur les 3 piliers suivants :

- Les conteneurs : Conçu pour les applications conteneurisées et fonctionnant sur Kubernetes,
- Serverless : Fonctionne sans moteur de CI/CD à gérer et à maintenir (exemple : GitLab CI, Serveur Jenkins, Bitbucket…),
- DevOps : Conçu en tenant compte des microservices et des équipes distribuées.
Les caractéristiques de Tekton
Voici les principales caractéristiques :

- Personnalisable : Les entités Tekton sont entièrement personnalisables ce qui permet aux Ingénieurs DevOps la création d’un catalogue varié et la mise à disposition des développeurs,
- Réutilisable : les ressources sont portables et peuvent être distribuées à différentes équipes au sein de l’organisation,
- Extensible : Un catalogue et un Hub (Tekton Hub) sont à disposition,
- Normalisé : Tekton s’installe et s’exécute comme une extension de votre cluster Kubernetes à l’aide d’un opérateur,permettant la gestion des objets Tekton comme n’importe quel objet Kubernetes,
- Mise à l’échelle : Pour augmenter les resources liées à vos workloads de CI/CD Cloud Native, il suffit simplement d’ajouter des nœuds à votre cluster sans avoir à modifier les allocations de ressources ou de modifier les pipelines.
Le projet Tekton
Le projet Tekton se compose de deux entités Tekton Pipelines et Tekton Triggers.
- Tekton Pipelines est un composant définissant un ensemble de CustomResource (CR) Kubernetes permettant de construire un pipeline CI/CD cloud-native.
- Tekton Triggers est un autre composant permettant de détecter et d’extraire des informations d’évènements de diverses sources mais aussi d’instancier/exécuter des pipelines.
Ces deux composants sont complémentaires pour la mise en place d’une chaîne d’intégration complète.
Tekton Pipelines
Le projet Tekton Pipelines est composé de plusieurs types de CustomResource Kubernetes:
- Task
- Step
- Pipeline
- TaskRun
- PipelineRun
Voici l’arborescence hiérarchisée des CustomResource OpenShift Pipelines :

Task
Les Tasks sont les plus petits éléments qui composent le projet Tekton Pipelines. Elles représentent la définition/déclaration d’une unité de travail à exécuter. On peut notamment citer comme exemple de Task le packaging d’un projet Java, le build d’un conteneur ou encore le scan de vulnérabilité d’une image de conteneur.
Les Tasks possèdent des Input et Output de paramètres afin de pouvoir être dynamiques. Il est aussi possible de configurer des Workspaces pour partager les données entre différentes Tasks. Celles-ci peuvent s’exécuter indépendamment d’un pipeline. Il est aussi possible de de faire appel à des Tasks prédéfinies, développées par la communauté.
Pour réaliser les Tasks, nous devons définir une liste de Steps à exécuter séquentiellement.

Step
Les Steps permettent d’exécuter des actions à l’aide de commandes ou scripts dans les conteneurs (pods). Ils ont l’avantage de reprendre les caractéristiques des paramètres Kubernetes que l’on connait déjà :
- Image
- Variable d’environnement
- Volume
- ConfigMap
- Secret
- …
Un exemple de Step dans une Task permettant de packager un projet Java est la définition de la version du projet, ou encore la création du jar.
|
Pipeline
Le Pipeline définit l’ordre d’exécution des Tasks. A l’instar d’une Task, il représente la définition/déclaration d’une suite de Tasks. Il a notamment pour rôle d’orchestrer l’exécution des Tasks à l’aide de conditions, de relancer les Tasks ainsi que de définir des Inputs, Outputs et les Workspaces permettant le partage de données entres les Tasks.
Voici un schéma illustrant l’orchestration de Tasks lors de la mise en place d’un pipeline :

TaskRun
La TaskRun représente l’instanciation/l’exécution d’une Task. Elle est exécutée en tant que pods, sur le cluster Kubernetes. Elle référence une Task spécifique ou permet la déclaration d’une Task directement dans la TaskRun.
La TaskRun fournit des données aux Tasks :
- Paramètres
- Ressources
- Service Account
- Workspace
Interaction entre les différentes CustomResource Kubernetes lors de l’execution d’une Task :

PipelineRun
Le PipelineRun permet l’instanciation/l’exécution d’un Pipeline. Elle référence un Pipeline spécifique ou permet la déclaration d’un Pipeline directement dans le PipelineRun.
Au même titre qu’une TaskRun, le PipelineRun fournit des données aux Pipelines :
- Parametres
- Ressources
- Service Account
- Workspace
Interaction entre les différentes entités Kubernetes lors de l’execution d’un Pipeline :

Exemple d’instanciation de PipelineRun à partir de pipelines à différentes heures :

Tekton Triggers
Le projet Tekton Triggers est composé de plusieurs types de CustomResource Kubernetes :
- Event Listener
- Trigger
- Interceptor
- TriggerBinding
- TriggerTemplate

Event Listener
L’EventListener est une ressource déployée dans le cluster Kubernetes permettant d’écouter les événements d’une application tierce (webhook). Cette ressource permet de détecter et de transmettre l’évènement à un ou plusieurs Triggers référencés.
Trigger
Le Trigger permet de spécifier sur quel type d’évènement le pipeline sera déclenché. Par exemple, le push d’un nouveau commit sur le gestionnaire de dépôt, l’approbation d’une merge request… Un Trigger spécifie des CustomResource de type TriggerTemplate, un TriggerBinding et généralement un parmamètre interceptor.
Interceptor
Le Trigger spécifie un interceptor permettant de filtrer les données utiles, de sécuriser les échanges à l’aide d’un secret (token), de transformer les meta données ainsi que de définir et de tester les conditions de déclenchements. Voici la liste des interceptors :
- Webhook
- GitHub
- GitLab
- BitBucket
- CEL
- …
TriggerBinding
La ressource TriggerBinding permet de récupérer les données interceptées et transformées par le Trigger et de les transmettre au TriggerTemplate.
TriggerTemplate
Le TriggerTemplate spécifie un modèle pour les CustomResource TaskRun ou PipelineRun qui permet d’instancier/exécuter lorsque l’EventListener détecte un évènement. Il permet d’exposer les paramètres récupérés dynamiquement lors de l’évènement et de les utiliser pour exécuter le PipelineRun/TaskRun.
Voici le schéma de d’exécution d’un workflow classique d’Intégration Continue (CI) :

OpenShift GitOps
Présentation
OpenShift GitOps est une solution de livraison continue (CD) basée sur le modèle déclaratif pour Kubernetes/OpenShift reposant sur le projet open source ArgoCD. Il permet la gestion de la configuration d’infrastructure ainsi que les mises à jour des applications dans un gestionnaire de dépôt Git.
Les 4 piliers fondamentaux
Le modèle GitOps repose sur 4 piliers fondamentaux :
- L’approche déclarative : l’ensemble du système est décrit de manière déclarative,
- Le dépôt Git comme unique source de vérité : les changements déclaratifs vous permettent de considérer les modifications comme des transactions permettant le versionnement, la traçabilité ou le contrôle d’accès,
- Un opérateur Kubernetes : les changements approuvés de l’état souhaité sont automatiquement appliqués au système grâce à l’opérateur qui scrute en temps réel le dépôt Git,
- L’observabilité continue : les agents logiciels garantissent l’exactitude et alertent en cas de divergence.

Méthode Push vs Pull
Lorsqu’on évoque le GitOps il existe deux types d’approches. La méthode classique de type Push et celle de type Pull davantage associée au GitOps.
Pour avoir une description détaillées des deux types d’approches et leurs spécificités, vous pouvez vous référer à un article précédemment écrit sur le blog SoKube GitOps and the Millefeuille dilemma.
Le projet ArgoCD
Le projet ArgoCD est composé de plusieurs types de ressources :
- AppProject
- Application
- ApplicationSet
Dans le cadre de ce blogpost nous utiliserions essentiellement les ressources AppProject et Application .
AppProject
L’AppProject représente un groupement logique d’applications. Cette CustomResource Kubernetes permet notamment de définir depuis quel dépôt Git les manifestes peuvent être récupérés, dans quel cluster et namespace les applications doivent être déployées, une section RBAC permettant d’établir un contrôle d’accès aux ressources Kubernetes et enfin quel type d’objet Kubernetes peut être créé.
Application
Une Application dans ArgoCD représente une application déployée dans un environnement au sein du cluster Kubernetes. Elle permet notamment de spécifier la configuration Git avec l’URL du dépôt, la branche, le dossier ou l’environnement afin de récupérer et de déployer les manifestes Kubernetes. Il est aussi possible de configurer d’autres paramètres comme la destination de déploiement (cluster et namespace), la politique de synchronisation ou encore le type de déploiement (manifests natifs, Helm ou Kustomize).
Pipeline GitOps Cloud-Native
Après expliqué les concepts liés à OpenShift Pipelines et OpenShift GitOps dans la première partie de ce blogpost, nous allons dans cette seconde section, mettre en évidence l’interaction entre ces différents outils. L’objectif consistera à construire un pipeline CI/CD GitOps Cloud Native pour le déploiement d’une application appelée Fruitz. Celle-ci est composée de deux microservices :
- Un backend développé avec en Java Quarkus : Fruit Quarkus,
- Un frontend reposant sur la technologie Angular.
Pour cette démonstration le pipeline GitOps Cloud-Native sera uniquement réalisé autour du microservice Java fruitz-quarkus. Il sera composé d’une CI (Intégration Continue) standard orchestrée par OpenShift Pipelines (Tekton) et d’une CD (Déploiement Continu) gérée par OpenShift GitOps (ArgoCD). Dans le cadre de cette démonstration, nous avons volontairement introduit une limitation (bug) applicative sur le backend Java fruitz-quarkus. L’objectif est de corriger cette limitation à l’aide du pipeline CI/CD GitOps Cloud-Native ainsi que d’en démontrer l’ensemble des bénéfices.
Pour respecter le modèle GitOps le code applicatif et celui de déploiement seront hébergés dans deux repositories distincts sur la plateforme GitLab.com. Le repository GitOps de déploiement Fruit-Deploy utilisera le package manager Helm pour déployer l’application au sein du cluster OpenShift.
Workflow d’un pipeline CI/CD GitOps :

Installation des outils
Installation d’OpenShift Pipelines et OpenShift GitOps
Cette démonstration sera orchestrée autour de la plateforme OpenShift. Vous aurez besoin d’avoir un cluster OpenShift fonctionnel avec les permissions de cluster-admin. Si vous souhaitez déployer un cluster OpenShift sur AWS vous pouvez vous référer à l’article Comment déployer un cluster Openshift dans AWS sur le blog SoKube.
Se connecter à la console OpenShift et installer les opérateurs RedHat OpenShift GitOps et RedHat OpenShift Pipelines :

Installation StorageClass
Afin de pouvoir provisionner des volumes (PersitentVolume) dynamiquement lors de l’exécution des pipelines Tekton, mais aussi pour déployer les applications, vous pouvez, à des fin de tests, déployer et utiliser le projet Kubernetes NFS Subdir External Provisioner qui permettra de créer une StorageClass reposant sur du NFS. Dans notre cas nous utiliserons la StorageClass appelée nfs-client.
StorageClass nfs-client:
|
OpenShift GitOps
Après l’installation d’OpenShift Pipelines à travers la console, nous allons maintenant déployer l’AppProject et l’Application ArgoCD. Créez les manifests suivants :
AppProjet fruitz-deployment:
|
Application fruitz-helm :
|
Pour permettre à OpenShift GitOps d’accéder au repository dans lequel est hébergé le code, si celui-ci ne dispose pas d’une visibilité publique, il est alors nécessaire de créer un Secret Kubernetes avec la clé privée permettant à ArgoCD d’y accéder. Entrez la commande suivante :
Secret private-repo :
|
ℹ️ Pensez bien à mettre votre clé privée SSH.
Depuis la console OpenShift, cliquez sur le petit menu, puis sélectionnez Cluster ArgoCD afin d’arriver sur l’application ArgoCD :

OpenShift GitOps dispose d’une intégration SSO utilisant celle déjà intégrée dans OpenShift. Sur la page d’authentification d’OpenShift GitOps sélectionnez LOG IN VIA OPENSHIFT afin d’utiliser le SSO et authentifiez-vous :

Vous arrivez sur la page des applications ArgoCD, nous pouvons constater que l’application fruitz-helm est présente ainsi qu’en état Synced (synchronisée) :

Vous pouvez cliquer sur l’application afin d’obtenir une vue d’ensemble de tous les objets déployés :

Sur l’objet fruitz-front-ingress tout en bas de la page, vous pouvez cliquer afin d’ouvrir la page web du frontend de l’application :

Vous êtes à présent sur la page du frontend de l’application. Vous pouvez tester que celle-ci fonctionne correctement avec l’ajout du fruit Pear. Cependant, l’ajout de tous les fruits ne fonctionne pas. En effet, nous pouvons aussi essayer d’ajouter le fruit Pineapple, et constater que ce fruit aussi n’est pas ajouté. Ce comportement/bug est bien entendu prévu. C’est notamment celui-ci qui met en evidence le bénéfice de l’association des outils ArgocCD et Tekton avec la mise en place du pipeline GitOps Cloud Native corrigeant le bug/comportement volontairement introduit.
Nous ajoutons le fruit Pear qui apparait maintenant sur l’interface web. Nous essayons aussi d’ajouter le fruit Pineapple mais l’ajout de celui-ci ne fonctionne pas et n’apparait donc pas sur l’interface web :

Après l’échec de l’ajout du fruit Pineapple nous pouvons analyser la stack trace en interrogeant les logs du pod backend. Nous constatons l’erreur suivante ERROR: value too long for type character varying(6). La taille de la chaîne de caractères est trop longue :

En effet, la taille de la chaîne de caractères est limitée à 6. Cela correspond au code en surbrillance jaune :

Nous avons explicitement spécifié la contrainte que la chaîne de caractères pour le nom des fruits ne pouvait pas dépasser 6 caractères.
OpenShift Pipelines
Après avoir démontré que notre application était fonctionnelle mais volontairement limitée, nous allons dans cette partie, montrer comment avoir une chaîne d’intégration complète permettant au développeur d’accélérer le développement de son application. La création d’un pipeline d’Intégration Continue (CI) directement depuis le commit d’une nouvelle fonctionnalité grâce à Tekton et déployer la nouvelle image dans le cluster OpenShift grâce à ArgoCD.
Tekton Trigger
Créez le projet (namespace) OpenShift cicd :
|
- Un EventListener: cette ressource permet de créer un point d’entrée sur notre cluster OpenShift afin de déclencher les pipelines,
- Un TriggerBinding: cette ressource permet de faire la liaison entre les metadatas récupérées et les transmettre aux pipelines,
- Un Trigger: ressource permettant de définir le type d’intercepteur (ex: GitLab, GitHub …) mais aussi sur quel type d’évènement déclencher les pipelines mais aussi d’extraire et transformer sur les metadatas récupérées
- un secret utilisé par le Trigger contenant le token qui sera associé au webhook permettant l’authentification,
- Un TriggerTemplate permettant de variabiliser le pipeline qui sera déclenché.
EventListener gitlab-listener-interceptor:
|
La création de cette ressource entraîne la création d’une Route OpenShift. Vous aurez besoin du host lié à cette route lors de la création d’un webhook. Pour récupérer le host lié à à la route, entrez la commande suivante :
|
Le résultat est le suivant :
|
TriggerBinding gitlab-triggerbinding:
|
Trigger gitlab-listener:
|
Le secret associé gitlab-trigger-secret:
|
ℹ️ Ce secret devra aussi être utilisé lors de la création du webhook sur GitLab.
TriggerTemplate gitlab-triggertemplate :
|
Après avoir créé les différentes ressources Kubernetes, nous devons créer un webhook sur le repository applicatif Java fruitz-quarkus qui permettra de prendre en compte les modifications en fonction du type d’évènement et de les transmettre à Tekton. Dans notre cas, lors du push d’un nouveau commit sur le repository fuitz-quarkus un pipeline d’Intégration Continue (CI) sera automatiquement déclenché.
Pour rappel, nous utilisons le SCM GitLab. Dans le projet, veuillez vous rendre dans partie latérale et cliquer sur Settings ► Webhook :

Puis entrez les informations suivantes:
- URL:
https://el-gitlab-listener-interceptor-cicd.apps.ocp-dev.infrasokube.io - Secret Token:
abcdefghijklmnopqrstuvwxyz0123456789ABCDEF - Trigger:
- Push events
- All branches
- Push events
- SSL verification
- Enable SSL verification
Le champ URL représente l’URL de la route OpenShift qui est créée par l’objet Tekton EventListener. C’est l’URL sur laquelle Tekton Trigger écoute et réceptionne les webhooks envoyés par GitLab. Elle permet notamment de déclencher les pipelines mais aussi la transmission des informations et des meta données à Tekton.
- Le champ Secret Token permet l’authentification lors du webhook envoyé,
- Le champ Trigger permet de définir sur quel type d’évènement les webhooks seront déclenchés,
- Le champs SSL, quant à lui, permet de vérifier la terminaison TLS de l’URL.

Tekton Pipelines
Configuration
Tasks
Nous devons maintenant créer les tâches qui composeront le pipeline de d’Intégration Continue (CI) applicatif. Notre pipeline sera simple et composé de tâches classiques :
- Le packaging du projet Java Quarkus et la création de l’artefact Java (.jar)
- Le build de l’image Docker avec le package Java Quarkus et le push de l’image buildée dans un registre de conteneur
- Un scan de vulnérabilité de l’image Docker
- La mise à jour du repository Helm de déploiement qui permettra de redéployer l’application avec la correction du bug applicatif.
Vous devrez créer les manifests suivants :
Task maven-package :
|
Task container-image-build-push :
|
Task scan-trivy :
|
Task update-helm-deployment-repository :
|
Vous devez aussi créer les secrets associés:
Le Secret gitlab-com-ssh-key qui permettra à Tekton d’opérer votre repository GitLab privé :
|
ℹ️ Pensez bien à mettre votre clé privée SSH.
Le Secret gitlab-com-docker-registry-token qui permettra à Tekton d’opérer votre registre de conteneurs GitLab :
|
ℹ️ Pensez bien à renseigner le champ
stringData.passworden générant un token sur GitLab.
Le Secret trivy-registry-gitlab-com-credentials qui permettra quant à lui d’autoriser l’outil Trivy à analyser directement les images hébergées dans votre registre d’image GitLab :
|
ℹ️ Pensez bien à renseigner le champ
stringData.gitlab-registry-tokenen générant un token sur GitLab.
Lors de la création du Pprojet (namespace)* sur OpenShift, un ServiceAccount pipeline est créé par défaut avec l’ensemble des permissions nécessaires pour executer les différentes tasks. Pour associer les secrets créés précédemment au ServiceAccount Tekton et permettre l’utilisation des secrets durant le pipeline, il est nécéssaire de patcher le ServiceAccount à l’aide des commandes suivantes :
|
Vous pouvez maintenant vous rendre dans la console OpenShift et cliquer sur Pipeline ► Tasks afin de voir les tasks créées :

Pipeline
Après avoir créé les ressources de type Tasks nous allons maintenant créer la ressource de type Pipeline qui nous permettra d’orchestrer l’exécution des Tasks avec notamment l’ordre d’exécution ou entre autre leur conditionnalité d’exécution.
Pipeline fruitz-quarkus :
|
Vous pouvez maintenant vous rendre dans la console OpenShift et cliquer sur Pipelines ► Pipelines ► Pipelines afin de voir le pipeline créé :

Vous pouvez aussi cliquer directement sur le pipeline fruitz-quarkus afin d’avoir plus d’informations sur celui-ci comme le nombre de Tasks liées, les Workspaces, les lLbels associés…

✅ La configuration Tekton est maintenant terminée.
Exécution du pipeline
Nous allons maintenant corriger le bug applicatif limitant l’ajout de fruits dont le nombre maximal de caractères ne peut dépasser 6. Nous étendrons cette limite à 9 caractères afin de pouvoir ajouter le fruit Pineapple.
Sur le repository applicatif fruitz-quarkus (backend), éditez le fichier src/main/java/org/sokube/hibernate/orm/Fruit.java pour augmenter le nombre maximal de caractères à 9 :

ℹ️ On peut constater que le commit ID est le suivant
157dbedb
Après avoir commité et poussé les modifications sur le repository applicatif fruitz-quarkus, nous pouvons constater que le pipeline Tekton a bien été déclenché et exécuté avec succès :


Il est possible d’avoir plus de détails en cliquant sur le Pipeline :

Nous pouvons aussi avoir une vue détaillée des différentes Tasks avec l’exécution des Steps :

Nous pouvons voir que l’image a bien été poussée dans la conteneur registry GitLab avec le bon tag (commit ID 157dbedb) :

Sur ArgoCD, nous constatons que le pod fruitz-backend a bien été redéployé avec la nouvelle image et le bon tag (commit ID 157dbedb) :


Cela a été rendu possible car le pipeline Tekton et notamment la Task update-helm-deployment-repository à mis à jour le repository GitOps de déploiement synchronisé avec ArgoCD.

Pour terminer nous pouvons tester la nouvelle fonctionnalité sur l’application. Nous pouvons voir qu’il est maintenant possible d’ajouter le fruit Pineapple :

Dans ce blog post nous avons pu voir comment mettre en place une chaîne d’intégration GitOps Cloud Native sur la plateforme OpenShift. Cet article présente les outils, comment les associer ainsi que comment les prendre rapidement en main. Bien entendu, il s’agit d’une démonstration pour une expérimentation rapide, de nombreuses choses supplémentaires doivent être prises en compte dans un contexte d’entreprise comme notamment, la gestion des secrets, la partie RBAC autour d’OpenShift GitOps ou encore les Steps composant le pipeline Tekton (ajout de tests, définition scanner les vulnérabilités pour les dépendances applicatives, pousser l’image dans un registry dédiée uniquement si le scan de vulnérabilité est négatif…).
Dans un prochain blogpost nous montrerons comment mettre en place le plugin ArgoCD Image Updater. Cet outil qui s’intègre parfaitement à ArgoCD, permet d’avoir un pipeline GitOps encore plus Cloud Native. Effectivement, il permet de se soustraire de la Task Tekton update-helm-deployment-repository qui vient commiter dans le repository de déploiement. Cette partie sera entièrement gérée par l’outil ArgoCD Image Updater.
Pour aller plus loin, découvrez notre formation ArgoCD et GitOps et nos consultants DevSecOps.