Пишем свой Dockerfile

Рейтинг: 70.2% · 15 голосов
Практический курс по Docker: образы, контейнеры, тома, сети, Compose и продакшен. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
Marina_DevOps
Сообщения: 25
Зарегистрирован: 11 май 2026, 05:31

Пишем свой Dockerfile

Сообщение Marina_DevOps »

АкадемияDocker с нуляГлава 4 из 17
Оглавление курса (17)
  1. Что такое Docker и какие задачи он решает
  2. Установка Docker и запуск первого контейнера
  3. Образы: слои, теги и реестр Docker Hub
  4. Пишем свой Dockerfile (вы здесь)
  5. Тома и хранение данных: volumes и bind mounts
  6. Сети в Docker: связываем контейнеры между собой
  7. Переменные окружения и конфигурация контейнеров
  8. Docker Compose: поднимаем многоконтейнерное приложение
  9. Оптимизация образов: multi-stage сборка, размер и кэш слоёв
  10. Логи, отладка и мониторинг контейнеров
  11. Базовая безопасность контейнеров
  12. Подготовка к продакшену: что важно учесть
  13. Dockerfile глубже: ENTRYPOINT и CMD, HEALTHCHECK, .dockerignore, запуск не от root
  14. Реестры образов: приватные registry, push и pull, теги и digest, imagePullSecrets
  15. BuildKit и buildx: multi-arch сборки, секреты сборки, экспорт кэша
  16. Docker в CI/CD: автосборка, сканирование образов (Trivy, Docker Scout), публикация
  17. Итоговый проект и куда расти: от Dockerfile до прода, обзор оркестрации (Kubernetes, Podman, OCI)
В третьей главе мы разобрали, как устроены образы: слои, теги, Docker Hub. До сих пор вы запускали чужие образы, теперь соберём собственный. Устроено всё просто: Dockerfile, обычный текстовый файл с инструкциями, по которым docker build шаг за шагом строит образ вашего приложения.

Минимальный рабочий Dockerfile:

Возьмём небольшой API на Node.js: server.js, package.json и package-lock.json. В корне проекта создаём файл с именем Dockerfile, без расширения:

Код: Выделить всё

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Построчно. FROM задаёт базовый образ, с него начинается сборка. Вариант на Alpine в несколько раз легче полного node:22, для большинства приложений его хватает. WORKDIR создаёт каталог /app и делает его рабочим для всех инструкций ниже. COPY переносит файлы из контекста сборки внутрь образа. RUN выполняет команду во время сборки, результат фиксируется отдельным слоем. EXPOSE сам по себе ничего не открывает, это документация: приложение слушает порт 3000. Наружу порт публикуется флагом -p при запуске, как во второй главе. CMD определяет, что выполнится при старте контейнера.

Сборка и контекст:

Код: Выделить всё

docker build -t shop-api:0.1 .
docker run -d -p 3000:3000 --name shop shop-api:0.1
curl http://localhost:3000
Точка в конце команды build задаёт контекст сборки: каталог, содержимое которого целиком отправляется демону Docker. Если в проекте лежит node_modules на гигабайт, всё это поедет в контекст и заметно замедлит сборку. Лечится файлом .dockerignore рядом с Dockerfile:

Код: Выделить всё

node_modules
.git
*.log
.env
Строка .env здесь не для красоты. Секреты не должны попадать в образ: любой, кто скачает его из реестра, спокойно вытащит файл из слоёв.

Почему package.json копируется отдельно:

Каждая инструкция создаёт слой, и Docker кэширует их. Пока файлы, которые берёт COPY, не изменились, слой и всё до следующего изменения подтягиваются из кэша. Зависимости меняются редко, код постоянно. Поэтому сначала копируем только манифесты и ставим пакеты, а весь код заносим после. Правка в server.js пересоберёт лишь последние слои, npm ci отработает из кэша мгновенно. Если же написать COPY . . одной строкой в начале, любое изменение кода будет заново тянуть все зависимости. На CI это разница между сборкой за десять секунд и за три минуты.

CMD и форма записи:

CMD с массивом в квадратных скобках называется exec-формой. Процесс запускается напрямую, получает PID 1 и сигналы вроде SIGTERM. Shell-форма CMD node server.js заворачивает запуск в /bin/sh, и при docker stop приложение может вообще не узнать об остановке: демон подождёт 10 секунд и убьёт контейнер. Корректное завершение так не сделать, пишите exec-форму. Есть ещё ENTRYPOINT: он жёстко фиксирует исполняемую команду, а CMD тогда превращается в аргументы по умолчанию. Для своих приложений на старте хватает одного CMD.

Типичные грабли:

Размазанный apt-get. В образах на Debian или Ubuntu команды update и install обязаны жить в одном RUN, иначе закэшированный слой с update протухнет и install начнёт ставить пакеты по устаревшим индексам:

Код: Выделить всё

RUN apt-get update && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*
FROM с тегом latest. Через полгода latest уедет на новую мажорную версию и сборка сломается в самый неудобный момент. Фиксируйте хотя бы мажор, как node:22-alpine.

Запуск от root. По умолчанию процесс в контейнере работает от root. В официальных образах node уже есть непривилегированный пользователь, достаточно строки USER node перед CMD. Подробнее эту тему разберём в главе про безопасность.

Забытый .dockerignore. Симптом: docker build надолго зависает на шаге "transferring context".

Итог:

Вы умеете описывать образ через FROM, WORKDIR, COPY, RUN, EXPOSE и CMD, собирать его с тегом и выстраивать инструкции так, чтобы кэш слоёв работал на вас. Данные внутри контейнера пока живут ровно до его удаления, в следующей главе подключим тома и разберёмся с хранением. А радикально ужимать образы multi-stage сборкой будем в девятой главе.
👍4 ❤️3 🔥 😄 🤔1
Аватара пользователя
kisel123
Сообщения: 2
Зарегистрирован: 14 май 2026, 05:37

Re: Пишем свой Dockerfile

Сообщение kisel123 »

Marina_DevOps писал(а):EXPOSE сам по себе ничего не открывает, это документация
получается его можно вообще не писать и всё заработает через -p? тогда зачем он нужен, чисто чтобы коллеги в Dockerfile порт увидели?
👍2 ❤️ 🔥 😄 🤔1
Аватара пользователя
johnstro
Сообщения: 1
Зарегистрирован: 21 май 2026, 00:39

Re: Пишем свой Dockerfile

Сообщение johnstro »

спасибо за момент с отдельным COPY package*.json. у нас на gitlab ci сборка шла минуты по четыре, переставил две строчки местами и стало секунд сорок. до этого думал что кэш слоёв это какая-то магия для оптимизаторов
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
kernelwhale
Сообщения: 2
Зарегистрирован: 11 май 2026, 16:55

Re: Пишем свой Dockerfile

Сообщение kernelwhale »

а если у меня python, принцип тот же? сначала COPY requirements.txt и pip install, потом уже COPY . . ? и что брать вместо node:22-alpine, python:3.12-slim норм или тоже alpine искать?
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
ridleyjim1
Сообщения: 2
Зарегистрирован: 13 май 2026, 04:23

Re: Пишем свой Dockerfile

Сообщение ridleyjim1 »

вопрос: почему npm ci, а не npm install? всю жизнь в докерфайлах install писал и вроде работало
👍1 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Образы: слои, теги и реестр Docker Hub
Следующая глава →
Тома и хранение данных: volumes и bind mounts

Все главы курса «Docker с нуля»

Поделиться темой: ✈ Telegram VK

Вернуться в «Docker с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость