Aller au contenu

Pipeline CI/CD d'AKKO — Woodpecker + Buildah + Harbor

Comment les changements passent d'un git push à un pod en production sur le cluster AKKO.

Avant/après en une ligne

Avant : SSH sur Netcup, git pull, lancer manuellement bash helm/scripts/deploy-netcup-full.sh. Aucune trace d'audit, aucun contrôle pré-merge, aucune preuve que « ce commit a été testé ».

Après : un git push main déclenche automatiquement un pipeline, 13 contrôles tournent sur le changement, Buildah reconstruit 13 images OCI dans des pods parallèles, les pousse vers Harbor, et fait un helm upgrade sur le cluster live. GitHub affiche ✓/✗ sur chaque commit.

Vue d'ensemble

  Développeur
     │ git push / ouvre une PR
  ┌──────────────┐   webhook GitHub    ┌──────────────────────────┐
  │ AKKO-p/AKKO  │ ──────────────────► │ Woodpecker server        │
  │ (GitHub)     │   /api/hook         │ ci.akko-ai.com           │
  └──────────────┘                     │ (namespace akko sur k3s) │
                                       └──────────────────────────┘
                                      un pod par .woodpecker/*.yml
                    ┌─────────────────────────────────────────────────────────┐
                    │ 13 pipelines de validation (PR + push)                  │
                    │ ───────────────────────────────────────────────         │
                    │ 00-validate          helm dep update + scan changeme    │
                    │ 01-helm-lint         helm lint (prod + rendered)        │
                    │ 02-helm-template     kubeconform sur les manifests      │
                    │ 03-no-hardcoding     lint (zéro akko.local dans chart)  │
                    │ 04-secrets           gitleaks + grep plaintext          │
                    │ 05-doc-consistency   cohérence README/CLAUDE/Chart      │
                    │ 06-trivy             scan CVE fs + config               │
                    │ 07-trino-ai-plugin   tests Maven plugin Trino           │
                    │ 10-aden-tests        pytest + seuil de couverture       │
                    │ 13-np-coverage       lint NetworkPolicy                 │
                    │                                                         │
                    │ +2 push-only (creds Harbor, interdits aux PRs)         │
                    │ 11-license-check     scan licences syft                 │
                    │                                                         │
                    │ +1 push + manuel (le vrai déploiement)                  │
                    │ 08-deploy-netcup     13 builds Buildah + helm upgrade   │
                    │                                                         │
                    │ +1 manuel-only (nécessite cluster live)                 │
                    │ 09-tests             pytest + Playwright smoke          │
                    └─────────────────────────────────────────────────────────┘
                                  │                            │
          ✓ tout vert             │                            │ 08 spécifique
                                  ▼                            ▼
          Statut GitHub           │              ┌─────────────────────────┐
          check vert              │              │ 13 pods de build Buildah│
                                  │              │ chacun push vers Harbor │
                                  │              └─────────────────────────┘
                                  │                            │
                                  │                            ▼
                                  │              ┌─────────────────────────┐
                                  │              │ step helm-upgrade       │
                                  │              │ reuse-values, wait 15m  │
                                  │              └─────────────────────────┘
                                  │                            │
                                  │                            ▼
                                  │              ┌─────────────────────────┐
                                  └────────────► │ k3s pull les nouveaux   │
                                                 │ digests 2026.04         │
                                                 └─────────────────────────┘

Pourquoi Buildah (et pas docker build ni kaniko)

k3s sur Netcup tourne sur containerd, pas Docker. L'image docker:27-cli a le binaire CLI mais aucun daemon à qui parler. Chaque docker build dans un pod Woodpecker échouait avec ERROR: Docker daemon is not running.

Monter /var/run/docker.sock n'est pas une option — k3s ne ship que containerd sur l'hôte, et monter des sockets hôte est une élévation de privilèges qu'on veut éviter.

Le premier essai utilisait kaniko (builder sans daemon de Google). Ça marchait techniquement, mais le 2026-04-23 on a découvert que GoogleContainerTools/kaniko avait été archivé par Google — le dernier commit était la notice d'archivage du 2025-06-03. Locker AKKO sur une dépendance non maintenue était inacceptable, donc on a pivoté.

Buildah (Red Hat, Apache 2.0, maintenu activement avec releases toutes les ~3 semaines) est devenu le builder canonique. Il :

  • Est un binaire Go compilé statiquement qui lit un Dockerfile et produit une image OCI depuis un pod ordinaire
  • Ne nécessite aucun daemon, aucun privilège, aucun mount hôte
  • Est le builder par défaut d'OpenShift Pipelines (Tekton) — Red Hat s'engage sur des cycles de support de plusieurs années
  • A une compatibilité Dockerfile à 100% (mêmes RUN/COPY/ARG/ENV/HEALTHCHECK que docker build)

Voir akko-technical-map/decisions/ADR-024-buildah-replaces-kaniko.md (repo de planning privé) pour l'analyse comparative complète (Buildah vs BuildKit vs Podman vs nerdctl).

Le pipeline de déploiement (08-deploy-netcup) en détail

steps:
  - detect-changes            # écrit /tmp/needs_rebuild
  - build-postgres            # pod Buildah → push harbor.akko-ai.com/akko/postgres:2026.04
  - build-spark               # en parallèle avec les autres, 2 agents max
  - build-notebook
  - build-mlflow
  - build-cockpit
  - build-trino
  - build-ai-service
  - build-airflow
  - build-dbt
  - build-mcp-trino
  - build-mcp-openmetadata
  - build-docs
  - build-catalog-manager
  - helm-upgrade              # depends_on: tous les build-*

Template de chaque step build-* :

image: quay.io/buildah/stable:v1.43.1
commands:
  - 'mkdir -p /tmp/auth'
  - 'AUTH=$(printf "%s:%s" "$HARBOR_USERNAME" "$HARBOR_PASSWORD" | base64 | tr -d "\n"); printf ''{"auths":{...}}'' "$AUTH" > /tmp/auth/config.json'
  - 'export REGISTRY_AUTH_FILE=/tmp/auth/config.json'
  - 'buildah --storage-driver vfs bud --layers --format docker -t <dest> -f <dockerfile> <context>'
  - 'buildah --storage-driver vfs push <dest>'

Notes :

  • --storage-driver vfs évite d'avoir besoin de fuse-overlayfs ou de capabilities kernel — tourne dans un pod PodSecurity-restricted. Plus lent qu'overlay (~20%) mais portable.
  • --layers active le cache inter-layers pendant un build.
  • --format docker produit des images v2 (OCI fonctionne aussi ; docker est plus largement accepté par les vieux registres).
  • REGISTRY_AUTH_FILE = manière buildah de pointer vers l'auth Harbor, équivalent au ~/.docker/config.json de docker.

Les secrets qui alimentent tout ça

Stockés dans le vault de Woodpecker (pas dans k8s Secrets, pas dans GitHub Actions secrets) :

Nom Consommateur Events
harbor_username steps build-*, 11-license-check push, manuel
harbor_password idem push, manuel
kubeconfig step helm-upgrade push, manuel
akko_domain helm-upgrade, 09-tests push, manuel

Woodpecker refuse d'injecter tout from_secret: dans les pipelines à event: pull_request — c'est l'anti-exfiltration par défaut.

Chemin de rebuild / redeploy

Automatique (canonique)

git push origin main
# → les pipelines 00..13 tournent en parallèle sur push main
# → 08-deploy-netcup build 13 images avec Buildah, push Harbor, fait helm upgrade

Manuel (fallback si CI down ou itération)

ssh root@159.195.77.208
cd /root/akko && git pull
bash helm/scripts/deploy-netcup-full.sh

Le script manuel utilise docker build (toujours fonctionnel sur l'hôte Netcup qui a Docker installé pour la commodité). Préférer le chemin automatique.

Hot reload d'un seul service (phase de construction)

En phase de développement actif, passer par le pipeline complet de 14 builds + helm upgrade pour un one-liner CSS ou un tweak de routes_query.py est absurde. Utiliser dev-reload.sh : build local d'une seule image sur l'hôte Netcup, push avec tag dev, patch de la Deployment live.

ssh root@159.195.77.208
cd /root/akko && git pull
bash helm/scripts/dev-reload.sh cockpit           # 30-60 s au lieu de 30-40 min
bash helm/scripts/dev-reload.sh akko-rag          # pour un nouveau service
bash helm/scripts/dev-reload.sh ai-service

Utilise un tag dev horodaté (harbor.akko-ai.com/akko/<svc>:2026.04-dev-<ts>) pour que le tag de prod :2026.04 reste intact. Le prochain helm upgrade automatique sur un push main remet la Deployment sur le tag propre. Liste complète : bash helm/scripts/dev-reload.sh (sans argument → aide).

Quand utiliser quel chemin :

Chemin Latence À utiliser quand
dev-reload.sh <svc> 30 s - 5 min itération sur UN service, feedback rapide
git push main → CI 5-40 min changement prêt, PR verte, audit trail
deploy-netcup-full.sh 30-45 min CI down, full rebuild nécessaire

Rollback

helm -n akko rollback akko   # révision précédente
# OU, pour revenir à une version taguée :
AKKO_TAG=2026.04-stable bash helm/scripts/deploy-netcup-full.sh

Cache de build

Deux couches de cache accélèrent les rebuilds :

  1. Path filters (voir .woodpecker/08-deploy-netcup.yml) — un push qui ne touche que docs/** ne déclenche que build-docs + helm-upgrade. Les builds non concernés skippent → -90 % de temps pipeline sur les commits typiques.

  2. Cache registry Buildah — chaque buildah bud écrit ses couches intermédiaires dans harbor.akko-ai.com/akko/cache-<service> via --cache-to et les relit via --cache-from. Quand un build est inévitable, buildah reprend à la dernière couche valide au lieu de re-exécuter chaque RUN depuis zéro. Gain typique sur un changement d'une ligne dans un Dockerfile :

Image À froid Avec cache
notebook-slim ~12 min ~2-3 min
notebook-full (FROM slim) ~35 min ~4-6 min
trino (plugin Maven) ~10 min ~2 min
cockpit (nginx + HTML) ~3 min ~40 s

Le premier run après l'activation de cette feature est aussi lent qu'un build à froid — les repos cache n'existent pas encore dans Harbor. Buildah tolère un 404 sur --cache-from et peuple le cache via --cache-to à la fin. À partir du run #2, le cache prend le relais.

Les images cache vivent dans des repos Harbor dédiés (akko/cache-*) pour que les tags de prod (akko/<service>:2026.04) restent propres. Politique de rétention recommandée sur cache-* : garder les 5 derniers manifests + GC hebdomadaire.

Dette connue

  • Trust flag non utilisé : Buildah tourne totalement sans privilèges — on n'active pas le flag trusted.volumes. Toute future step qui en aurait besoin doit passer par un ADR.
  • 09-tests manuel only : smoke tests live qui n'ont de sens qu'après un déploiement. Vrai chemin = depends_on: [helm-upgrade] inter-pipeline, pas supporté par Woodpecker v3.4.
  • Bugs Dockerfile latents : le cache docker cachait des vrais bugs. Les rebuild-from-scratch les exposent. Pattern à retirer : un apt-get -y upgrade orphelin entre apt-get update et apt-get install.

Politique de maintenance des outils

Tout builder/outil intégré dans le CI AKKO doit passer un check de maintenance avant adoption : dernier commit < 3 mois, dernière release < 6 mois, ≥ 2 mainteneurs actifs, organisation de support claire. Décisions documentées via ADR. Voir feedback_verify_tool_maintained.md dans la mémoire de session.

Références