CI/CD et GitOps#
Du code au cluster : le pipeline CI/CD#
Imaginez une équipe de développeurs qui pousse du code plusieurs fois par jour. Sans automatisation, chaque déploiement demande : exécuter les tests à la main, construire l’image Docker, la pousser sur un registry, modifier les manifestes YAML, les appliquer sur le cluster. Ça prend du temps, introduit des erreurs humaines, et ralentit l’équipe.
CI/CD (Intégration Continue / Déploiement Continu) automatise entièrement ce pipeline.
GitHub Actions : anatomie d’un workflow#
GitHub Actions est aujourd’hui l’outil CI/CD le plus utilisé. Un workflow est un fichier YAML dans .github/workflows/ :
# .github/workflows/cicd.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ─── Job 1 : Tests ───────────────────────────────────────────────
test:
runs-on: ubuntu-latest
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Configurer Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Installer les dépendances
run: pip install -r requirements.txt
- name: Lancer les tests
run: pytest tests/ --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
# ─── Job 2 : Build et push de l'image ───────────────────────────
build-push:
needs: test # Lance seulement si test réussit
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Pour OIDC (sans secret long-lived)
outputs:
image-digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Login GHCR via OIDC
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Métadonnées de l'image
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=sha-
type=raw,value=latest
- name: Build et push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Signer l'image (cosign)
uses: sigstore/cosign-installer@v3
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
# ─── Job 3 : Déploiement K8s ────────────────────────────────────
deploy:
needs: build-push
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Configurer kubectl
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Mettre à jour l'image dans le manifeste
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-push.outputs.image-digest }}"
sed -i "s|image: .*|image: ${IMAGE}|g" k8s/deployment.yaml
- name: Déployer sur Kubernetes
run: kubectl apply -f k8s/
- name: Vérifier le déploiement
run: kubectl rollout status deployment/mon-app --timeout=5m
OIDC : plus de secrets long-lived
GitHub Actions supporte l’authentification OIDC (OpenID Connect). Au lieu de stocker un token Docker Hub dans les secrets, le workflow obtient un token éphémère signé par GitHub, valide uniquement pour cette exécution. C’est bien plus sécurisé.
La permission id-token: write active ce mécanisme.
GitOps : Git comme source de vérité#
L’approche CI/CD traditionnelle est en mode push : le pipeline CI « pousse » le déploiement sur le cluster. GitOps inverse cette logique.
Le principe GitOps repose sur quatre règles :
Tout est déclaratif : l’état désiré est décrit dans des fichiers (YAML, Helm charts)
Git est la source de vérité : aucune modification manuelle sur le cluster
Les changements passent par Git : pull request → review → merge → déploiement automatique
La réconciliation est continue : un agent surveille le cluster et corrige toute dérive
ArgoCD#
ArgoCD est l’outil GitOps le plus populaire. Il tourne dans le cluster et surveille en permanence un dépôt Git.
Application ArgoCD (CRD)#
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mon-app-production
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/mon-org/mon-app-gitops
targetRevision: main
path: kubernetes/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Supprime les ressources plus dans git
selfHeal: true # Corrige les dérives manuelles
syncOptions:
- CreateNamespace=true
- PruneLast=true
# Commandes ArgoCD CLI
argocd app list # Lister les applications
argocd app get mon-app-production # État détaillé
argocd app sync mon-app-production # Synchronisation manuelle
argocd app rollback mon-app-production 5 # Rollback à la révision git 5
argocd app diff mon-app-production # Diff live vs git
Kustomize : overlays et personnalisation#
Kustomize est un outil natif Kubernetes pour personnaliser des manifestes YAML sans les modifier. Il utilise le concept d”overlays : une base partagée, déclinée par environnement.
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[5], line 55
51 ax.add_patch(rect)
52 ax.text(x, y+0.5, texte, ha='center', va='center',
53 fontsize=7.5, color='#333', multialignment='center')
---> 55 for (ox, _, oc, _), (rx, ry, rc, _) in zip(overlays, resultats):
56 ax.annotate("", xy=(rx, ry+1.2), xytext=(ox, 5.0),
57 arrowprops=dict(arrowstyle='->', color=oc, lw=1.5))
58 ax.text(rx, ry+1.6, "kustomize build", ha='center',
59 fontsize=7, color=oc, style='italic')
ValueError: too many values to unpack (expected 4)
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: mon-app
images:
- name: mon-app
newTag: latest
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
patches:
- patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: mon-app
spec:
replicas: 10
- path: patch-resources.yaml
images:
- name: mon-app
newTag: "1.3.0"
resources:
- hpa.yaml
# Construire et voir le résultat (sans appliquer)
kustomize build overlays/production
# Appliquer directement via kubectl
kubectl apply -k overlays/production
# ArgoCD supporte Kustomize nativement
# (détection automatique à la présence du kustomization.yaml)
Flux vs ArgoCD#
Simulation d’un contrôleur GitOps#
Sécurité CI/CD#
OIDC : fini les secrets long-lived#
Bonne pratique : OIDC partout
Ne jamais stocker de tokens Docker Hub, de kubeconfig ou de clés API dans les secrets GitHub. Utilisez OIDC :
GitHub Actions → AWS :
aws-actions/configure-aws-credentialsavecrole-to-assumeGitHub Actions → GCP :
google-github-actions/authavecworkload_identity_providerGitHub Actions → Docker Hub : depuis 2024, Docker Hub supporte OIDC
GitHub Actions → GHCR :
GITHUB_TOKEN(automatique, OIDC natif)
L’avantage : si le workflow est compromis, le token est déjà expiré. Il n’y a rien à révoquer.
cosign : signer les images dans le pipeline#
# Dans le pipeline GitHub Actions
- name: Installer cosign
uses: sigstore/cosign-installer@v3
- name: Signer l'image
run: |
cosign sign --yes \
--oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/mon-org/mon-app@${DIGEST}
# Dans le cluster : politique d'admission (Policy Controller)
# Refuse tout pod avec une image non signée par cosign
Récapitulatif#
Ce qu’il faut retenir
CI/CD automatise le chemin du code au cluster : tests → build → push image → déploiement.
GitHub Actions est l’outil CI/CD le plus répandu. Un workflow = fichier YAML dans
.github/workflows/.GitOps = Git comme source de vérité, réconciliation continue par un agent dans le cluster. Pas d’accès direct au cluster depuis la CI.
ArgoCD : UI riche, Application CRD, sync automatique. Flux : plus minimaliste, tout GitOps, automation des images nativement.
Kustomize : personnaliser des manifestes YAML avec des overlays sans les modifier. Base + dev/staging/prod.
Sécurité : OIDC plutôt que secrets long-lived. Signer les images avec cosign.