Содержание
Как устроен образ Docker
Образ — это набор слоёв (layers). Каждая инструкция в Dockerfile создаёт новый слой. Слои кэшируются: если файл не изменился — Docker не пересобирает этот слой. (Если ты только начинаешь с Docker — сначала пройди базовый гайд по контейнерам.)
Это критически важно для скорости сборки. Неправильный порядок инструкций — и кэш сбрасывается при каждом изменении кода.
Все основные инструкции Dockerfile
# FROM — базовый образ. Всегда первая инструкция
FROM node:22-alpine
# WORKDIR — рабочая директория внутри контейнера
# Лучше явно задавать, а не работать в корне
WORKDIR /app
# COPY — копировать файлы с хоста в контейнер
COPY package*.json ./
# RUN — выполнить команду при сборке
RUN npm ci --only=production
# COPY остальной код (после установки зависимостей!)
COPY . .
# ENV — переменные окружения
ENV NODE_ENV=production
ENV PORT=3000
# EXPOSE — документирует порт (не открывает его сам по себе!)
EXPOSE 3000
# USER — запускать от непривилегированного пользователя
USER node
# CMD — команда по умолчанию при запуске контейнера
CMD ["node", "index.js"]
# ENTRYPOINT — фиксированная точка входа (не перезаписывается при запуске)
# ENTRYPOINT ["node"]
Правильный порядок для кэширования
Главное правило: то, что меняется реже — выше, что меняется чаще — ниже. В CI это правило особенно окупается — см. BuildKit cache в GitHub Actions.
# ❌ Плохо — при любом изменении кода переустанавливаем зависимости
FROM node:22-alpine
WORKDIR /app
COPY . . # Копируем всё сразу
RUN npm install # Этот слой сбросится при ЛЮБОМ изменении файла
CMD ["node", "index.js"]
# ✅ Хорошо — зависимости кэшируются отдельно от кода
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./ # Только package.json — меняется редко
RUN npm ci # Этот слой кэшируется пока package.json не изменится
COPY . . # Код — меняется часто, но зависимости уже закэшированы
CMD ["node", "index.js"]
Multi-stage сборка — уменьшаем размер образа
Классическая проблема: для сборки нужны dev-инструменты (компиляторы, тесты), но в продакшне они не нужны. Multi-stage решает это:
# Стадия 1: сборка
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # Устанавливаем ВСЕ зависимости включая devDependencies
COPY . .
RUN npm run build # Собираем проект
# Стадия 2: продакшн образ
FROM node:22-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production # Только production зависимости
COPY --from=builder /app/dist ./dist # Берём только собранные файлы из стадии 1
USER node
CMD ["node", "dist/index.js"]
💡 Образ из Multi-stage сборки может быть в 5-10 раз меньше. Node.js проект: 1.2 ГБ → 120 МБ.
Оптимизация размера образа
Используй alpine-образы — они минималистичны:
# ❌ 1.1 ГБ
FROM node:22
# ✅ 180 МБ
FROM node:22-alpine
Очищай кэш пакетного менеджера в одной инструкции RUN:
# ❌ Кэш остаётся в слое
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# ✅ Всё в одном слое, кэш удаляется
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
Не копируй лишнее — используй .dockerignore:
node_modules
.git
.env
*.log
dist
coverage
Полезные команды при разработке
# Собрать образ с тегом
docker build -t myapp:1.0 .
# Посмотреть слои и их размеры
docker history myapp:1.0
# Проверить размер образа
docker images myapp
# Запустить и зайти внутрь для отладки
docker run -it myapp:1.0 sh
# Запустить с монтированием кода (для разработки)
docker run -p 3000:3000 -v $(pwd):/app myapp:1.0
⚠️ Никогда не запускай контейнер от root-пользователя в продакшне. Всегда добавляй
USER node(или другого непривилегированного пользователя) передCMD.
Обсуждение
Пока нет комментариев. Будьте первым.