Optimisation de l'assemblage et du cache
(Section : Technologie et infrastructure)
Résumé succinct
La vitesse CI/CD dans iGaming affecte directement la fréquence de sortie, le coût des minutes et la stabilité de p99 en cas de pics de charge. La clé est les caches corrects (dépendances, artefacts, couches de conteneurs, résultats intermédiaires de compilation), les assemblages incrémentiels et la détermination. Le « bon » assemblage se répète rapidement lorsque l'entrée est inchangée, et le cache invalide est prévisible et contrôlable.
1) Carte cache et ce que nous mettons en cache
Dépendances : paquets NPM/pnpm, pip wheels/Poetry, Maven/Gradle, Go mode, Cargo crates, NuGet.
artefacts intermédiaires de compilation : '~/.cache/pip', '~/.m2', '.gradle', '~/.cargo/registry/',' $ GOMODCACHE ',' target/', 'build/',' node _ modules/.pnn-pm ".
Couches conteneurisées : Cache Docker layer (BuildKit/GHA cache), cache basé sur le registre, multi-étapes.
Outils et SDK : toolchains/micro-images (JDK, Node, Python, Rustup targets).
Mono/polyrepo-meta : cache Nx/Turborepo/Bazel remote-cache pour les tâches (lint/test/build).
Données de test et fiches e2e : BD snepshots compilés par les gangs UI.
ML/données : datacets préparés, embeddings, moteurs compilés (TensorRT/ONNX).
2) Principes d'assemblage rapide et prévisible
1. Déterminisme : fixez les versions (lockfiles), les images de base pin, les plugins hermétiques → la sortie reproductible.
2. Idempotence : le même ensemble → les mêmes artefacts et hachages.
3. Incrémentalité : nous ne reprenons que le changement (DAG/needs/matrix/affected).
4. Localité et « chaleur » : caches à côté des runners/dans le registre, échauffement avant les pics.
5. Handicap évident : clés de cache par lockfile/fichiers de configuration et par hachage de contenu.
6. Hygiène : TTL/' expire _ in ', auto-nettoyage, contrôle de la taille des caches et des artefacts.
7. Sécurité de la chaîne d'approvisionnement : cache ≠ poubelle pour les secrets ; La signature SBOM/artefacts reste obligatoire.
3) Docker/OCI : images rapides sans recaptures
Modèles
Multi-stage (builder → runtime).
Runtime minimum (distroless/ubi-micro, seulement so/ca-certs nécessaires).
Ordre des calques : d'abord changeant rarement (deps), puis code.
'.dockerignore' : excluez '.git', tests/fictions, caches locaux.
BuildKit : 'cache-from/to' → cache partagé entre jobs et branches.
Exemple Dockerfile (Node + pnpm)
dockerfile syntax=docker/dockerfile:1.7
FROM node:20-bookworm AS deps
WORKDIR /app
COPY pnpm-lock.yaml./
RUN corepack enable && pnpm fetch
FROM node:20-bookworm AS builder
WORKDIR /app
COPY --from=deps /root/.cache/pnpm /root/.cache/pnpm
COPY package.json pnpm-lock.yaml./
RUN corepack enable && pnpm install --offline
COPY..
RUN pnpm build
FROM gcr.io/distroless/nodejs20 AS runtime
WORKDIR /app
COPY --from=builder /app/dist./dist
USER 10001
CMD ["dist/server.js"]
BuildKit в CI (GitHub Actions)
yaml
- uses: docker/setup-buildx-action@v3
- uses: actions/cache@v4 with:
path: /tmp/.buildx-cache key: buildx-${{ github.ref }}-${{ github.sha }}
restore-keys: buildx-${{ github.ref }}-
- uses: docker/build-push-action@v6 with:
push: true tags: ${{ env.IMAGE }}
cache-from: type=gha cache-to: type=gha,mode=max
4) Écosystèmes linguistiques : Quoi mettre en cache et comment
Java/Kotlin (Maven/Gradle)
Remote cache Gradle, parallélisme, configuration sur demande.
Clé cache : hach'build. gradle[.kts]` + lockfiles + `gradle-wrapper. properties`.
Publication de build cache node sur le stockage objet/HTTP.
Compilation incrémentale et test-split par lots.
yaml
GitLab CI cache:
key: gradle-${CI_COMMIT_REF_SLUG}
paths: [.gradle/caches,.gradle/wrapper ]
script:
-./gradlew --build-cache --parallel build
Node. js (npm/pnpm/yarn)
Cache local ('~/.np',' ~/.cache/pnp'), clé lockfile.
Nx/Turborepo remote-cache (S3/Redis) pour les tâches (lint/test/build).
'turbo run build --cache-dir = .turbo' et 'affected' -recim pour le monorépo.
Python (pip/Poetry)
Cache wheels + virtualenv ; clé par 'requirements. lock`/`poetry. lock`.
L'assemblage des roues dans une étape distincte, la réutilisation entre les matrices.
Pour les extensions C - 'pip wheel' + 'auditwheel' dans l'image de construction.
Go
Кэш `GOMODCACHE`, `GOCACHE`; fixez 'GOTOOLCHAIN '/versions.
Partagez les étapes : 'go mod download' → une copie du code → 'go build'.
Pour les grands monorep - Bazel/Bazelisk ou « mage » avec une feuille de couche.
Rust
`~/.cargo/registry`, `~/.cargo/git`, `target/`; sccache (distant/local).
Partagez le cache entre les branches de 'Cargo. lock`.
C/C++
ccache/sccache + clé par les indicateurs du compilateur et les versions du SDK.
Sortir toolchain dans une image de base séparée.
5) Bazel/Nx/Turborepo : cache des tâches et graphe
Bazel remote cache (HTTP/Cloud) - adressable au contenu ; étanchéité stricte, bac à sable.
Nx/Turborepo - Cache de sortie de tâche et exécution « only-affected » en monorépo.
Handicap : dépend des entrées de l'étape (fichiers/drapeaux/variables).
6) Stratégies d'invalidité du cache
Clé = hachage des entrées : lockfiles, configurations des compilateurs, manifestes.
Handicap léger : 'restore-keys' (GHA )/préfixes (GLCI).
Handicap sévère : rotate namespace/clé en cas de changements critiques.
Séparation des couches : deps vs sources - changez le code sans toucher aux dépendances lourdes.
7) Barres et matrices incrémentales
DAG/needs : démarrez les jobs dépendants en parallèle, n'attendez pas les séquences.
Paths-filter : déclencheur uniquement pour les composants concernés.
Shard tests : par catalogue/seed, aligner la durée.
Warm-pool runner : images préchauffées/cache avant les pics (tournois/campagnes).
8) Artefacts vs cache : ce qui diffère
Cache : entrées surutilisées/résultats intermédiaires, zone « sale », TTL courte/moyenne.
Artefacts : assemblages finaux (images, binaires, charts), immuables, signés, avec SBOM.
Règles : le cache est nettoyé agressivement, les artefacts sont stockés selon la politique de libération.
9) Observabilité, KPI et FinOps
Métriques (par pipline/repère) :- Hit-rate cache (%), Warm-start vs Cold-start time, durée moyenne/médiane des étapes.
- Cost per pipeline/job, taille cache/artefacts, passe (jobs/heure).
- Proportion "affected-runs'dans le monorépo, recycle inchangé (waste).
- Chute de hit-rate en dessous du seuil, augmentation du temps « build image », gonflement des artefacts, défauts de SLO.
10) Sécurité de cache et chaîne d'approvisionnement
Pas de secrets dans le cache/artefacts ; masque vars, scans de secrets.
Pin SHA actions externes/plugins, runners de confiance seulement.
Signature des conteneurs/binaires (cosign), SBOM (CycloneDX/SPDX) et vérification sur CD.
Isolation : Cache namespace séparé pour 'dev/stage/prod', droits « lecture seule » pour les marques étrangères.
11) Modèles pratiques
GitHub Actions - Langue + Conteneur
yaml name: ci on: [push, pull_request]
concurrency: { group: ${{ github.ref }}, cancel-in-progress: true }
jobs:
build:
runs-on: ubuntu-latest steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: { node-version: '20' }
- uses: actions/cache@v4 with:
path: ~/.cache/pnpm key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
- run: corepack enable && pnpm i --frozen-lockfile
- run: pnpm build
- uses: docker/build-push-action@v6 with:
tags: ${{ env.IMAGE }}
cache-from: type=gha cache-to: type=gha,mode=max
GitLab CI — Gradle remote cache
yaml variables:
GRADLE_USER_HOME: ".gradle"
cache:
key: gradle-${CI_COMMIT_REF_SLUG}
paths: [.gradle/wrapper,.gradle/caches ]
build:
stage: build script:
-./gradlew --build-cache --no-daemon build artifacts:
paths: [ "build/libs/.jar" ]
expire_in: 3 days
Jenkins — ccache/sccache
groovy pipeline {
agent { label 'cpp' }
environment { CCACHE_DIR = '/cache/ccache' }
stages {
stage('Build') {
steps {
sh 'ccache -M 10G'
sh 'cmake -B build -S. && cmake --build build -j$(nproc)'
}
}
}
post { always { sh 'ccache -s' } }
}
12) ML/données : nous accélérons les assemblages lourds
Cache modèles/embeddings/datacets sur les runners NVMe locaux ; versioning par hash.
Pré-assemblage des moteurs TensorRT/ONNX en tant qu'artefacts de sortie ; réutilisation dans l'inference.
Chunked-artefacts (splits) pour les grands modèles ; TTL et nettoyage différé.
13) Chèque de mise en œuvre
1. Fixez les lockfiles et les images de base ; activez BuildKit.
2. Séparez les calques Docker : deps → code ; ajoutez '.dockerignore'.
3. Soulevez le cache à distance (Gradle/Bazel/Nx/Turbo) ; lancez dependency proxy.
4. Configurez le cache de dépendance dans CI par lockfile ; inclure les matrices et 'paths-filter'.
5. Entrez les factures incrémentielles et « affected only » dans le monorépo.
6. Mesurer hit-rate, warm/cold time, coût ; exposez les alertes.
7. Activer SBOM/signature, interdire les secrets dans le cache.
8. Chauffer les caches avant les sorties de pointe ; réglementer la TTL/retraite.
9. Documentez l'invalidité des caches et des runbooks sur « cache cassé ».
10. Nettoyez régulièrement les caches « éternels » et archivez les artefacts lourds.
14) Anti-modèles
Énorme Dockerfile avec des étapes souvent changeantes avant l'installation deps.
Cache « perpétuel » partagé sans clés/TTL → flocons et ordures.
Mélange des artefacts et du cache ; absence de signature/SBOM.
Les versions non imprimées des outils → « le cache est là, mais ne se répète pas ».
Matrices « pour tous » à chaque PR ; absence de 'concurrency. cancel`.
Un seul runner sans cache chaud et sans dependency proxy.
Résultats
L'optimisation de l'assemblage est un système de cache, d'incrémentation et de déterminisme. La bonne structure de Dockerfile, remote-cache pour les bilds, dependency proxy et la discipline de handicap donnent des convoyeurs rapides, bon marché et reproductibles. Ajoutez des règles d'observation et de sécurité - et vos sorties seront fréquentes, stables et économiques.