Bonnes pratiques¶
Optimisation des pipelines¶
Cache des dependances¶
Le cache reduit drastiquement le temps de build en evitant de retelecharger les dependances à chaque run.
# Cache Go modules
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: go-${{ hashFiles('go.sum') }}
restore-keys: go-
# Cache Node modules
- name: Cache Node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
| Langage | Répertoire a cacher | Cle de cache | Gain typique |
|---|---|---|---|
| Go | ~/go/pkg/mod | go-${{ hashFiles('go.sum') }} | 60-80% |
| Node | ~/.npm | npm-${{ hashFiles('package-lock.json') }} | 50-70% |
| Python | ~/.cache/pip | pip-${{ hashFiles('requirements.txt') }} | 40-60% |
| Rust | ~/.cargo/registry, target/ | cargo-${{ hashFiles('Cargo.lock') }} | 70-90% |
| Java | ~/.m2/repository | maven-${{ hashFiles('pom.xml') }} | 50-70% |
Stages paralleles¶
Exécuter les étapes indépendantes en parallele reduit le temps total du pipeline :
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make lint
test-unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make test-unit
test-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make test-integration
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: trivy fs .
# Ce job attend tous les precedents
build:
needs: [lint, test-unit, test-integration, scan]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make build
graph LR
subgraph Sequential["Sans parallelisme : 15 min"]
S1[lint] --> S2[test-unit] --> S3[test-integration] --> S4[scan] --> S5[build]
end
subgraph Parallel["Avec parallelisme : 7 min"]
P1[lint] --> P5[build]
P2[test-unit] --> P5
P3[test-integration] --> P5
P4[scan] --> P5
end Matrix builds¶
Tester sur plusieurs versions/OS en une seule définition :
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ["1.21", "1.22", "1.23"]
os: [ubuntu-latest, alpine]
fail-fast: false # ne pas arreter les autres si un echoue
steps:
- uses: actions/checkout@v4
- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- run: go test ./...
Build d'images multi-stages¶
Reduire la taille de l'image finale en separant le build du runtime :
# Dockerfile multi-stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server ./cmd/server
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]
Self-hosted runners¶
Auto-scaling des runners¶
Pour les charges variables, déployer des runners qui scalent automatiquement :
# Kubernetes : utiliser un Deployment avec HPA
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-runner
namespace: ci-runners
spec:
replicas: 2 # minimum
selector:
matchLabels:
app: gitea-runner
template:
metadata:
labels:
app: gitea-runner
spec:
serviceAccountName: gitea-runner
containers:
- name: runner
image: gitea/act_runner:latest
args: ["daemon", "--config", "/config/config.yaml"]
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
volumeMounts:
- name: config
mountPath: /config
- name: data
mountPath: /data
volumes:
- name: config
configMap:
name: runner-config
- name: data
emptyDir: {}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gitea-runner-hpa
namespace: ci-runners
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gitea-runner
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Nettoyage des runners¶
Les runners accumulent des images, des caches et des artefacts. Nettoyer régulièrement :
# CronJob de nettoyage
#!/bin/bash
# /home/actrunner/cleanup.sh
# Supprimer les images non utilisees depuis plus de 24h
podman image prune -a --filter "until=24h" -f
# Supprimer les conteneurs arretes
podman container prune -f
# Supprimer les volumes orphelins
podman volume prune -f
# Verifier l'espace disque
USAGE=$(df -h /home/actrunner | tail -1 | awk '{print $5}' | tr -d '%')
if [ "${USAGE}" -gt 80 ]; then
echo "WARNING: disk usage at ${USAGE}%"
# Alerte via webhook
fi
# Cron : executer chaque nuit
echo "0 3 * * * actrunner /home/actrunner/cleanup.sh" | sudo tee /etc/cron.d/runner-cleanup
Resource limits par label¶
Différents labels pour différents profils de build :
| Label | CPU | RAM | Cas d'usage |
|---|---|---|---|
small | 1 | 2 Go | Lint, scan, scripts |
ubuntu-latest | 2 | 4 Go | Builds standards |
large | 4 | 8 Go | Builds Java, monorepos |
gpu | 2+GPU | 16 Go | ML inference, builds CUDA |
GitOps avance¶
Structure du dépôt de manifestes¶
Organisation recommandee pour un dépôt fleet-infra géré par Flux :
fleet-infra/
├── clusters/
│ ├── production/
│ │ ├── flux-system/ ← manifestes Flux (bootstrap)
│ │ ├── infrastructure.yaml ← Kustomization pour l'infra
│ │ ├── apps.yaml ← Kustomization pour les apps
│ │ └── tenants/ ← Kustomizations par equipe
│ │ ├── team-backend.yaml
│ │ └── team-frontend.yaml
│ └── staging/
│ ├── flux-system/
│ ├── infrastructure.yaml
│ └── apps.yaml
├── infrastructure/
│ ├── controllers/ ← Ingress, cert-manager, ESO...
│ └── configs/ ← ClusterIssuer, StorageClass...
└── apps/
├── base/
│ ├── myapp/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ └── api/
└── overlays/
├── staging/
│ └── kustomization.yaml
└── production/
└── kustomization.yaml
Kustomization en cascade (infrastructure → apps)¶
# clusters/production/infrastructure.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure-controllers
namespace: flux-system
spec:
interval: 1h
retryInterval: 1m
prune: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./infrastructure/controllers
---
# clusters/production/apps.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 5m
prune: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./apps/overlays/production
# Les apps attendent que l'infra soit prete
dependsOn:
- name: infrastructure-controllers
Promotion entre environnements¶
Le workflow de promotion suit un chemin predictible :
graph LR
develop --> staging["staging<br/>(auto-sync, interval 5m)"]
staging --> production["production<br/>(auto-sync, dependsOn staging)"] # Workflow de promotion
- name: Promote to production
if: github.ref == 'refs/heads/main'
run: |
TAG="${GITHUB_SHA::8}"
# Copier le tag de staging vers production
cd apps/overlays/production
kustomize edit set image \
harbor.example.com/org/myapp:${TAG}
git commit -am "promote: myapp ${TAG} to production"
git push
Contrôle du déploiement en production
Pour les environnements critiques, utiliser dependsOn pour forcer le staging avant la production. La promotion peut aussi passer par une PR sur le dépôt de manifestes pour ajouter une review humaine.
Drift detection et correction¶
Flux détecte automatiquement les modifications manuelles sur le cluster quand le feature gate DetectDrift est activé :
# Verifier le drift via les evenements
flux get kustomizations
# Voir les evenements de drift
kubectl -n flux-system get events \
--field-selector reason=DriftDetected
# Forcer la reconciliation
flux reconcile kustomization myapp-production --with-source
Configurer une alerte sur le drift :
# Alert specifique au drift
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: drift-alerts
namespace: flux-system
spec:
providerRef:
name: mattermost
eventSeverity: info
eventSources:
- kind: Kustomization
name: "*"
inclusionList:
- ".*drift.*"
Sécurité des pipelines¶
Actions epinglees par hash¶
Ne jamais utiliser un tag mutable (@v4) en production — epingler par hash SHA :
# Mauvais : le tag v4 peut etre modifie par le mainteneur
- uses: actions/checkout@v4
# Bon : hash immuable, verifiable
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Provenance SLSA¶
Générer une attestation de provenance pour chaque artefact :
- name: Generate SLSA provenance
run: |
# Signer l'image avec cosign
cosign sign --key env://COSIGN_PRIVATE_KEY \
${REGISTRY}/${IMAGE_NAME}:${TAG}
# Generer l'attestation de provenance
cosign attest --key env://COSIGN_PRIVATE_KEY \
--predicate provenance.json \
--type slsaprovenance \
${REGISTRY}/${IMAGE_NAME}:${TAG}
Verification cosign dans Flux¶
Flux peut vérifier la signature cosign des images avant de les deployer :
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: myapp-production
namespace: flux-system
spec:
interval: 5m
prune: true
sourceRef:
kind: GitRepository
name: manifests
path: ./overlays/production
verify:
provider: cosign
secretRef:
name: cosign-public-key
Least-privilege runners¶
| Privilege | Runner CI | Flux controllers |
|---|---|---|
| Root | Non | Non |
| Privileged | Non | Non |
| Host network | Non | Non |
| Host PID | Non | Non |
| Capabilities | Aucune (DROP ALL) | Aucune |
| Read-only rootfs | Oui | Oui |
| Non-root user | Oui (UID 1000+) | Oui |
Dependabot / Renovate pour les actions¶
Maintenir les actions et les images de base a jour :
# .gitea/renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"gitea-actions": {
"enabled": true,
"pinDigests": true
},
"dockerfile": {
"enabled": true
}
}
Disaster recovery¶
Flux v2 backup¶
Flux se gère lui-même via GitOps : les manifestes Flux sont dans le dépôt Git. Le backup principal est donc le dépôt Git lui-même.
# Exporter l'etat actuel de Flux
flux export --all > flux-backup-$(date +%Y%m%d).yaml
# Exporter les sources
flux export source git --all >> flux-backup-$(date +%Y%m%d).yaml
# Exporter les Kustomizations
flux export kustomization --all >> flux-backup-$(date +%Y%m%d).yaml
# Exporter les HelmReleases
flux export helmrelease --all >> flux-backup-$(date +%Y%m%d).yaml
# Sauvegarder les secrets Flux (cles SSH, SOPS, tokens)
kubectl -n flux-system get secrets -o yaml > flux-secrets-backup.yaml
# Stocker dans Vault (pas sur le filesystem)
vault kv put secret/backup/flux \
secrets=@flux-secrets-backup.yaml
rm flux-secrets-backup.yaml
Git est le backup
Tant que le dépôt Git fleet-infra est intact, Flux peut etre reinstalle sur un nouveau cluster avec flux bootstrap. Les secrets (cles SSH, SOPS) doivent cependant etre sauvegardes separement car ils ne sont pas dans Git.
Gitea Actions history retention¶
Configurer la retention des logs et artefacts :
# /etc/gitea/app.ini
[actions]
ENABLED = true
# Retention des logs (defaut : 365 jours)
LOG_RETENTION_DAYS = 365
# Retention des artefacts (defaut : 90 jours)
ARTIFACT_RETENTION_DAYS = 90
# Taille maximale des artefacts
MAX_ARTIFACT_SIZE = 512M
Restauration¶
| Composant | Procedure de restauration | RTO estime |
|---|---|---|
| Gitea Actions | Restaurer Gitea (DB + config), re-enregistrer les runners | < 30 min |
| Flux v2 | flux bootstrap + restaurer secrets depuis Vault | < 15 min |
| Runners | Redeployer via systemd/Kubernetes, re-enregistrer | < 10 min |
| Secrets (Vault) | Restaurer Vault depuis snapshot | < 20 min |
Troubleshooting¶
Runner ne se connecte pas¶
# 1. Verifier la connectivite reseau
curl -v https://git.example.com/api/v1/version
# 2. Verifier le token d'enregistrement
# Les tokens expirent — en regenerer un si necessaire
# 3. Verifier les logs du runner
journalctl -u gitea-runner -f
# 4. Verifier le statut dans Gitea
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
"https://git.example.com/api/v1/admin/runners" | jq .
Image pull failures¶
# 1. Verifier l'acces au registre depuis le runner
podman login harbor.example.com
# 2. Verifier que l'image existe
curl -s "https://harbor.example.com/v2/org/myapp/tags/list" \
-H "Authorization: Basic ${AUTH}"
# 3. Verifier les credentials dans les secrets CI
# Les robot accounts Harbor expirent-ils ?
# 4. Verifier les network policies (le runner peut-il joindre Harbor ?)
kubectl -n ci-runners exec -it runner-pod -- curl -v https://harbor.example.com
Flux reconciliation bloquee¶
# 1. Verifier le statut des Kustomizations
flux get kustomizations
# 2. Voir les evenements detailles
flux events
# 3. Verifier le statut des sources Git
flux get sources git
# 4. Forcer un refresh depuis Git
flux reconcile source git manifests
# 5. Forcer la reconciliation de la Kustomization
flux reconcile kustomization myapp-production --with-source
# 6. Verifier les logs du kustomize-controller
kubectl -n flux-system logs -l app=kustomize-controller --tail=50
# 7. Verifier les logs du source-controller (clone Git)
kubectl -n flux-system logs -l app=source-controller --tail=50
# 8. Suspendre/reprendre si necessaire
flux suspend kustomization myapp-production
flux resume kustomization myapp-production
OOM dans les builds¶
# 1. Verifier les limites memoire du runner
podman stats --no-stream
# 2. Augmenter les limites si necessaire
# Dans config.yaml du runner :
# container.options: --memory=8g
# 3. Optimiser le build
# - Utiliser des builds multi-stages
# - Reduire le nombre de layers
# - Eviter COPY . . (utiliser .dockerignore)
# - Paralleliser moins (GOMAXPROCS=2, npm --max-old-space-size)
# 4. Verifier si le cache est correctement configure
# Un cache absent force un re-telechargement complet a chaque build
Pipeline timeout¶
# 1. Verifier le timeout configure
# runner.timeout dans config.yaml (defaut : 3h)
# 2. Identifier l'etape lente
# Verifier les logs du run dans Gitea UI > Actions > Run > Steps
# 3. Causes frequentes :
# - Test d'integration qui attend une DB non demarree
# - Push d'image lent (bande passante reseau)
# - Build sans cache (tout retelecharge a chaque run)
# - Deadlock dans les tests
Checklist de mise en production¶
| Élément | Verifie | Remarque |
|---|---|---|
| Runners déployés et enregistres | [ ] | Minimum 2 pour la redondance |
| Flux bootstrap effectue | [ ] | flux check passe |
| GitRepository connecte au depot manifests | [ ] | Cle SSH deploy en lecture seule |
| Secrets chiffres via SOPS | [ ] | Cle age sauvegardee dans Vault |
| Network policies appliquees | [ ] | Runners isoles, flux-system protege |
| Branch protection activee | [ ] | CI requis avant merge |
| Drift detection active | [ ] | Feature gate DetectDrift=true |
| Notifications configurees | [ ] | Provider + Alert sur erreurs |
| Monitoring en place | [ ] | Grafana dashboards, metriques Flux |
| Backup secrets Flux dans Vault | [ ] | Cles SSH + SOPS |
| Documentation a jour | [ ] | ADR, runbook, contacts |