Atelier Kratix : Construire son PaaS Interne (IDP) Multi-Cluster sur Scaleway
Cet atelier pratique propose de concevoir une plateforme de self-service d’infrastructure et de services applicatifs (Internal Developer Platform - IDP) en s’appuyant sur Kratix.
Nous allons mettre en place une architecture multi-cluster réelle sur Scaleway, utiliser l’Object Storage Scaleway (S3) comme magasin d’état (StateStore), et explorer deux manières d’automatiser la fourniture de services :
- L’automatisation moderne (GitOps & Crossplane) : Provisionnement en self-service d’une base de données managée Scaleway.
- L’automatisation avec approbation humaine (Ticketing Gating) : Une Promise qui crée un ticket d’approbation sur une console de support, attend la validation manuelle d’un administrateur, et récupère dynamiquement les données saisies par l’approbateur pour configurer l’espace de travail cible (Namespace + Quotas).
- La composition de Promises (Pattern Async) : Une Promise dépendante qui récupère le namespace créé par la Promise 2 et y injecte un
SecretKubernetes, en utilisant le SDK Python officielkratix-sdket le mécanisme natifworkflow-control.yamlpour la réconciliation asynchrone.
- Niveau : Intermédiaire à Avancé.
- Durée cible : 120 minutes.
- Public : Consultants Cloud/DevOps OCTO Technology et participants aux formations d’architecture Kubernetes.
- Environnement cible : 2 clusters managés Scaleway Kapsule (Platform & Worker).
Objectifs pédagogiques
Section intitulée « Objectifs pédagogiques »À la fin de cet atelier, les participants sauront :
- Expliquer l’architecture Hub-and-Spoke de Kratix et le rôle des Promises, StateStores et Destinations.
- Installer et configurer Kratix en mode multi-cluster avec Scaleway Object Storage.
- Concevoir une Promise d’infrastructure moderne interfacée avec Crossplane.
- Concevoir une Promise de processus avec gating humain, capable de faire du polling d’API et de récupérer des données dynamiques post-approbation pour enrichir l’état Kubernetes final.
- Composer des Promises dépendantes en utilisant le SDK Python
kratix-sdket le mécanisme natifworkflow-control.yamlde Kratix (réconciliation asynchrone non-bloquante). - Comparer les architectures de synchronisation basées sur FluxCD (S3-native) et ArgoCD (Git-native).
Concepts manipulés
Section intitulée « Concepts manipulés »- Kratix : Promises (API + Workflows/Pipelines), Destinations, BucketStateStore / GitStateStore, Status Updates.
- GitOps / Réconciliation : FluxCD (contrôleur Source/Bucket), ArgoCD.
- Infrastructure-as-Code : Crossplane (Providers, Custom Resources), OpenTofu/Terraform.
- Cloud Scaleway : Kapsule (Kubernetes), Object Storage (S3), Database Instances (simulées via Crossplane CRs).
Architecture cible de l’atelier
Section intitulée « Architecture cible de l’atelier »L’architecture repose sur deux clusters Kubernetes distincts provisionnés sur Scaleway :
flowchart TD
subgraph Platform Cluster ["Cluster Platform (Control Plane)"]
Kratix["Kratix Operator"]
Ticketing["Fake Ticketing API & UI (Port 30080)"]
end
subgraph Object Storage ["Scaleway Cloud Storage (S3)"]
S3Bucket["S3 Bucket 'kratix-state-store'"]
end
subgraph Worker Cluster ["Cluster Worker (Cible Applicative)"]
GitOps["GitOps Agent (FluxCD ou ArgoCD)"]
Crossplane["Crossplane Operator (Scaleway Provider)"]
end
Developer["Développeur"] -->|1. Crée Request| Platform["Platform Cluster"]
Kratix -->|2. Exécute Pipeline| PipelinePod["Pipeline Pod (Python/yq)"]
%% Scénario 1 : Crossplane
PipelinePod -->|3a. Écrit Manifests| S3Bucket
GitOps -->|4a. Réconcilie depuis| S3Bucket
GitOps -->|5a. Déploie DatabaseInstance| Crossplane
%% Scénario 2 : Ticketing
PipelinePod -->|"3b. REST POST (Création)"| Ticketing
PipelinePod -->|"4b. REST GET (Polling)"| Ticketing
Admin["Opérateur Support"] -->|5b. Approuve & Saisit Quotas| Ticketing
PipelinePod -->|6b. Récupère Quotas & écrit| S3Bucket
GitOps -->|7b. Déploie Namespace + Quota| WorkerCluster["Worker Cluster"]
Prérequis
Section intitulée « Prérequis »- Outils locaux :
tofuouterraform(v1.4+)kubectlhelm
- Accès Scaleway :
- Un compte Scaleway avec un projet actif.
- Des clés API Scaleway valides configurées dans votre environnement :
Fenêtre de terminal export SCW_ACCESS_KEY="votre_access_key"export SCW_SECRET_KEY="votre_secret_key"export SCW_DEFAULT_PROJECT_ID="votre_project_id"
Déroulé de l’atelier
Section intitulée « Déroulé de l’atelier »Étape 1 : Provisionner les clusters Scaleway (Platform & Worker)
Section intitulée « Étape 1 : Provisionner les clusters Scaleway (Platform & Worker) »Pour simplifier l’installation, un orchestrateur Terraform global a été conçu dans le dossier de l’atelier. Il instancie le module scaleway-kapsule à deux reprises : une fois pour la Platform (sans Crossplane/ArgoCD) et une fois pour le Worker (avec Crossplane et ArgoCD activés).
1.1 Déployer l’infrastructure
Section intitulée « 1.1 Déployer l’infrastructure »Rendez-vous dans le dossier Terraform de l’atelier :
cd demos/kratix/terraformInitialisez et appliquez la configuration (en passant vos clés API Scaleway sous forme de variables Terraform) :
tofu inittofu apply \ -var="scaleway_access_key=$SCW_ACCESS_KEY" \ -var="scaleway_secret_key=$SCW_SECRET_KEY" \ -var="scaleway_project_id=$SCW_DEFAULT_PROJECT_ID" \ -auto-approveNote : Cette étape provisionne deux clusters Kapsule Scaleway physiques complets, configure Traefik, cert-manager, installe Crossplane ainsi qu’ArgoCD sur le cluster worker, et génère automatiquement le secret d’accès S3 sur la Platform. Compter environ 8 à 12 minutes.
scaleway_project_id est obligatoire : contrairement au provider Terraform, le Provider Scaleway de Crossplane ne lit pas la variable d’environnement SCW_DEFAULT_PROJECT_ID — un project_id vide fait échouer la création de l’instance RDB avec At least project_id is required.
1.2 Configurer vos contextes kubectl
Section intitulée « 1.2 Configurer vos contextes kubectl »Une fois le déploiement Terraform terminé, le script écrit les fichiers de configuration kubeconfig directement dans votre répertoire personnel ~/.kube/.
Configurez vos variables d’environnement dans votre terminal pour basculer facilement :
export KUBECONFIG_PLATFORM=~/.kube/kubeconfig-kratix-platformexport KUBECONFIG_WORKER=~/.kube/kubeconfig-kratix-workerTestez l’accès et vérifiez que vos deux clusters répondent correctement :
# Vérifier le cluster Platformkubectl --kubeconfig=$KUBECONFIG_PLATFORM get nodes
# Vérifier le cluster Workerkubectl --kubeconfig=$KUBECONFIG_WORKER get nodesÉtape 2 : Installer Kratix, le StateStore S3 et le service de Ticketing
Section intitulée « Étape 2 : Installer Kratix, le StateStore S3 et le service de Ticketing »2.1 Installation de Kratix sur le cluster Platform (Automatique)
Section intitulée « 2.1 Installation de Kratix sur le cluster Platform (Automatique) »Kratix a été installé automatiquement sur votre cluster Platform lors de l’exécution du code Terraform (tofu apply) grâce à sa chart Helm officielle (syntasso/kratix).
Vous pouvez vérifier que l’opérateur et les CRDs Kratix sont bien présents sur le cluster Platform :
# Vérifier l'état de l'opérateurkubectl --kubeconfig=$KUBECONFIG_PLATFORM get pods -n kratix-platform-system
# Vérifier les CRDs Kratix disponibleskubectl --kubeconfig=$KUBECONFIG_PLATFORM get crds | grep kratix2.2 Récupérer le nom du bucket S3 créé par Terraform
Section intitulée « 2.2 Récupérer le nom du bucket S3 créé par Terraform »Le bucket S3 a été provisionné automatiquement par Terraform avec un nom unique (ex: kratix-state-store-xxxxxxxx). Vous pouvez récupérer son nom exact dans la sortie de votre commande tofu apply précédente ou via la commande suivante :
# Se placer dans le dossier Terraform de l'ateliercd demos/kratix/terraformtofu output kratix_state_store_bucket_nameConservez ce nom dans une variable pour les étapes suivantes :
export BUCKET_NAME="<nom-de-votre-bucket-recupere>"2.3 Déployer le service de Ticketing simulé sur le cluster Platform
Section intitulée « 2.3 Déployer le service de Ticketing simulé sur le cluster Platform »Ce service simule un outil de support JIRA/ServiceNow et propose une page web de validation :
# Se replacer à la racine du dépôtcd ../../..kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f demos/kratix/ticketing-service/service.yaml
# Attendre que le service démarrekubectl --kubeconfig=$KUBECONFIG_PLATFORM wait --for=condition=Available deployment/ticketing-service -n ticketing-system --timeout=120sExposez l’interface web de ticketing sur votre machine locale :
kubectl --kubeconfig=$KUBECONFIG_PLATFORM port-forward svc/ticketing-service -n ticketing-system 30080:80Vous pouvez maintenant ouvrir http://localhost:30080 sur votre navigateur. L’interface (Dark Mode premium) devrait indiquer qu’aucun ticket n’est actif pour le moment.
2.4 Configurer le BucketStateStore et la Destination sur la Platform
Section intitulée « 2.4 Configurer le BucketStateStore et la Destination sur la Platform »Kratix a besoin d’authentification pour écrire dans le compartiment S3 Scaleway. Le secret scaleway-s3-credentials contenant vos credentials de connexion a été créé automatiquement sur le cluster Platform par Terraform lors de la première étape.
Appliquez directement la configuration du BucketStateStore et de la Destination (en veillant à utiliser le bon nom de bucket) :
# Appliquer la configuration en remplaçant BUCKET_NAMEkubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f - <<EOFapiVersion: platform.kratix.io/v1alpha1kind: BucketStateStoremetadata: name: scaleway-s3 namespace: defaultspec: endpoint: s3.fr-par.scw.cloud insecure: false bucketName: ${BUCKET_NAME} secretRef: name: scaleway-s3-credentials namespace: default---apiVersion: platform.kratix.io/v1alpha1kind: Destinationmetadata: name: worker-1spec: path: worker-1 stateStoreRef: name: scaleway-s3 kind: BucketStateStoreEOFÉtape 3 : Configurer la synchronisation sur le cluster Worker (ArgoCD vs Flux)
Section intitulée « Étape 3 : Configurer la synchronisation sur le cluster Worker (ArgoCD vs Flux) »Dans une architecture Hub-and-Spoke, le cluster Worker doit réconcilier les ressources que Kratix dépose dans le bucket S3. Deux approches s’affrontent selon vos préférences de tooling :
Approche A : FluxCD (Recommandé avec S3)
Section intitulée « Approche A : FluxCD (Recommandé avec S3) »FluxCD supporte nativement les buckets S3 via son contrôleur Source.
Note : L’installation de FluxCD (via Helm) et la création du secret scaleway-s3-credentials-flux ont été réalisées automatiquement sur le cluster Worker par Terraform.
Il vous suffit de configurer la synchronisation du bucket sur le cluster Worker (en remplaçant BUCKET_NAME par le nom de votre bucket) :
# Configurer la réconciliation du bucketkubectl --kubeconfig=$KUBECONFIG_WORKER apply -f - <<EOFapiVersion: source.toolkit.fluxcd.io/v1kind: Bucketmetadata: name: kratix-worker-bucket namespace: flux-systemspec: interval: 10s provider: generic bucketName: ${BUCKET_NAME} # /!\ Remplacer par votre bucket endpoint: s3.fr-par.scw.cloud insecure: false secretRef: name: scaleway-s3-credentials-flux---apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: kratix-worker-kust namespace: flux-systemspec: interval: 10s path: ./worker-1 prune: true sourceRef: kind: Bucket name: kratix-worker-bucketEOFApproche B : ArgoCD (Idéal avec GitStateStore)
Section intitulée « Approche B : ArgoCD (Idéal avec GitStateStore) »ArgoCD n’est pas conçu pour s’abonner nativement à un bucket S3 de fichiers YAML bruts (il est conçu pour Git). Si ArgoCD est l’outil standard de votre organisation :
- Recommandation : Configurer la Destination Kratix en tant que
GitStateStore(Kratix pousse sur GitHub/GitLab). ArgoCD s’abonne alors nativement au dépôt Git. - Alternative (S3 Polling) : Si vous devez utiliser S3 avec ArgoCD, vous devez déployer un petit CronJob sur le worker qui effectue un
rcloneouaws s3 syncdepuis votre bucket vers un dossier local, ou utiliser un Config Management Plugin (CMP) sidecar dans ArgoCD.
(Dans le cadre de cet atelier, nous utiliserons l’Approche A avec FluxCD pour valider les flux S3 sans complexifier l’installation d’ArgoCD).
Étape 4 : Exercice 1 - La Promise d’Infrastructure Moderne (Crossplane)
Section intitulée « Étape 4 : Exercice 1 - La Promise d’Infrastructure Moderne (Crossplane) »Dans cet exercice, nous allons analyser et déployer la Promise scaleway-db. Le but est d’exposer une API simplifiée aux développeurs pour qu’ils puissent commander une base de données PostgreSQL de manière autonome.
Note : Pour simplifier cet exercice, Crossplane ainsi que son Provider Scaleway ont été installés et configurés automatiquement sur le cluster Worker via Terraform. Le secret contenant vos clés d’API Scaleway a été automatiquement déployé dans le namespace crossplane-system sur le Worker.
4.1 Comprendre la Promise et la gestion des Secrets
Section intitulée « 4.1 Comprendre la Promise et la gestion des Secrets »La Promise est définie dans promises/scaleway-db/promise.yaml. Contrairement à des valeurs écrites en texte brut dans la requête, cette Promise source de manière sécurisée les mots de passe de base de données à partir d’un secret Kubernetes sur le cluster Platform.
Cette Promise permet de configurer le nom et la clé du secret contenant les mots de passe de base de données via l’API grâce aux propriétés .spec.adminPasswordSecretRef et .spec.userPasswordSecretRef.
Le pipeline Kratix (qui s’exécute sous l’image alpine/k8s:1.30.2) possède des permissions RBAC pour lire les secrets de l’espace de travail. Il extrait les références fournies par l’utilisateur (ou utilise les valeurs par défaut database-passwords avec les clés admin-password/user-password), interroge l’API Kubernetes pour lire les secrets et génère les 5 manifests requis pour Crossplane et les secrets sur le cluster Worker :
- Les secrets de mots de passe de BDD (
v1/Secret) répliqués dans le namespacecrossplane-systemsur le Worker. - L’instance de BDD physique (
Instancede typerdb.scaleway.upbound.io/v1alpha1). - La base de données logique (
Database). - L’utilisateur applicatif (
User). - Les privilèges d’accès associés (
Privilege).
4.2 Validation de l’Exercice 1
Section intitulée « 4.2 Validation de l’Exercice 1 »-
Créer le secret contenant vos mots de passe sur le cluster Platform (dans le namespace de travail, ici
default) :Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM create secret generic database-passwords \--from-literal=admin-password="MonSuperPasswordAdmin123" \--from-literal=user-password="MonSuperPasswordUser123" -
Appliquer la Promise sur la Platform :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/scaleway-db/promise.yaml -
Soumettre une demande de base de données en tant que développeur :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/scaleway-db/request-example.yaml -
Vérifier la génération des manifests : Le pipeline Kratix doit s’exécuter et générer le fichier
database.yamldans votre compartiment S3 sous la structureworker-1/resources/.... -
Vérifier le déploiement sur le Worker : Le contrôleur FluxCD va appliquer les ressources sur le cluster worker. Vérifiez que l’instance de base de données est en cours de création :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_WORKER get instances.rdb.scaleway.upbound.iokubectl --kubeconfig=$KUBECONFIG_WORKER get databases.rdb.scaleway.upbound.iokubectl --kubeconfig=$KUBECONFIG_WORKER get users.rdb.scaleway.upbound.io
4.3 Supprimer une instance de base de données
Section intitulée « 4.3 Supprimer une instance de base de données »Quand vous supprimez une requête PostgresSQLInstance, la Promise exécute le workflow delete qui génère les manifests de suppression. Ces derniers sont envoyés au Worker via le StateStore et synchronisés par FluxCD.
Pour démolir l’instance créée à l’étape précédente :
kubectl --kubeconfig=$KUBECONFIG_PLATFORM delete postgressqlinstances.scaleway.octo.com <nom-de-la-ressource>Kratix va alors :
- Exécuter le pipeline
deletequi génère les manifests avecdeletionPolicy: Delete. - Écrire ces manifests dans
/kratix/output/database.yaml. - FluxCD sur le Worker synchronise les changements.
- Crossplane supprime la ressource Kubernetes ET l’instance PostgreSQL réelle chez Scaleway (et ses secrets associés).
Observation pédagogique : Cette suppression est irréversible. Vous verrez en temps réel :
- Sur le Platform : la ressource
PostgresSQLInstancedisparaît. - Sur le Worker : les Custom Resources Crossplane (
Instance,Database,User,Privilege) sont supprimées. - Chez Scaleway : l’instance PostgreSQL et ses données sont effectivement détruites.
Alternative en Production : deletionPolicy: Orphan
Section intitulée « Alternative en Production : deletionPolicy: Orphan »Dans un environnement de production réel, on utilise souvent deletionPolicy: Orphan pour protéger les données. Cela signifie :
- Ressources Kubernetes : supprimées.
- Ressource réelle (PostgreSQL chez Scaleway) : conservée (“orphanée”).
Vous auriez alors une chance de récupérer les données avant une destruction manuelle ultérieure. Pour l’implémenter, changez simplement deletionPolicy: Delete en deletionPolicy: Orphan dans le pipeline delete.
Pédagogiquement, ce manifest utilise Delete pour montrer le cycle complet.
Étape 5 : Exercice 2 - La Promise de Processus Hérité avec Validation Manuelle (Gating)
Section intitulée « Étape 5 : Exercice 2 - La Promise de Processus Hérité avec Validation Manuelle (Gating) »Cet exercice illustre comment Kratix peut s’interfacer avec un processus non-Kubernetes (système de ticketing interne). Le pipeline de la Promise implémente un workflow d’approbation asynchrone :
- Création du ticket : POST vers l’API de ticketing, récupération du
ticket_id. - Polling synchrone : Boucle qui interroge toutes les 5 secondes l’état du ticket (timeout 3 minutes).
- Récupération des données : Une fois approuvé, l’opérateur a saisi un namespace et des quotas via l’UI.
- Génération des manifests : Création d’un
Namespaceet d’unResourceQuotaavec les données saisies.
La Promise complète est déjà présente dans promises/ticketing/promise.yaml. Voici comment elle fonctionne :
Comment fonctionne le pipeline configure
Section intitulée « Comment fonctionne le pipeline configure »Étape 1 - Création du ticket (Extraction de l’input)
# Lire la requête TicketRequest depuis /kratix/input/object.yamltitle = get_val('title')description = get_val('description')requester = get_val('requester')ticket_id = get_val('ticketId') # Vide si première exécution
if not ticket_id: # POST /tickets avec les données de la requête payload = {"title": f"[{res_name}] {title}", "description": description, ...} response = urlopen(Request(f"{api_url}/tickets", data=payload, ...)) ticket_id = response['ticket_id']Kratix écrit le statut initial Pending avec le ticket_id dans /kratix/metadata/status.yaml. Cela permet à la ressource TicketRequest de tracer le ticket.
Étape 2 - Polling de l’approbation
# Boucle tant que timeout non dépasséwhile time.time() - start_time < 180: # 3 minutes response = urlopen(Request(f"{api_url}/tickets/{ticket_id}")) if response['status'] == 'Approved': output_data = response['output_data'] # Les données saisies par l'opérateur approved_by = response['approved_by'] break time.sleep(5) # Attendre 5 secondes avant le prochain pollSi l’approbation ne vient pas à temps, le pipeline échoue (exit 1) et met à jour le statut à Failed.
Étape 3 - Génération des manifests finaux
# Extraire les données saisies par l'opérateurns_name = output_data.get('namespace_name', 'default-namespace')cpu_limit = output_data.get('cpu_limit', '1')memory_limit = output_data.get('memory_limit', '2Gi')
# Générer le Namespace et le ResourceQuotamanifests = f"""apiVersion: v1kind: Namespacemetadata: name: {ns_name}---apiVersion: v1kind: ResourceQuotametadata: name: platform-quota namespace: {ns_name}spec: hard: limits.cpu: "{cpu_limit}" limits.memory: "{memory_limit}""""Kratix écrit les manifests dans /kratix/output/workspace.yaml. FluxCD sur le Worker applique automatiquement le Namespace et le ResourceQuota.
Validation de l’Exercice 2
Section intitulée « Validation de l’Exercice 2 »-
Appliquez la Promise sur la Platform :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/ticketing/promise.yaml -
Déclarez une requête de ticket :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/ticketing/request-example.yaml -
Observez le statut initial :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM get ticketrequests -o yamlVous verrez le statut
Pendingavec leticketId(ex:TICKET-1). Le pod du pipeline est maintenant en boucle de polling. -
Approuvez le ticket dans l’UI :
- Ouvrez
http://localhost:30080sur votre navigateur. - Saisissez un namespace et des limites (ex: namespace =
dev-workspace-1, CPU =4, Mémoire =8Gi). - Cliquez sur “Approuver & Renseigner les Données”.
- Ouvrez
-
Observez le résultat :
- Le pod du pipeline sort de sa boucle de polling.
- Le statut passe à
Approvedavec les métadonnées (namespaceName, cpuLimit, memoryLimit, approvedBy, resolvedAt). - Le namespace et les quotas sont appliqués sur le cluster Worker via FluxCD. Vérifiez :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_WORKER get nskubectl --kubeconfig=$KUBECONFIG_WORKER get resourcequota -n <nom-du-namespace>
5.1 Supprimer une requête de ticket
Section intitulée « 5.1 Supprimer une requête de ticket »Quand vous supprimez une TicketRequest, la Promise exécute le workflow delete. Ce pipeline génère un manifest de suppression qui supprime le Namespace (et son ResourceQuota) du cluster Worker.
kubectl --kubeconfig=$KUBECONFIG_PLATFORM delete ticketrequests.platform.octo.com <nom-de-la-ressource>Kratix va alors :
- Exécuter le pipeline
deletequi récupère le nom du namespace depuis le statut. - Générer un manifest de suppression du Namespace.
- FluxCD sur le Worker synchronise et supprime le Namespace et son ResourceQuota.
Important : Cette action est irréversible. Elle supprime effectivement l’espace de travail provisionné sur le Worker.
Étape 6 : Exercice 3 - Promise dépendante avec création de Secret (Pattern Async)
Section intitulée « Étape 6 : Exercice 3 - Promise dépendante avec création de Secret (Pattern Async) »Cet exercice illustre un concept avancé : une Promise qui en dépend d’une autre. La Promise namespace-secret ne peut s’exécuter que si une TicketRequest (créée via la Promise 2) a été approuvée. Elle récupère dynamiquement le nom du namespace créé par la Promise 2 et y déploie un Secret Kubernetes.
Contrairement à la Promise 2 qui utilise un polling synchrone (le pod reste actif 3 minutes), cette Promise 3 montre un pattern plus production-like : la réconciliation asynchrone non-bloquante.
Le concept de dépendance entre Promises
Section intitulée « Le concept de dépendance entre Promises »Kratix ne gère pas nativement les dépendances entre Promises. C’est au pipeline de la Promise dépendante d’implémenter cette logique :
- Le pipeline lit le statut de la
TicketRequestréférencée via l’API Kubernetes (client Pythonkubernetes), en utilisant le SDK Kratix (kratix-sdk) pour lire l’input et écrire les outputs. - Si la
TicketRequestestApproved→ le pipeline récupèrestatus.namespaceNameet génère leSecretviasdk.write_output(). - Si la
TicketRequestest encorePending→ le pipeline appellesdk.write_retry_after(timedelta(seconds=30))qui écrit/kratix/metadata/workflow-control.yaml, puis se termine proprement (exit 0). - Kratix lit ce fichier et relance automatiquement le pipeline après 30 secondes, sans garder de pod actif. Le cycle se répète jusqu’à ce que la
TicketRequestsoit approuvée.
Comment fonctionne le pipeline configure (Python)
Section intitulée « Comment fonctionne le pipeline configure (Python) »Étape 1 - Lecture de la requête via le SDK Kratix
from kratix_sdk import KratixSDK, Statusfrom kubernetes import client, config
# Initialiser le SDK Kratixsdk = KratixSDK()
# Lire la requête via le SDK (lit /kratix/input/object.yaml)resource = sdk.read_resource_input()ticket_request_name = resource.get_value('spec.ticketRequestName')
# Interroger le statut de la TicketRequest référencée via l'API K8sconfig.load_incluster_config()api = client.CustomObjectsApi()ticket = api.get_namespaced_custom_object( group='platform.octo.com', version='v1alpha1', namespace=ticket_request_namespace, plural='ticketrequests', name=ticket_request_name)Étape 2 - Branchement selon le statut
ticket_state = ticket['status']['status']namespace_name = ticket['status']['namespaceName']
if ticket_state == 'Approved' and namespace_name: # Générer le Secret via le SDK manifest_bytes = yaml.dump(secret_manifest).encode('utf-8') sdk.write_output('secret.yaml', manifest_bytes)
status = Status() status.set('status', 'Ready') status.set('namespaceName', namespace_name) sdk.write_status(status)
elif ticket_state == 'Pending': # Pattern async natif : sdk.write_retry_after() écrit workflow-control.yaml sdk.write_retry_after(timedelta(seconds=30), message="En attente...") sdk.write_status(Status(...)) sys.exit(0) # Le pod se termine, Kratix le relancera dans 30sLe SDK kratix-sdk (documentation) encapsule les interactions avec les répertoires Kratix (/kratix/input, /kratix/output, /kratix/metadata). La méthode write_retry_after() écrit le fichier workflow-control.yaml avec le délai spécifié, déléguant la gestion du requeue à Kratix de façon native.
Avantages du pattern async vs polling synchrone :
- Le pod ne consomme des ressources que pendant quelques secondes (pas 3 minutes).
- Le pod ne peut pas être tué par Kubernetes pendant une longue attente.
- Le pattern est idempotent : le pipeline peut être relancé N fois sans effet de bord.
- C’est le même pattern utilisé par les contrôleurs Kubernetes (reconciliation loop).
- Aucun RBAC
patchnécessaire : le pipeline n’a pas besoin de modifier sa propre ressource.
Gestion des erreurs et auto-réconciliation
Section intitulée « Gestion des erreurs et auto-réconciliation »Un aspect crucial de cette Promise est sa gestion gracieuse des erreurs. Contrairement à la Promise 2 qui termine en sys.exit(1) en cas d’échec (ce qui crée un Job Kubernetes en échec et déclenche un backoff exponentiel), la Promise 3 utilise le mécanisme write_retry_after() pour tous les cas d’erreur :
| Situation | Statut écrit | Retry | Comportement |
|---|---|---|---|
TicketRequest introuvable (404) | WaitingDependency | 60s | Message indique que la Promise ticketing-gating doit être installée |
TicketRequest encore Pending | Pending | 30s | Message indique d’attendre l’approbation |
TicketRequest en Failed | WaitingDependency | 120s | Message indique que le ticket doit être résolu |
| Statut inconnu | WaitingDependency | 60s | Message indique une nouvelle tentative |
| Erreur API K8s (RBAC, connexion) | WaitingDependency | 60s | Message indique l’erreur technique |
except ApiException as e: if e.status == 404: # La TicketRequest n'existe pas — retry gracieux sans échec msg = (f"TicketRequest '{ticket_request_name}' introuvable. " f"Vérifiez que la Promise 'ticketing-gating' est installée.") status = Status() status.set('status', 'WaitingDependency') status.set('message', msg) sdk.write_status(status) sdk.write_retry_after(timedelta(seconds=60), message=msg) sys.exit(0) # Exit 0 : pas d'échec, Kratix requeue proprementPourquoi sys.exit(0) et non sys.exit(1) ?
sys.exit(1)→ le Job Kubernetes est marquéFailed, Kratix incrémenteworkflowsFailed, et Kubernetes applique un backoff exponentiel (10s, 20s, 40s, … jusqu’à 6 min). L’erreur est “bruyante” et le retry n’est pas contrôlable.sys.exit(0)+write_retry_after()→ le Job est marquéSucceeded, Kratix litworkflow-control.yamlet requeue après le délai exact spécifié. Le statut de la ressource reste propre et informatif.
La CRD définit également subresources.status: {} pour permettre des mises à jour de statut isolées (sans re-soumettre le spec), et le schéma de statut documente les valeurs possibles : Pending, Ready, Failed, WaitingDependency.
Validation de l’Exercice 3
Section intitulée « Validation de l’Exercice 3 »-
Prérequis : Avoir terminé l’Exercice 2 et avoir une
TicketRequestapprouvée (request-new-env). -
Appliquez la Promise sur la Platform :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/namespace-secret/promise.yaml -
Déclarez une requête de secret :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM apply -f promises/namespace-secret/request-example.yaml -
Observez le statut initial :
Fenêtre de terminal kubectl --kubeconfig=$KUBECONFIG_PLATFORM get namespacesecretrequests -o yaml- Si la
TicketRequestest déjàApproved: le statut passe directement àReadyet leSecretest généré. - Si la
TicketRequestest encorePending: le statut restePendingavec le message d’attente. Le pipeline s’est terminé proprement et sera re-déclenché par Kratix.
- Si la
-
Observez le résultat sur le Worker :
Fenêtre de terminal # Récupérer le nom du namespace créé par la TicketRequestNAMESPACE=$(kubectl --kubeconfig=$KUBECONFIG_PLATFORM get ticketrequest request-new-env -o jsonpath='{.status.namespaceName}')# Vérifier que le Secret a été créé dans ce namespacekubectl --kubeconfig=$KUBECONFIG_WORKER get secret app-config -n $NAMESPACE -o yamlVous devriez voir le Secret
app-configavec les clésDATABASE_URL,API_KEYetLOG_LEVEL(en base64).
6.1 Supprimer une requête de secret
Section intitulée « 6.1 Supprimer une requête de secret »Quand vous supprimez une NamespaceSecretRequest, la Promise exécute le workflow delete. Ce pipeline récupère le nom du namespace depuis le statut et génère un manifest de suppression du Secret.
kubectl --kubeconfig=$KUBECONFIG_PLATFORM delete namespacesecretrequests.platform.octo.com secret-for-qa-envKratix va alors :
- Exécuter le pipeline
deletequi récupère le namespace depuis le statut (ou depuis laTicketRequest). - Générer un manifest de suppression du
Secret. - FluxCD sur le Worker synchronise et supprime le
Secretdu namespace.
Important : Le namespace lui-même n’est pas supprimé (il est géré par la Promise 2). Seul le Secret est retiré.
Questions de débriefing
Section intitulée « Questions de débriefing »- Pourquoi Kratix est-il qualifié d’orchestrateur de plateforme ? En quoi diffère-t-il d’un simple opérateur Kubernetes classique comme Crossplane ?
- Quel est l’intérêt du couplage avec un StateStore (comme S3 ou Git) ? Pourquoi Kratix n’applique-t-il pas directement les ressources sur le cluster worker via des clients Kubernetes ?
- Quels sont les avantages et inconvénients d’un gating humain dans un pipeline de plateforme ? Comment gérer la haute disponibilité des pods de pipeline si l’attente dure plusieurs heures/jours ? (Piste : utiliser des contrôleurs asynchrones ou des webhooks plutôt qu’un polling synchrone dans un Pod).
- Si vous deviez remplacer FluxCD par ArgoCD, comment modifieriez-vous la Promise ? Quelle configuration de StateStore utiliseriez-vous et pourquoi ?
- Comparez les patterns de polling synchrone (Exercice 2) et de réconciliation asynchrone via
workflow-control.yaml(Exercice 3). Quels sont les trade-offs en termes de consommation de ressources, de résilience et de complexité d’implémentation ? Dans quel cas préféreriez-vous l’un ou l’autre ? - Comment pourriez-vous formaliser la dépendance entre Promises ? Kratix ne gère pas nativement les dépendances. Quelles approches envisageriez-vous pour rendre ce pattern plus générique et réutilisable (webhooks, contrôleurs personnalisés, CRD de dépendance) ?
Nettoyage
Section intitulée « Nettoyage »Pour éviter des coûts cloud inutiles sur votre compte Scaleway, détruisez les deux clusters ainsi que le compartiment S3 :
# Se rendre dans le dossier Terraform de l'ateliercd demos/kratix/terraform
# Détruire les deux clusters Scaleway et le bucket S3tofu destroy \ -var="scaleway_access_key=$SCW_ACCESS_KEY" \ -var="scaleway_secret_key=$SCW_SECRET_KEY" \ -var="scaleway_project_id=$SCW_DEFAULT_PROJECT_ID" \ -auto-approvePour aller plus loin
Section intitulée « Pour aller plus loin »- Gestion asynchrone durable : Dans une production réelle, faire un polling de 3 minutes dans un Pod est une mauvaise pratique (le pod consomme des ressources et peut être tué par Kubernetes). On préférera concevoir une Promise qui se termine immédiatement après la création du ticket, et configurer le système de ticketing pour qu’il fasse un appel d’API de retour (Webhook / callback) vers le Kubernetes API de la Platform pour mettre à jour la ressource Kratix, ce qui déclenchera à nouveau le pipeline (réconciliation asynchrone). La Promise 3 de cet atelier illustre d’ailleurs une alternative plus production-like avec
workflow-control.yaml. - Sécurisation des Secrets : Intégrez HashiCorp Vault ou Scaleway Secret Manager pour distribuer dynamiquement les credentials de base de données générés par Crossplane.
- Composition de Promises : Explorez comment créer un contrôleur personnalisé qui surveille les statuts de plusieurs Promises et déclenche automatiquement les Promises dépendantes, plutôt que de laisser chaque Promise s’auto-replanifier.