Урок 4 из 8 · 20 мин · обновлено
Dockerfile: собираем свой образ
Как описать свой образ в Dockerfile: инструкции FROM, WORKDIR, COPY, RUN, EXPOSE и CMD, сборка командой docker build, теги версий, кэш слоёв и файл .dockerignore.
Свой образ
Готовые образы закрывают многое, но рано или поздно нужен свой: ваш сайт или программа вместе с окружением. Образ описывают в файле Dockerfile — это рецепт: что взять за основу, что скопировать, что установить и что запускать.
Первый Dockerfile: сайт на nginx
Создайте папку my-site и положите в неё два файла. Простую страницу index.html:
<!DOCTYPE html>
<html lang="ru">
<head><meta charset="UTF-8"><title>Мой сайт</title></head>
<body><h1>Привет из контейнера!</h1></body>
</html>
И файл Dockerfile — без расширения:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
Всего две строки:
FROM nginx:alpine— взять за основу образ nginx;COPY index.html …— скопировать нашу страницу туда, откуда nginx отдаёт сайт.
Что запускать, указывать не нужно: базовый образ nginx уже знает, как стартовать веб-сервер.
Сборка и запуск
Откройте терминал в папке my-site и соберите образ:
docker build -t my-site:1.0 .
-t my-site:1.0— имя и тег нового образа;.— контекст сборки: текущая папка, где лежат Dockerfile и файлы.
Запустите контейнер из своего образа:
docker run -d --name my-site -p 8080:80 my-site:1.0
Откройте http://localhost:8080 — там ваша страница «Привет из контейнера!». Этот образ можно отправить в реестр и запустить на любом компьютере с Docker.
Изменили страницу — соберите новую версию с другим тегом my-site:1.1. Контейнер сам не обновится: его нужно остановить, удалить и запустить заново из новой версии.
Главные инструкции
| Инструкция | Что делает |
|---|---|
FROM |
базовый образ, с которого начинаем |
WORKDIR |
рабочая папка внутри образа для следующих инструкций |
COPY |
скопировать файлы из папки сборки в образ |
RUN |
выполнить команду при сборке: установить пакеты, создать папки |
EXPOSE |
отметить, какой порт слушает программа — подсказка, а не открытие порта |
CMD |
что запускать при старте контейнера |
Типичный Dockerfile для программы на Python выглядит так:
FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
RUN выполнился один раз, при сборке, и зависимости остались в образе. CMD выполняется каждый раз, когда по образу запускают контейнер.
Кэш слоёв: порядок важен
Каждая инструкция создаёт слой. При повторной сборке Docker проверяет: если инструкция и её файлы не изменились, слой берётся из кэша — мгновенно. Но если слой изменился, пересобираются он и все слои ниже.
Поэтому в примере выше сначала копируется только requirements.txt и ставятся зависимости, а весь код — потом. Код меняется постоянно, зависимости — редко. При правке кода Docker возьмёт слой с установленными зависимостями из кэша, и сборка займёт секунды, а не минуты.
Правило: редко меняющиеся шаги — выше, часто меняющиеся — ниже.
.dockerignore
Команда COPY . . копирует всю папку. В неё может случайно попасть лишнее: папка .git, временные файлы и, хуже всего, файлы с паролями. Файл .dockerignore рядом с Dockerfile исключает их из сборки — так же, как .gitignore в Git:
.git
.env
*.log
__pycache__
Попробуйте сами. Соберите образ сайта из урока с тегом 1.0 и запустите его. Затем измените текст на странице, соберите версию 1.1 и запустите её на порту 8081 рядом с первой. Откройте обе версии в браузере.
Решение
docker build -t my-site:1.0 .
docker run -d --name site-v1 -p 8080:80 my-site:1.0
# поменяли текст в index.html
docker build -t my-site:1.1 .
docker run -d --name site-v2 -p 8081:80 my-site:1.1
docker images my-site
На localhost:8080 — старый текст, на localhost:8081 — новый. Обе версии живут рядом: образы неизменяемы, и каждый тег — отдельный снимок.
При второй сборке обратите внимание на вывод: шаг FROM взят из кэша — строка с CACHED, а COPY выполнен заново, потому что файл изменился.
Уберите за собой:
docker stop site-v1 site-v2
docker rm site-v1 site-v2
Мини-тест: проверьте себя
Закрепите урок: несколько вопросов, разбор ответов — в конце.
-
Чем RUN отличается от CMD?
RUN готовит образ: устанавливает зависимости, создаёт папки. CMD говорит, какую программу запустить, когда по образу стартует контейнер.
-
Что означает точка в конце docker build -t my-site:1.0 .?
Точка — контекст сборки. Docker берёт из этой папки Dockerfile и файлы, которые копирует COPY.
-
Почему COPY requirements.txt и установку зависимостей ставят выше, чем COPY всего кода?
Если слой не изменился, Docker берёт его из кэша. Изменение в слое пересобирает его и все слои ниже.
-
Зачем нужен файл .dockerignore?
Всё из папки сборки может попасть в образ. .dockerignore защищает от случайного копирования паролей и мусора.