Как собирается образ
Образ собирается слоями, сверху вниз. Docker читает Dockerfile строку за строкой и выполняет каждую инструкцию, а каждая инструкция добавляет новый слой поверх предыдущих. Так, шаг за шагом, и набирается готовый образ.

Зачем вообще знать про слои, станет ясно чуть ниже, когда дойдем до кеширования: именно от слоев зависит скорость сборки.
Главные инструкции

Этих пяти инструкций достаточно для большинства приложений.
Упаковываем Python-приложение
Допустим, у нас простое веб-приложение на Python:
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "app.py"]Разберем по строкам.
-
FROM python.12 берет за основу готовый образ с уже установленным Python 3.12. Систему с нуля мы не собираем, внутри этого образа уже есть Linux и Python, нам остается добавить свое приложение. FROM всегда стоит первой инструкцией в Dockerfile.
-
WORKDIR /app задает рабочую папку внутри образа, и все следующие команды выполняются относительно нее. По сути это
cd /appна всю оставшуюся сборку. Если папки нет, Docker ее создаст. -
COPY requirements.txt . копирует файл со списком зависимостей с вашего компьютера внутрь образа, в текущую папку
/app, на которую указывает точка. Самrequirements.txt– это перечень библиотек, нужных приложению. -
RUN pip install -r requirements.txt выполняет установку зависимостей прямо во время сборки образа, и после этого все нужные библиотеки зашиты внутрь. RUN срабатывает один раз при сборке, а не при запуске.
-
COPY . . копирует весь остальной код приложения: первая точка означает «откуда», из текущего каталога на вашем компьютере, вторая – «куда», в
/appвнутри образа. -
EXPOSE 8080 объявляет, что приложение внутри слушает порт 8080. Это подсказка о том, на каком порту работает приложение. Сам порт она не открывает, но помогает понять, что пробрасывать наружу.
-
CMD [“python”, “app.py”] задает, что запустить, когда контейнер стартует. В отличие от RUN, который выполняется при сборке, CMD срабатывает при запуске контейнера.
Почему COPY в два захода
Обратите внимание: requirements.txt копируется отдельной строкой, а весь остальной код – позже, отдельным COPY. Это сделано не для красоты, а ради кеша.
Docker кеширует каждый слой. Если слой не изменился, Docker берет его из кеша и не пересобирает. А вот начиная с первого изменившегося слоя пересобирается уже все, что идет ниже.

Дальше работает простое наблюдение: зависимости меняются редко, а код правят постоянно. Поэтому зависимости копируются и ставятся первыми, и этот тяжелый шаг оседает в кеше, а код кладется уже после него. Когда вы в очередной раз поправили код, Docker не переустанавливает библиотеки заново, ведь их слой не тронут. На больших проектах это экономит минуты на каждой сборке.
Собираем образ
Когда Dockerfile готов, собираем образ:
docker build -t myapp .| Часть команды | Что делает |
|---|---|
docker build | собирает образ из Dockerfile |
-t myapp | называет (tag) его myapp |
. | указывает, что Dockerfile искать в текущей папке |
Docker прочитает Dockerfile, выполнит все инструкции по порядку, и на выходе получится образ с именем myapp. Проверить, что он появился:
docker images
Запускаем
Свой образ запускается так же, как раньше запускался nginx:
docker run -d -p 8080:8080 myapp
Приложение работает в контейнере. Этот контейнер можно запустить на любой машине с Docker, на сервере, на ноутбуке коллеги, в облаке, и везде он заведется одинаково, потому что все окружение зашито внутрь образа.
Что еще бывает в Dockerfile
Кроме основных пяти, есть и другие инструкции, которые встретятся позже: ENV для переменных окружения, ARG для аргументов сборки, ENTRYPOINT (похоже на CMD, но со своими нюансами), USER для выбора пользователя, под которым идет запуск, VOLUME для данных.