Оптимізація збірки та кешування
(Розділ: Технології та Інфраструктура)
Коротке резюме
Швидкість CI/CD в iGaming безпосередньо впливає на частоту релізів, вартість хвилин і стійкість p99 при пікових навантаженнях. Ключ - правильні кеші (залежності, артефакти, шари контейнерів, проміжні результати компіляції), інкрементальні збірки і детермінованість. «Хороша» збірка швидко повторюється при незмінному введенні, а кеш-інвалідація передбачувана і контрольована.
1) Карта кешів і що ми кешуємо
Залежності: пакети NPM/pnpm, pip wheels/Poetry, Maven/Gradle, Go моди, Cargo crates, NuGet.
Проміжні артефакти компіляції: `~/.cache/pip`, `~/.m2`, `.gradle`, `~/.cargo/registry/`, `$GOMODCACHE`, `target/`, `build/`, `node_modules/.pnpm-store`.
Контейнерні шари: Docker layer cache (BuildKit/GHA cache), registry-based кэш, multi-stage.
Інструменти та SDK: toolchains/мікрообрази (JDK, Node, Python, Rustup targets).
Моно/полірепо-мета: кеш Nx/Turborepo/Bazel remote-cache для завдань (lint/test/build).
Тест-дані та e2e фікстури: снепшоти БД, скомпільовані UI-бандли.
ML/дані: підготовлені датасети, ембеддинги, скомпільовані рушії (TensorRT/ONNX).
2) Принципи швидкої і передбачуваної збірки
1. Детермінованість: фіксуйте версії (lockfiles), pin базові образи, hermetic-плагіни → відтворюваний вивід.
2. Ідемпотентність: одна і та ж збірка → однакові артефакти і хеші.
3. Інкрементальність: пересобираємо лише те, що змінилося (DAG/needs/matrix/affected).
4. Локальність і «тепло»: кеші поруч з раннерами/в реєстрі, прогрівши перед піками.
5. Явна інвалідація: ключі кешу по lockfile/файлам конфігурації і по хешу вмісту.
6. Гігієна: TTL/' expire _ in', авто-очищення, контроль розміру кешів і артефактів.
7. Безпека ланцюжка поставки: кеш ≠ смітник для секретів; SBOM/підпис артефактів залишаються обов'язковими.
3) Docker/OCI: швидкі образи без пересборів
Патерни
Multi-stage (builder → runtime).
Мінімальний runtime (distroless/ubi-micro, тільки потрібні so/ca-certs).
Порядок шарів: спочатку рідко мінливе (deps), потім код.
`.dockerignore`: виключайте'.git', тести/фікстури, локальні кеші.
BuildKit: 'cache-from/to'→ загальний кеш між джобами і гілками.
Приклад 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) Мовні екосистеми: що кешувати і як
Java/Kotlin (Maven/Gradle)
Remote cache Gradle, паралелізм, конфігурація-за-запитом.
Ключ кешу: хеш'build. gradle[.kts]` + lockfiles + `gradle-wrapper. properties`.
Публікація build cache node на об'єктному storage/HTTP.
Інкрементальна компіляція і тест-split по пакетах.
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)
Кеш локального store ('~/.npm','~/.cache/pnpm'), ключ по lockfile.
Nx/Turborepo remote-cache (S3/Redis) для завдань (lint/test/build).
'turbo run build --cache-dir = .turbo'і «affected» -режим для монорепо.
Python (pip/Poetry)
Кеш wheels + virtualenv; ключ по'requirements. lock`/`poetry. lock`.
Збірка wheels в окремій стадії, перевикористання між матрицями.
Для C-розширень -'pip wheel'+'auditwheel'в builder-образі.
Go
Кэш `GOMODCACHE`, `GOCACHE`; фіксуйте'GOTOOLCHAIN '/версії.
Діліть кроки: 'go mod download'→ копія коду →'go build'.
Для великих монореп - Bazel/Bazelisk або'mage'з шаровим білдом.
Rust
`~/.cargo/registry`, `~/.cargo/git`, `target/`; sccache (віддалений/локальний).
Спільне використання кешу між гілками по'Cargo. lock`.
C/C++
ccache/sccache + ключ по прапорах компілятора і версіях SDK.
Винос toolchain в окремий базовий образ.
5) Bazel/Nx/Turborepo: кеш завдань і графа
Bazel remote cache (HTTP/Cloud) - контент-адресований; сувора герметичність, пісочниці.
Nx/Turborepo - кеш виходів завдань і «only-affected» виконання в монорепо.
Інвалідація: залежить від входів кроку (файли/прапори/змінні).
6) Стратегії інвалідації кеша
Ключ = хеш входів: lockfiles, конфіги компіляторів, маніфести.
М'яка інвалідація: 'restore-keys'( GHA )/префікси (GLCI).
Жорстка інвалідація: rotate namespace/ключ при критичних змінах.
Розділення шарів: deps vs sources - змінюйте код без зворушення важких залежностей.
7) Інкрементальні білди та матриці
DAG/needs: запускайте залежні джоби паралельно, не чекайте послідовностей.
Paths-filter: тригер тільки для порушених компонентів.
Shard тестів: за каталогами/seed, вирівнюйте тривалість.
Warm-pool раннерів: попередньо прогріті образи/кеші перед піками (турніри/кампанії).
8) Артефакти vs кеш: чим відрізняються
Кеш: перевикористовувані входи/проміжні результати, «брудна» область, TTL короткий/середній.
Артефакти: фінальні збірки (образи, бінарники, charts), незмінні, підписані, з SBOM.
Правила: кеш чиститься агресивно, артефакти - зберігаються по політиці релізів.
9) Спостережуваність, KPI і FinOps
Метрики (по пайплайну/репо):- Hit-rate кеша (%), Warm-start vs Cold-start time, середня/медіана тривалості стадій.
- Cost per pipeline/job, розмір кешу/артефактів, пропускна (jobs/hour).
- Частка «affected-runs» в монорепо, перезбірка без змін (waste).
- Падіння hit-rate нижче порога, зростання часу «build image», роздування артефактів, промахи по SLO.
10) Безпека кешу і supply chain
Ніяких секретів в кеші/артефактах; mask vars, скани секретів.
Pin SHA зовнішніх actions/plugins, тільки довірені runners.
Підпис контейнерів/бінарів (cosign), SBOM (CycloneDX/SPDX) і перевірка на CD.
Ізоляція: роздільні namespace кешу для'dev/stage/prod', права «тільки читання» для foreign branches.
11) Практичні шаблони
GitHub Actions - мова + контейнер
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/дані: прискорюємо важкі збірки
Кеш моделей/ембеддингів/датасетів на локальних NVMe раннерів; версіонування по хешу.
Передзбирання TensorRT/ONNX рушіїв як реліз-артефактів; повторне використання в інференсі.
Chunked-артефакти (splits) для великих моделей; TTL і відкладене очищення.
13) Чек-лист впровадження
1. Зафіксуйте lockfiles і базові образи; увімкніть BuildKit.
2. Розділіть шари Docker: deps → код; додайте'.dockerignore'.
3. Підніміть remote cache (Gradle/Bazel/Nx/Turbo); заведіть dependency proxy.
4. Налаштуйте кеш залежностей в CI по lockfile; увімкніть матриці і'paths-filter'.
5. Введіть інкрементальні білди і «affected only» в монорепо.
6. Міряйте hit-rate, warm/cold time, вартість; Виставити алерти.
7. Увімкніть SBOM/підпис, забороните секрети в кеші.
8. Прогрівайте кеші перед піковими релізами; регламентуйте TTL/retention.
9. Документуйте інвалідацію кешів і runbooks на «кеш зламаний».
10. Регулярно чистіть «вічні» кеші і архівуйте важкі артефакти.
14) Антипатерни
Величезний Dockerfile з часто мінливими кроками до установки deps.
Загальний «вічний» кеш без ключів/TTL → флейки і сміття.
Змішування артефактів і кешу; відсутність підпису/SBOM.
Неприджаті версії інструментів → «кеш є, але не повторюється».
Матриці «на все» при кожному PR; відсутність'concurrency. cancel`.
Єдиний runner без теплого кешу і без dependency proxy.
Підсумки
Оптимізація збірки - це системна робота з кешами, інкрементальністю і детермінованістю. Правильна структура Dockerfile, remote-cache для білдів, dependency proxy і дисципліна інвалідації дають швидкі, дешеві і відтворювані конвеєри. Додайте спостережуваність і правила безпеки - і ваші релізи будуть частими, стабільними і економними.