Как минимизировать размер Docker образа используя многоступенчатые сборки? Опишите свой подход к оптимизации процесса сборки приложения TypeScript для backend.
Подсказки:
- Подумайте о разделении среды сборки от среды выполнения в прод на разные образа.
- Рассмотрите использование
node:alpine
в качестве базового образа. - Обратите внимание на стратегию копирования
package.json
иnode_modules
.
Выше ожиданий:
- Понимание механики кэширования слоёв Docker и возможностей buildkit.
- Знание лучших практик безопасности контейнеров и сканирования уязвимостей контейнеров.
- Расширенные конфигурации npm/yarn workspace и обработка монорепозитория.
Многоэтапные (multi-stage) сборки в Docker помогают создать оптимизированные образы, разделяя среду сборки и среду выполнения.
- Организация этапов сборки
Разделите Dockerfile на отдельные этапы:
- Этап зависимостей для разработки
- Этап сборки
- Этап среды выполнения для производства
Это разделение позволяет включать только необходимые артефакты в конечный образ, существенно уменьшая его размер.
- Управление зависимостями
Установите зависимости в отдельном слое, чтобы эффективно использовать кэш Docker:
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
# Установка только зависимостей производства
RUN npm ci --only=production
Для зависимостей разработки и инструментов сборки:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json ./
# Установка всех зависимостей, включая devDependencies
RUN npm ci
COPY . .
RUN npm run build
- Оптимизация слоев
Используйте .dockerignore
, чтобы исключить нежелательные файлы:
node_modules
npm-debug.log
dist
coverage
.git
.env
*.md
Это предотвращает копирование ненужных файлов в образ и инвалидацию кэша.
- Среда выполнения для производства
Используйте минимальный базовый образ для производства:
FROM node:18-alpine AS runner
WORKDIR /app
# Копирование только зависимостей производства
COPY --from=deps /app/node_modules ./node_modules
# Копирование собранного приложения
COPY --from=builder /app/dist ./dist
- Управление системными библиотеками
- Установите необходимые системные пакеты в одной команде RUN
- Удалите кэш пакетов после установки
- Используйте виртуальные пакеты в Alpine для временных зависимостей сборки
Пример обработки системных библиотек:
RUN apk add --no-cache --virtual .build-deps \
python3 \
make \
g++ \
&& npm ci \
&& apk del .build-deps
- Безопасность контейнеризированного приложения
- Используйте конкретные теги версий для базовых образов
- Запускайте контейнер как пользователя, отличного от root
- Реализуйте многоэтапные сборки для уменьшения поверхности атаки
- Регулярное сканирование уязвимостей с помощью инструментов, таких как Snyk или Trivy
- Дополнительные методы оптимизации
Для установок monorepo:
- Используйте сборки, специфичные для рабочего пространства
- Организация общих зависимостей между рабочими пространствами
- Выборочное копирование файлов рабочего пространства
Оптимизация управления пакетами:
- Используйте package-lock.json или yarn.lock для детерминированных сборок
- Реализуйте hoisting зависимостей в monorepo
- npm ci вместо npm install для чистых установок
- Функции BuildKit
Включите функции BuildKit для лучшей производительности:
- Параллельное выполнение этапов
- Улучшенный кэширование слоёв
- Монтирование кэша для node_modules
Пример использования кэша BuildKit:
# syntax=docker/dockerfile:1.4
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=production
- Development vs Production конфигурации
Поддерживайте отдельные конфигурации Dockerfile:
- Разработка: можно включить доп инструменты разработки и source maps для сборки
- Пром стенды: минимальная среда выполнения
- Лучшие практики для приложений TypeScript
- Комплияция TypeScript на этапе сборки
- Включение в образ только скомпилированного JavaScript код для выполнения
- Оптимизации в tsconfig.json для пром сборок
Пример полного оптимизированного Dockerfile:
# syntax=docker/dockerfile:1.4
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=production
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runner
WORKDIR /app
USER node
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
CMD ["node", "dist/main.js"]
Примеры запуска:
# Build the deps stage
docker build --target deps -t myapp-deps .
# Run the deps stage as an independent container
docker run --rm -it myapp-deps
# Build the builder stage
docker build --target builder -t myapp-builder .
# Run the builder stage as an independent container
docker run --rm -it myapp-builder
# Build the runner stage
docker build --target runner -t myapp-runner .
# Run the runner stage as an independent container
docker run --rm -it myapp-runner
Этот подход гарантирует:
- Минимальный размер образа
- Эффективное использование кэша
- Безопасную среду выполнения
- Оптимизированный процесс сборки
- Правильное управление зависимостями
Регулярный мониторинг и обновления:
- Отслеживание изменений размера образа
- Мониторинг обновлений зависимостей
- Сканирование на предмет уязвимостей
- Регулярное обновление базовых образов