Контейнеризация стала неотъемлемой частью современной разработки. Однако рост размеров образов и длительное время сборки могут замедлять CI/CD пайплайн. Docker предлагает два мощных инструмента для решения этих проблем: multistage builds и BuildKit. В статье рассматриваются их возможности, сравниваются подходы и приводятся практические рекомендации по оптимизации.
Что такое Docker multistage builds?
Multistage builds позволяют использовать несколько FROM в одном Dockerfile, отделяя этапы сборки от финального образа. Это даёт возможность:
- Уменьшить конечный образ, оставив в нём только необходимые артефакты.
- Избежать установки инструментария (компиляторов, тестовых зависимостей) в продакшн‑контейнере.
- Упростить поддержание единого файла сборки.
Пример простого multistage Dockerfile для Python‑приложения:
# syntax=docker/dockerfile:1.4
FROM python:3.11-slim AS builder
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN pip install poetry && \
poetry config virtualenvs.create false && \
poetry install --no-dev --no-interaction --no-ansi
COPY . .
RUN poetry build -f wheel
FROM python:3.11-slim AS runtime
WORKDIR /app
COPY --from=builder /app/dist/.whl ./
RUN pip install .whl && rm -f *.whl
CMD ["python", "-m", "myapp"]
На этапе builder устанавливаются все зависимости и собирается wheel‑пакет, а в финальном образе runtime оставляются только готовые артефакты.
BuildKit: современный движок сборки Docker
BuildKit – это новый бекенд сборки, включённый в Docker начиная с версии 18.09. Он предоставляет:
- Параллельное выполнение слоёв
RUN. - Кеширование на уровне файловой системы, включая
--mount=type=cache. - Безопасную работу с секретами (
--secret). - Поддержку более гибкого синтаксиса (
# syntax=...).
Для активации BuildKit достаточно установить переменную окружения DOCKER_BUILDKIT=1 или добавить флаг --progress=plain к команде docker build.
Пример использования кэша и секретов в том же Dockerfile:
# syntax=docker/dockerfile:1.4
FROM node:20-alpine AS builder
WORKDIR /app
# Кеш npm пакетов
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
# Пример передачи секретов (например, токен доступа к приватному репозиторию)
# docker build --secret id=npm_token,src=.npmrc
Кеширование ускоряет повторные сборки, а секреты позволяют хранить чувствительные данные вне образа.
Сравнительная таблица возможностей
| Функция | Multistage builds | BuildKit | Комментарий |
|---|---|---|---|
| Уменьшение размера образа | ✔️ (через отдельные стадии) | ✔️ (через --mount=type=cache и --squash) |
Оба подхода совместимы. |
| Параллельное выполнение слоёв | ❌ | ✔️ | Сокращает время сборки, особенно при тяжёлых RUN шагах. |
| Кеширование зависимостей | Ограничено | ✔️ (type=cache) |
Менее затратный CI. |
| Работа с секретами | ❌ | ✔️ (--secret) |
Безопасность CI/CD. |
| Поддержка в облачных CI (GitHub Actions, GitLab CI) | ✔️ | ✔️ (нужна настройка переменных) | Оба решения легко интегрируются. |
Оптимизация CI/CD пайплайна с использованием BuildKit
Ниже пример конфигурации GitHub Actions, где включён BuildKit, используется кеш Docker и секреты:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: 1
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
push: true
tags: user/app:${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
secrets: |
npm_token=${{ secrets.NPM_TOKEN }}
Ключевые моменты:
- Переменная
DOCKER_BUILDKIT=1активирует новый движок. - Buildx предоставляет кроссплатформенную сборку и кеширование.
- Секреты передаются в Docker без их попадания в образ.
Практические рекомендации
- Сочетайте multistage и BuildKit. Используйте multistage для чистоты образа, а BuildKit – для ускорения и кеширования.
- Кешируйте зависимости. Применяйте
--mount=type=cacheв шагахRUN(npm, pip, apt). - Не храните секреты в образе. Передавайте их через
--secretили CI‑переменные. - Оптимизируйте порядок слоёв. Помещайте часто меняющиеся команды в конец Dockerfile, чтобы кешировать более стабильные слои.
- Используйте
# syntax=docker/dockerfile:1.4в первой строке. Это включает новые возможности BuildKit без дополнительных флагов.
Заключение
Docker multistage builds и BuildKit – это не конкуренты, а взаимодополняющие инструменты. Multistage обеспечивает «чистый» финальный образ, а BuildKit ускоряет процесс сборки, предоставляет продвинутый кеш и безопасную работу с секретами. Их совместное применение позволяет существенно сократить размер образов, ускорить CI/CD и повысить безопасность.
Услуги RybinskLab
RybinskLab предлагает профессиональную разработку и оптимизацию контейнерных решений: настройка CI/CD пайплайнов, внедрение Docker‑multistage и BuildKit, аудит образов и их безопасность. Свяжитесь с нами, чтобы вывести ваш проект на новый уровень эффективности.