Перейти к содержимому
TelegramGitHub

Свой образ: Dockerfile

Dockerfile — это текстовый файл с рецептом образа. Каждая строка — один шаг. Командой docker build Docker читает рецепт и собирает образ.

Так вы упаковываете свой скрипт или пайплайн, чтобы он работал одинаково на любом компьютере.

ИнструкцияЧто делает
FROMБерёт готовый образ за основу. Всегда первая строка
WORKDIRЗадаёт рабочую папку в образе. Если её нет, создаёт
COPYКопирует файлы с вашего компьютера в образ
RUNВыполняет команду при сборке (например, pip install)
ENVЗадаёт переменную окружения
CMDКоманда, которая запускается при старте контейнера
ENTRYPOINTПрограмма, которая запускается при старте. CMD становится её аргументами

RUN работает один раз, при сборке. CMD работает каждый раз, когда вы запускаете контейнер.

Учебный скрипт app/report.py читает orders.csv и печатает выручку по каналам. Ему нужен только Python. Упакуем его.

Перейдите в папку курса и создайте файл Dockerfile:

Окно терминала
cd ~/docker-practice
FROM python:3.12-slim
WORKDIR /app
COPY app/ .
CMD ["python", "report.py"]

Что здесь происходит:

  1. Берём python:3.12-slim. В нём уже есть Python.
  2. Рабочая папка в образе — /app.
  3. Копируем всё из папки app/ с вашего компьютера в /app.
  4. При старте запускаем python report.py.

Соберите образ:

Окно терминала
docker build -t orders-report:1.0 .
  • -t orders-report:1.0 — имя и тег образа.
  • Точка . в конце — контекст сборки. Это папка, из которой COPY берёт файлы. Точка — текущая папка.

В конце вы увидите:

=> naming to docker.io/library/orders-report:1.0

Проверьте:

Окно терминала
docker images orders-report
REPOSITORY TAG IMAGE ID CREATED SIZE
orders-report 1.0 57062938303c ... 145MB

Запустите образ и подключите данные через bind mount (урок про тома):

Окно терминала
docker run --rm -v "$PWD/data:/data" orders-report:1.0
Файл: /data/orders.csv
Заказов: 500
Выручка по каналам (только completed):
organic 23715.82
ads 23082.74
email 15459.67

Мы не положили orders.csv в образ. Данные меняются, а образ — это программа. Данные подключают снаружи.

Запустите без -v:

Окно терминала
docker run --rm orders-report:1.0
Traceback (most recent call last):
File "/app/report.py", line 11, in <module>
with open(path, newline="") as f:
^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/data/orders.csv'

Скрипт ищет /data/orders.csv, а в контейнере такой папки нет. Это ошибка в запуске, а не в образе.

Путь к файлу скрипт берёт из переменной ORDERS_PATH. Её можно поменять без пересборки:

Окно терминала
docker run --rm -v "$PWD/data:/mnt" -e ORDERS_PATH=/mnt/orders.csv orders-report:1.0

Первая строка ответа теперь Файл: /mnt/orders.csv.

Хотите задать путь по умолчанию в образе? Добавьте в Dockerfile строку ENV ORDERS_PATH=/data/orders.csv. Флаг -e при запуске всё равно её перекроет.

CMD — команда по умолчанию. Её легко заменить при запуске:

Окно терминала
docker run --rm orders-report:1.0 python --version
Python 3.12.16

Всё, что вы пишете после имени образа, заменяет CMD. Версия Python у вас может быть другой.

Окно терминала
docker run --rm orders-report:1.0 ls
load_orders.py
report.py
requirements.txt

Так видно, что WORKDIR /app работает: ls показывает файлы в /app. Здесь же лежит load_orders.py. Мы запустим его позже, в Compose.

ENTRYPOINT — программа, которая запускается всегда. То, что вы пишете после имени образа, превращается в её аргументы.

ENTRYPOINT ["python"]
CMD ["report.py"]
КомандаЧто запустится
docker run образpython report.py
docker run образ --versionpython --version
docker run образ other.pypython other.py

Так устроены образы-инструменты: dbt, aws, terraform. Образ — это сама программа, вы передаёте ей аргументы. Для обычного приложения хватает CMD.

Команду пишут двумя способами:

ФормаПримерКогда
execCMD ["python", "report.py"]Всегда по умолчанию. Программа получает сигналы (docker stop) напрямую
shellCMD python report.pyDocker запускает /bin/sh -c. Сигнал получает shell, а не ваша программа

С формой shell docker stop может ждать 10 секунд и убить программу принудительно. Пишите форму exec: квадратные скобки и двойные кавычки.

Каждая инструкция делает слой образа. Посмотрите их:

Окно терминала
docker history orders-report:1.0
CREATED BY SIZE
CMD ["python" "report.py"] 0B
COPY app/ . # buildkit 1.9kB
WORKDIR /app 0B
CMD ["python3"] 0B
...

Ваши три слоя — сверху. Ниже идут слои базового образа Python. Наш код весит 1.9 КБ. Остальные 145 МБ — это Python.

Слои кэшируются. Как этим пользоваться — в следующем уроке.

Работайте в папке ~/docker-practice.

Создайте Dockerfile для report.py, как выше. Соберите образ orders-report:1.0. Запустите его с данными.

Проверка:

Файл: /data/orders.csv
Заказов: 500
Выручка по каналам (только completed):
organic 23715.82
ads 23082.74
email 15459.67
Решение
FROM python:3.12-slim
WORKDIR /app
COPY app/ .
CMD ["python", "report.py"]
Окно терминала
docker build -t orders-report:1.0 .
docker run --rm -v "$PWD/data:/data" orders-report:1.0

Запустите образ без -v. Прочитайте ошибку. Исправьте запуск, не меняя образ.

Проверка: сначала FileNotFoundError ... '/data/orders.csv'. После исправления — отчёт.

Решение

Добавьте -v "$PWD/data:/data". Образ менять не нужно: он не содержит данных.

Запустите образ, но вместо отчёта выведите версию Python.

Проверка: Python 3.12.x.

Решение
Окно терминала
docker run --rm orders-report:1.0 python --version

Соберите образ с тегом orders-report:2.0, у которого ORDERS_PATH по умолчанию равен /data/orders.csv, добавив ENV. Проверьте, какие переменные заданы в образе.

Проверка: в списке Env есть ORDERS_PATH=/data/orders.csv.

Решение

Добавьте строку ENV ORDERS_PATH=/data/orders.csv после FROM. Затем:

Окно терминала
docker build -t orders-report:2.0 .
docker image inspect orders-report:2.0 --format '{{.Config.Env}}'

В выводе будет и PATH, и другие переменные базового образа. Найдите нужную.

Сколько строк в docker history создали вы сами, а сколько пришло из python:3.12-slim? Какие ваши слои весят 0B?

Проверка: ваших слоёв три: WORKDIR, COPY, CMD. WORKDIR и CMD весят 0B.

Решение
Окно терминала
docker history orders-report:1.0

Строка CMD ["python3"] и всё ниже пришло из базового образа.

Дальше: Хороший образ.