Est-il encore approprié d'employer le terme GitOps si vous ne déployez plus directement depuis un dépôt Git ? Git reste au cœur des workflows GitOps, c'est indéniable. Pourtant, de plus en plus d'équipes de développement choisissent de stocker leurs configurations d'infrastructure sous forme d'artefacts OCI (Open Container Initiative) dans des registres de conteneurs. Cette pratique gagne du terrain en tant que nouvelle source unique de vérité pour les déploiements GitOps. Découvrez dans cet article les raisons qui motivent cette tendance et comment les fonctionnalités de GitLab soutiennent cette amélioration des workflows GitOps.
Qu'est-ce que la pratique GitOps ?
Le projet OpenGitOps définit quatre principes qui encadrent la pratique GitOps :
- Un système géré par GitOps doit exprimer son état désiré de manière déclarative.
- Cet état désiré doit être stocké dans un système garantissant l'immuabilité et la gestion des versions, et conserver un historique complet des versions.
- Des agents logiciels doivent pouvoir effectuer automatiquement un pull des déclarations de l'état désiré depuis la source.
- Ils doivent surveiller en continu l'état réel du système et tenter de le faire converger vers l'état désiré.
Un exemple concret de pratique GitOps consiste à stocker les manifestes Kubernetes d'un microservice dans un projet GitLab. Ces ressources Kubernetes sont ensuite continuellement synchronisées par un contrôleur s'exécutant sur le cluster Kubernetes hébergeant le microservice. Ainsi, les équipes de développement sont en mesure de gérer l'infrastructure avec les mêmes workflows que pour du code applicatif : ouverture de merge requests afin d'examiner et de valider les modifications et gestion des versions. La pratique GitOps présente également de nombreux avantages opérationnels. Par exemple, elle permet de prévenir la dérive de configuration et facilite l'audit des modifications ayant conduit à un certain état du système ou à un incident lors des déploiements.
Avantages et limites de Git dans les workflows GitOps
Git est sans conteste un composant essentiel des workflows GitOps. Cependant, les dépôts Git n'ont pas été conçus pour être utilisés directement par des contrôleurs GitOps lors des déploiements. Git offre aux équipes de développement la possibilité de collaborer sur les modifications d'infrastructure et d'auditer ces dernières ultérieurement, mais les contrôleurs GitOps n'ont pas besoin de récupérer l'intégralité d'un dépôt Git pour réussir un déploiement. Il leur suffit d'accéder aux définitions d'infrastructure spécifiques à un environnement donné.
De plus, un autre aspect important du processus de déploiement consiste à signer et vérifier les déploiements pour garantir que les modifications en lien avec un déploiement, apportées à un environnement, proviennent d'une source de confiance. Bien que les commit Git puissent effectivement être signés et vérifiés par les contrôleurs GitOps, ils peuvent également inclure des éléments sans lien direct avec le déploiement (par exemple, des modifications de documentation, des mises à jour pour d'autres environnements, ou une restructuration du dépôt Git) ou offrir une image de déploiement incomplète, car un déploiement peut être constitué de plusieurs commits. Là encore, il semble que cette fonctionnalité Git ne soit pas en adéquation avec les exigences spécifiques du déploiement continu.
Les workflows GitOps basés sur Git peuvent parfois conduire à un excès d'automatisation, ce qui peut être problématique. Par exemple, dès qu'une modification est fusionnée dans une branche surveillée, elle est déployée immédiatement. Il n'y a aucun mécanisme de contrôle dans le processus en dehors de Git. Comment avoir la certitude d'empêcher le déclenchement d'un déploiement un vendredi en fin d'après-midi ? Que se passe-t-il si les équipes responsables du déploiement ne disposent pas des autorisations nécessaires pour fusionner les modifications dans certains projets GitLab ? L'utilisation d'images OCI ajoute une étape intermédiaire au processus : un pipeline intégré au workflow. Celui-ci inclut toutes les fonctionnalités nécessaires au contrôle de la livraison, telles que des approbations ou le gel des déploiements.
Les images OCI
L'Open Container Initiative a contribué à définir des normes relatives aux formats de conteneurs. Bien que la plupart des équipes de développement aient l'habitude de créer des images de conteneurs à partir d'un fichier Dockerfiles, beaucoup ne sont peut-être pas aussi familiers avec le stockage des manifestes Kubernetes dans un registre de conteneurs. Étant donné que le registre de conteneurs de GitLab est conforme à la norme OCI, il permet aux utilisateurs d'effectuer le push des manifestes Kubernetes liés à un environnement particulier directement dans le registre de conteneurs. Au lieu de cloner un dépôt Git complet, les contrôleurs GitOps, tels que Flux CD, peuvent désormais récupérer uniquement l'artefact OCI contenant les manifestes nécessaires au déploiement.
Dans de nombreux workflows GitOps, un même dépôt Git regroupe les configurations d'infrastructure pour tous les environnements cibles dans lesquels un microservice sera déployé. En empaquetant uniquement les manifestes Kubernetes liés à un environnement spécifique dans une image OCI, Flux CD télécharge les fichiers strictement nécessaires au déploiement dans cet environnement spécifique.
Les atouts sécurité des artefacts OCI
Comme mentionné précédemment, signer et vérifier les artefacts à déployer dans un environnement ajoutent une couche de sécurité supplémentaire aux projets de développement logiciel. Une fois que les manifestes Kubernetes font l'objet d'un push dans un registre de conteneurs, un outil comme Sigstore Cosign peut être utilisé pour signer l'image OCI avec une clé privée, stockée en toute sécurité dans un projet GitLab en tant que variable CI/CD. Flux CD peut ensuite utiliser une clé publique stockée sur un cluster Kubernetes pour vérifier et garantir que le déploiement provient d'une source fiable.
Push et signature des images OCI avec GitLab
GitLab offre de nombreuses fonctionnalités qui simplifient le processus d'empaquetage, de signature et de déploiement d'images OCI dans des workflows GitOps. Une pratique courante consiste à structurer les projets GitLab en séparant le code des microservices d’un côté, et le dépôt de l’infrastructure de déploiement de l’autre. Ainsi, pour une application composée de n
microservices, il est fréquent d'avoir n +1
projets GitLab.
Le projet de code produit généralement un artefact sous forme d'image de conteneur qui sera utilisée pour empaqueter l'application. Le projet d'infrastructure (aussi appelé projet de livraison) contient, quant à lui, les manifestes Kubernetes définissant toutes les ressources nécessaires pour que chaque microservice puisse monter en charge, recevoir et traiter le trafic des utilisateurs. L'artefact découlant de ce projet est généralement une image OCI servant à déployer l'application et d'autres manifestes sur Kubernetes.
Dans cette configuration, la séparation des environnements est gérée en définissant des manifestes Kubernetes dans des dossiers distincts représentant les environnements (par exemple, de développement, de préproduction et de production) qui hébergeront l'application. Lorsque des modifications sont apportées au code d'un microservice et qu'une nouvelle image de conteneur fait l'objet d'un push, il suffit alors de mettre à jour la référence de cette image dans le manifeste situé dans le dossier correspondant à l'environnement cible, puis de créer une merge request pour déployer ces modifications via l'intégration de GitLab avec Flux CD. Une fois cette merge request examinée, approuvée et fusionnée, le job CI/CD du projet de livraison déclenche le push d'une nouvelle image OCI que Flux CD récupérera et déploiera dans le nouvel environnement.
Pour signer une image OCI, il vous suffit d'intégrer Cosign dans le job CI/CD de votre projet et de générer une nouvelle paire de clés publique/privée avec Cosign en exécutant localement les commandes ci-dessous. Veillez simplement à vous connecter à votre instance GitLab via l'interface de ligne de commande glab et remplacez la variable [PROJECT_ID
] dans la commande Cosign par l'ID de votre projet de livraison.
glab auth login
cosign generate-key-pair gitlab://[PROJECT_ID]
Une fois la commande Cosign exécutée avec succès, les clés Cosign seront ajoutées à votre projet dans la section des variables CI/CD, sous les noms de clés COSIGN_PUBLIC_KEY
et COSIGN_PRIVATE_KEY
.
Exemple de job CI/CD
Voici à quoi ressemblera un job GitLab CI/CD permettant d'effectuer un push d'une image OCI :
frontend-deploy:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
paths:
- manifests/dev/frontend-dev.yaml
trigger:
include:
- component: gitlab.com/components/fluxcd/[email protected]
inputs:
version: 0.3.1
kubernetes_agent_reference: gitlab-da/projects/tanuki-bank/flux-config:dev
registry_image_url: "oci://$CI_REGISTRY_IMAGE/frontend"
image_tag: dev
manifest_path: ./manifests/dev/frontend-dev.yaml
flux_oci_repo_name: frontend
flux_oci_namespace_name: frontend-dev
signing_private_key: "$COSIGN_PRIVATE_KEY"
Le catalogue GitLab CI/CD comprend un composant CI/CD maintenu par GitLab pour travailler avec les artefacts OCI et Flux CD. Ce composant permet aux équipes de développement d'effectuer un push des manifestes Kubernetes sous forme d'images OCI vers le registre de conteneurs de GitLab ou dans un registre de conteneurs externe, de signer ces image OCI à l'aide de Cosign et d'en déclencher immédiatement la synchronisation via Flux CD.
Dans l'exemple ci-dessus, le component
Flux CD est inclus dans un fichier .gitlab-ci.yml
d'un projet GitLab. À l'aide aux paramètres inputs
du composant, les utilisateurs peuvent définir le registre de destination pour l'image (registry_image_url
et image tag
), le chemin d'accès aux manifestes Kubernetes qui feront l'objet d'un push (manifest_path
), la clé privée Cosign utilisée pour signer l'image (signing_private_key
), ainsi que l'espace de nommage Kubernetes et le nom de l'OCIRepository de Flux CD nécessaires pour synchroniser les mises à jour vers l'environnement de déploiement (flux_oci_namespace_name
et flux_oci_repo_name
).
La variable kubernetes_agent_reference
permet aux jobs GitLab CI/CD d'hériter automatiquement de la variable kubeconfig
nécessaire pour accéder à un cluster Kubernetes, sans avoir à stocker manuellement une variable CI/CD kubeconfig
dans chaque projet GitLab. En configurant GitLab Agent for Kubernetes, vous pouvez configurer les jobs CI/CD de tous les projets GitLab dans un groupe GitLab et faire en sorte qu'ils héritent des autorisations nécessaires pour déployer sur le cluster Kubernetes.
Vous pouvez configurer le contexte de GitLab Agent for Kubernetes au même endroit que la configuration de GitLab Agent for Kubernetes dans votre groupe GitLab. Il est toutefois recommandé d'effectuer ces modifications dans le projet qui gère Flux CD. Pour en savoir plus sur la configuration de GitLab Agent pour l'accès CI/CD, consultez notre documentation dédiée au workflow CI/CD.
Par ailleurs, les variables $COSIGN_PRIVATE_KEY
, $FLUX_OCI_REPO_NAME
, et $FRONTEND_DEV_NAMESPACE
sont des variables CI/CD qui contiennent des données sensibles. Elles sont stockées tout en masquant leur contenu dans les logs CI/CD. Enfin, la variable $CI_REGISTRY_IMAGE
est disponible par défaut dans tous les jobs GitLab et spécifie le registre de conteneurs associé au projet GitLab.
Déploiement des images OCI
En intégrant Flux CD à vos projets GitLab, vous pouvez automatiser les déploiements ainsi que la vérification des signatures pour les environnements de vos microservices. Une fois que Flux CD est configuré pour se synchroniser avec un projet GitLab, vous pouvez simplement ajouter les définitions de ressources personnalisées Kubernetes suivantes à votre projet pour synchroniser votre image OCI dont le push a été effectué.
apiVersion: v1
kind: Namespace
metadata:
name: frontend-dev
labels:
name: frontend-dev
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: cosign-public-key
namespace: frontend-dev
spec:
encryptedData:
cosign.pub: AgAKgLf4VbVzJOmr6++k81LlFayx88AELaUQFNOaXmBF4G+fBfBYeABl0skNvMAa1UrPVNSfMIHgFoYHoO96g576a+epk6V6glOI+++XvYbfsygof3GGxe0nL5Qh2b3ge0fNpyd0kTPSjTj0YUhRhKtMGMRSRw1jrwhNcGxCHK+Byibs52v8Np49KsIkeZKbzLdgYABkrv+k0j7hQM+jR180NpG+2UiRvaXpPuogxkbj61FEqWGrJHk8IVyfl3eh+YhoXxOHGDqko6SUC+bUZPDBlU6yKegO0/8Zq3hwulrSEsEjzRZNK+RFVMOLWWuC6h+WGpYhAMcsZPwjjJ/y29KLNa/YeqkN/cdk488QyEFc6ehCxzhH67HxIn2PDa+KkEOTv2TuycGF+Q00jKIizXF+IwLx/oRb3pTCF0AoAY8D8N3Ey+KfkOjsBON7gGID8GbQiJqX2IgIZxFMk0JRzxbRKOEqn+guLd5Shj7CD1a1Mkk0DxBdbqrGv2XNYUaFPI7xd3rZXUJZlnv+fsmwswsiGWRuXwim45HScWzQnfgLAe7tv3spVEGeaO5apl6d89uN21PBQnfE/zyugB//7ZW9tSp6+CSMyc5HynxI8diafqiwKPgvzLmVWRnkvxJijoXicRr3sCo5RudZPSlnjfd7CKdhwEVvLl7dRR4e/XBMdxCzk1p52Pl+3/kJR+LJii5+iwOpYrpVltSZdzc/3qRd19yMpc9PWpXYi7HxTb24EOQ25i21eDJY1ceplDN6bRtop2quzkjlwVeE2i4cEsX/YG8QBtQbop/3fjiAjKaED3QH3Ul0PECS9ARTScSkcOL3I00Xpp8DyD+xH0/i9wCBRDmH3yKX18C8VrMq02ALSnlP7WCVVjCPzubqKx2LPZRxK9EG0fylwv/vWQzTUUwfbPQZsd4c75bSTsTvxqp/UcFaXA==
template:
metadata:
name: cosign-public-key
namespace: frontend-dev
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: frontend
namespace: frontend-dev
spec:
interval: 1m
url: oci://registry.gitlab.com/gitlab-da/projects/tanuki-bank/tanuki-bank-delivery/frontend
ref:
tag: dev
verify:
provider: cosign
secretRef:
name: cosign-public-key
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: frontend
namespace: frontend-dev
spec:
interval: 1m
targetNamespace: frontend-dev
path: "."
sourceRef:
kind: OCIRepository
name: frontend
prune: true
La ressource Kustomization
permet de personnaliser davantage les manifestes Kubernetes, tout en spécifiant l'espace de nommage dans lequel déployer les ressources. La ressource OCIRepository
de Flux CD, quant à elle, permet de spécifier la référence et le tag du dépôt d'images OCI à synchroniser régulièrement. Vous remarquerez également les champs verify.provider
et verify.secretRef
: ils permettent de vérifier que l'image OCI déployée sur le cluster a bien été signée avec la clé privée Cosign correspondante, utilisée précédemment dans le job CI/CD.
La clé publique correspondante doit être stockée dans un secret Kubernetes qui doit être présent dans le même espace de nommage que la ressource OCIRepository
. Pour que ce secret soit géré par Flux CD et non stocké en texte brut, vous pouvez utiliser SealedSecrets pour chiffrer sa valeur et la faire déchiffrer côté cluster par un contrôleur.
Si vous souhaitez opter pour une approche plus simple sans SealedSecrets, vous pouvez déployer le secret via un job GitLab CI/CD à l'aide de l'interface de ligne de commande kubectl CLI
. Il vous suffit alors de supprimer le SealedSecret inclus dans la configuration ci-dessus et d'exécuter un job CI/CD pour déployer le secret de clé publique avant de lancer le job qui effectuera un push de la nouvelle image OCI. Cette opération permet de stocker le secret en toute sécurité dans GitLab et d'y accéder sur le cluster par le biais de la ressource OCIRepository. Cependant, bien qu'elle soit moins complexe, cette approche ne permet pas de gérer les secrets en environnement de production.
Les avantages combinés des artefacts OCI, de GitLab et de GitOps
Les artefacts OCI permettent aux équipes GitOps d'affiner leurs déploiements avec un niveau de sécurité renforcé et une approche plus minimaliste, sans priver les utilisateurs des atouts de Git, notamment une source unique de vérité et une collaboration efficace. Les images OCI introduisent une méthode d'empaquetage optimisée qui améliore le déploiement au sein des workflows GitOps.
GitLab continue de s'améliorer grâce aux retours de ses clients et de la communauté cloud-native dans le but de simplifier les workflows GitOps. Pour tester les fonctionnalités présentées dans cet article, vous pouvez commencer par un essai gratuit de 60 jours de GitLab Ultimate. Nous serions ravis de connaître votre avis suite à l'utilisation de ces outils. Alors, n'hésitez pas à nous faire part de vos retours d'expérience sur le forum de la communauté GitLab.