Структурная инженерия и дизайн
Пошаговое руководство по созданию многоступенчатых изображений Docker
Table of Contents
Что такое многоступенчатые докерные сборки?
Многоступенчатые сборки Docker — это функция, введённая в Docker 17.05, которая позволяет использовать несколько высказываний в рамках одного Dockerfile. Каждая инструкция начинает новый этап, который может иметь собственное базовое изображение, зависимости и команды. Артефакты могут быть выборочно скопированы с одного этапа на другой, в то время как окончательное изображение сохраняет только то, что строго необходимо для запуска приложения. Этот подход устраняет необходимость вручную цеплять Dockerfiles или полагаться на сложные скрипты сборки, и резко уменьшает размер изображения, исключая компиляторы, библиотеки разработки и промежуточные файлы.
Основная идея состоит в том, чтобы отделить среду сборки от среды выполнения. В типичной одноступенчатой сборке разработчик устанавливает все инструменты сборки (например, компиляторы, менеджеры пакетов, тестовые рамки) в том же изображении, которое будет использоваться для производства. Это раздувает изображение и увеличивает поверхность атаки. Многоступенчатые сборки решают это, используя толстое, богатое функциями изображение для компиляции, а затем копируют только полученные артефакты в минимальное изображение выполнения, такое как или .
Преимущества многоэтапных конструкций
Преимущества внедрения многоступенчатых сборок выходят за рамки простого уменьшения размера. Они оказывают глубокое влияние на безопасность, ремонтопригодность и скорость развертывания.
1.Уменьшенный размер изображения
Отбрасывая зависимости от времени сборки, многоступенчатые сборки часто сокращают изображения на 50-90%. Например, приложение Node.js, построенное с использованием полного изображения (более 300 МБ), может быть уменьшено до менее 20 МБ, копируя только встроенную папку в базу . Эта экономия памяти напрямую приводит к более быстрому времени тяги, меньшей пропускной способности сети и более низким затратам на реестр.
2. Улучшение безопасности
Каждый установленный пакет или инструмент в образе контейнера является потенциальной уязвимостью. Многоступенчатые сборки позволяют исключить компиляторы, отладчики и библиотеки разработки из конечного изображения, значительно уменьшая поверхность атаки. Можно даже использовать изображения, подобные или для стадии выполнения, которые содержат только голый минимум для выполнения двоичного приложения.
3.Упорядоченный процесс строительства
Все этапы сборки определяются в одном Dockerfile, что делает процесс автономным и простым в версии. CI/CD трубопроводы получают выгоду от одной точки входа: Dockerfile. Нет необходимости поддерживать отдельные скрипты сборки или ручные этапы очистки.
4. Повышение воспроизводимости и последовательности
Поскольку вся сборка захвачена в одном Dockerfile, любой разработчик или система может воспроизводить одни и те же слои. Использование определенных тегов версий для базовых изображений дополнительно гарантирует согласованные сборки в разных средах.
Создание многоступенчатого докерфила: шаг за шагом
Этот шаг охватывает создание готового к производству многоступенчатого Dockerfile для приложения Node.js и React. Те же принципы применимы к любому компилируемому языку.
1 Планируйте свои этапы
Перед написанием кода наметьте нужные вам этапы. Типичная многоступенчатая сборка имеет как минимум два этапа:
- Стадия сборки — устанавливает все инструменты сборки, устанавливает зависимости и запускает команду сборки.
- Стадия выполнения — использует минимальное базовое изображение, копирует только встроенные артефакты со стадии сборщика и определяет поведение среды выполнения.
Для сложных проектов можно добавить промежуточные этапы для тестирования, статического анализа или сжатия активов.
2.Написать этап строительства
Начните с базового изображения, которое включает в себя требуемую инструментальную цепочку. Используйте названные этапы с , чтобы ссылаться на них позже.
FROM node:14-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
Ключевые моменты:
- Используйте для детерминированной, более быстрой установки зависимости.
- Сохраняйте команды в качестве отдельных слоев, когда это возможно, чтобы использовать кэширование.
- Установите инструменты сборки (например, TypeScript, webpack) только на этом этапе.
3. Добавить промежуточный этап тестирования (факультативно)
Чтобы обеспечить качество кода, добавьте этап, на котором выполняются тесты. Этот этап может повторно использовать изображение конструктора или установить дополнительные инструменты. Поскольку это не окончательный этап, тестовые сбои не будут присутствовать на конечном изображении.
FROM builder AS test
RUN npm run test
Вы можете запустить этот этап в своем конвейере CI с помощью , чтобы быстро улавливать сбои, не создавая всего окончательного изображения.
4.Определить этап выполнения
Для приложения React изображение во время выполнения может быть сервером Nginx. Для бэкэнд-API это может быть безотказное базовое изображение или минимальная альпийская версия с временем выполнения Node.js. Копируйте только основные артефакты с помощью .
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Если вам нужно время выполнения Node.js, избегайте копирования от застройщика; вместо этого переустановите производственные зависимости на этапе выполнения:
FROM node:14-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]
5.Создание и тестирование изображения
Создайте окончательное изображение, используя стандартную команду:
docker build -t myapp:latest .
Чтобы проверить размер, запустите и сравните с одноступенчатой сборкой. Запустите контейнер и подтвердите, что приложение отвечает правильно:
docker run -d -p 8080:80 myapp:latest
curl http://localhost:8080
Лучшие практики для многоэтапных зданий
- Используйте специальные базовые теги изображения — избегайте , чтобы предотвратить сюрпризы. или
- Оптимизация кэширования слоя — копирование и перед остальной частью исходного кода, так что слой недействителен только при изменении зависимостей.
- Перенос в билдинг — включите BuildKit с для более быстрых сборок, встроенного кэширования и лучшего параллелизма.
- Создать несколько конечных стадий для различных сред — например, этап разработки с отладчиками и этап производства с затвердевшим базовым изображением.
- Использовать для сборки — в разработке вы можете остановиться на стадии , чтобы получить живую перезагрузку и исходные карты, а затем перестроить с для производства.
- Сохраняйте секреты из изображений — используйте флаг Docker BuildKit , если вам нужно пройти учетные данные во время сборки; никогда не включайте их в окончательное изображение.
Общие шаблоны и случаи использования
Компиляционные языки (Go, Rust, C++)
Для статических двоичных файлов этап выполнения может использовать (пустое базовое изображение). Только двоичный и, возможно, конфигурационный файл копируются. Пример для Go:
FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o myapp .
FROM scratch
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
Приложения Python
Используйте этап сборки с для установки зависимостей и компиляции любых расширений C, а затем копируйте только установленные пакеты на этап выполнения:
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
COPY . .
FROM python:3.11-slim
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app /app
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
Frontend с API сервером
Создайте как интерфейс, так и бэкэнд в одном Dockerfile. Используйте отдельные этапы сборки для каждого, а затем скопируйте оба артефакта в одно изображение во время выполнения:
FROM node:14-alpine AS frontend-builder
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ .
RUN npm run build
FROM node:14-alpine AS api-builder
WORKDIR /app
COPY api/package*.json ./
RUN npm ci
COPY api/ .
RUN npm run build
FROM node:14-alpine
WORKDIR /app
COPY --from=frontend-builder /app/build ./public
COPY --from=api-builder /app/dist ./dist
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]
Устранение неполадок многоступенчатых конструкций
- Кэширование слоя не работает — убедитесь, что команды заказывают зависимости перед исходным кодом. Используйте для исключения ненужных файлов.
- Артефакт не найден — проверьте пути в заявлении. Стадия строителя должна производить выход в указанном месте.
- Секретная утечка — никогда не копируйте целые каталоги, которые могут содержать или .
- Большие финальные изображения, несмотря на многоступенчатость — проверьте, случайно ли вы копируете или весь источник.
Заключение
Многоступенчатые сборки Docker являются краеугольным камнем современной контейнеризации. Они позволяют отправлять экономичные, безопасные изображения, сохраняя процесс сборки простым и документированным в одном Dockerfile. Разделяя проблемы между сборкой и временем выполнения, вы можете резко уменьшить размер изображения, улучшить безопасность и упростить конвейеры CI / CD. Методы, показанные здесь, применяются практически к любому стеку - будь то создание Node.js, Go, Python, Java или фронтенд приложений. Начните с преобразования существующих Dockerfiles в многоступенчатые сборки, и вы сразу увидите улучшение скорости сборки, эффективности развертывания и общих затрат на инфраструктуру.
Для получения более подробной информации обратитесь к официальной многоступенчатой документации Docker по сборке и Руководство по передовым методам Dockerfile . Примеры реального мира также доступны в документации Docker Library.