БОЛЬ

Джуниор присылает в чат: “У меня всё работает”. Вы клонируете репозиторий, запускаете npm install – ошибка. У него Node 18, у вас Node 22. У него Ubuntu, у вас macOS. У него python3 указывает на 3.10, у вас – на 3.12.

“Работает у меня” – это не баг, это отсутствие воспроизводимой среды. Dockerfile решает проблему: он описывает среду декларативно. Кто бы ни собрал образ – результат будет одинаковый.

КАК УСТРОЕНО

Dockerfile – это текстовый файл с инструкциями для сборки образа. Каждая инструкция создаёт слой (layer) в образе.

Основные инструкции:

ИнструкцияНазначениеПример
FROMБазовый образFROM node:22-alpine
WORKDIRРабочая директорияWORKDIR /app
COPYКопировать файлы из контекстаCOPY . .
RUNВыполнить команду при сборкеRUN npm install
ENVПеременная окруженияENV NODE_ENV=production
EXPOSEДокументация портаEXPOSE 3000
CMDКоманда запуска контейнераCMD ["node", "app.js"]

Порядок инструкций важен: Docker кеширует слои, и если слой не менялся – он берётся из кеша. Об этом подробнее в Level 03.

ПРАКТИКА

Вам нужно собрать образ для Node.js-приложения: взять базовый образ, задать рабочую директорию, скопировать исходники, установить зависимости, указать порт и команду запуска.

Расставьте Dockerfile в правильном порядке

  • CMD ["node", "app.js"]
  • COPY . .
  • RUN npm install
  • EXPOSE 3000
  • FROM node:22-alpine
  • WORKDIR /app
FROM всегда первая инструкция – определяет базовый образ. WORKDIR задаёт директорию для всех последующих команд. COPY копирует исходники, RUN устанавливает зависимости. EXPOSE документирует порт (не открывает его!). CMD – команда при запуске контейнера, всегда последняя.

РАЗБОР

# Базовый образ: Node.js 22 на Alpine Linux (маленький)
FROM node:22-alpine

# Все команды выполняются в /app
WORKDIR /app

# Копируем всё из текущей директории в /app контейнера
COPY . .

# Устанавливаем зависимости
RUN npm install

# Документируем порт (для docker run -P)
EXPOSE 3000

# Команда запуска -- выполнится при docker run
CMD ["node", "app.js"]

Этот Dockerfile работает, но не оптимален. Каждое изменение кода инвалидирует кеш npm install. Как это исправить – в Level 03.

Сборка и запуск:

docker build -t myapp .
docker run -d -p 3000:3000 myapp

ВОПРОС НА СОБЕСЕ

Вопрос: В чём разница между CMD и ENTRYPOINT?

Показать ответ

CMD задаёт команду по умолчанию, которую можно полностью переопределить при docker run:

CMD ["node", "app.js"]
# docker run myapp           -> node app.js
# docker run myapp bash      -> bash (CMD заменён)

ENTRYPOINT задаёт фиксированную команду, а аргументы из docker run дописываются к ней:

ENTRYPOINT ["node"]
CMD ["app.js"]
# docker run myapp           -> node app.js
# docker run myapp server.js -> node server.js (CMD заменён, ENTRYPOINT нет)

На практике: CMD – для приложений, где пользователь может захотеть запустить shell. ENTRYPOINT – для утилит, где команда фиксирована (например, curl, psql). Комбинация ENTRYPOINT + CMD даёт фиксированную команду с дефолтными аргументами.