7.06. Dockerfile
Dockerfile
Инструкции
★ Dockerfile — это текстовый файл, содержащий инструкции для автоматической сборки Docker-образа. Он является ключевым элементом по работе с Docker, так как позволяет создавать образы, которые можно использовать для запуска контейнеров. Dockerfile позволяет описать процесс создания образа шаг за шагом, что делает его воспроизводимым и легко поддерживаемым. Один и тот же Dockerfile может быть использован для создания одинаковых образов на разных машинах. Каждая инструкция в Dockerfile создаёт новый слой (layer) образа. Это позволяет эффективно использовать кэширование и уменьшать размер конечного образа.
Dockerfile — это ключевая документация образа, которая описывает все зависимости, переменные среды и команды. Когда мы запускаем команду docker build, Docker читает Dockerfile и выполняет инструкции по порядку. Каждая инструкция создаёт новый слой (layer) в образе. Слои кэшируются, что ускоряет процесс сборки при повторных запусках.
Разберём инструкции Dockerfile.
- FROM
FROM <image>:<tag>
Команда задаёт базовый образ, на основе которого будет создан новый образ. Это обязательная первая инструкция в Dockerfile. Пример:
FROM ubuntu:20.04
FROM может использоваться с алиасом для многоэтапной сборки:
FROM golang:1.20 AS builder
- LABEL
LABEL <key>=<value>
Добавляет метаданные к образу (например, автор, версия, описание). Метки могут быть просмотрены с помощью команды docker inspect. Пример:
LABEL maintainer="tim@mail.ru"
- ENV
ENV <key>=<value>
Устанавливает переменные среды, которые будут доступны внутри контейнера. Переменные сохраняются в образе и могут использоваться другими инструкциями. Пример:
ENV APP_HOME=/app
- RUN
RUN <command>
Выполняет команду в новом слое и создаёт новый образ. Используется для установки пакетов, настройки системы. Есть два формата:
- Shell-формат:
RUN <command> (выполняется через /bin/sh -c). - Exec-формат:
RUN ["executable", "param1", "param2"].
Пример:
RUN apt-get update && apt-get install -y python3
- COPY
COPY <src> <dest>
Копирует файлы или папки из локальной файловой системы в контейнер.
<src>- путь к файлам/папкам на хосте.<dest>- путь внутри контейнера.
Пример:
COPY . /app
- ADD
ADD <src> <dest>
Аналогично COPY, но также может распаковывать локальные .tar файлы. Не рекомендуется использовать, если не требуется распаковка архивов. Пример:
ADD archive.tar.gz /app
- CMD
CMD ["executable", "param1", "param2"]
Определяет команду, которая будет выполнена при запуске контейнера. Может быть переопределена при запуске контейнера через docker run. В файле может быть только одна инструкция CMD. Пример
CMD ["python3", "app.py"]
- ENTRYPOINT
ENTRYPOINT ["executable", "param1", "param2"]
Определяет команду, которая всегда будет выполняться при запуске контейнера. Аргументы, переданные через docker run, добавляются к команде ENTRYPOINT. Пример:
ENTRYPOINT ["python3", "app.py"]
По сути, это альтернатива CMD, но более гибкая. 9. WORKDIR
WORKDIR /path/to/workdir
Задаёт рабочую директорию для последующих инструкций (RUN, CMD, ENTRYPOINT). Если директория не существует, она будет создана. Пример:
WORKDIR /app
- ARG
ARG <name>[=<default value>]
Определяет переменную, которую можно передать во время сборки образа через флаг --build-arg. Пример:
ARG VERSION=1.0
- EXPOSE
EXPOSE <port>[/protocol]
Информирует Docker о том, что контейнер будет слушать указанный порт. Не открывает порт автоматически, используется для документации. Пример:
EXPOSE 8080
- VOLUME
VOLUME ["/data"]
Создаёт точку монтирования для работы с постоянным хранилищем. Позволяет сохранить данные вне контейнера. Пример:
VOLUME /var/lib/mysql
- USER
USER <user>[:<group>]
Задаёт пользователя и группу, от имени которых будут выполняться команды. По умолчанию команды выполняются от имени root. Пример:
USER appuser
- ONBUILD
ONBUILD <instruction>
Добавляет триггер для будущих сборок, основанных на текущем образе. Инструкция будет выполнена только при сборке дочернего образа. Пример:
ONBUILD COPY . /app
- STOPSIGNAL
STOPSIGNAL signal
Задаёт сигнал, который будет отправлен контейнеру при его остановке. Пример:
STOPSIGNAL SIGTERM
- SHELL
SHELL ["executable", "parameters"]
Переопределяет команду оболочку для выполнения инструкций. По умолчанию используется /bin/sh -c на Linux и cmd /S /C на Windows. Пример:
SHELL ["/bin/bash", "-c"]
- HEALTHCHECK
HEALTHCHECK [OPTIONS] CMD command
Определяет команду для проверки работоспособности контейнера. Параметры:
- --interval: Интервал между проверками (по умолчанию 30 секунд).
- --timeout: Таймаут для проверки (по умолчанию 30 секунд).
- --retries: Количество попыток перед объявлением контейнера неработоспособным (по умолчанию 3). Пример:
HEALTHCHECK --interval=30s --timeout=10s \
CMD curl -f http://localhost/ || exit 1
В Dockerfile можно использовать и комментарии для документации, через #.
Лучшие практики и разбор
Лучшие практики:
- Используйте минимальные базовые образы (например, alpine), чтобы уменьшить размер конечного образа.
- Объединяйте команды (&&), чтобы минимизировать количество слоёв:
RUN apt-get update && apt-get install -y package1 package2
- Используйте .dockerignore, чтобы исключить ненужные файлы из сборки.
- Используйте COPY вместо ADD, если не требуется распаковка архивов.
- Обеспечьте безопасность, используя минимальные привилегии (USER).
Давайте создадим пример хорошего Dockerfile по этим практикам. Представим, что у нас есть простое Python-приложение, которое запускает веб-сервер на Flask. Структура проекта предполагает, что в корне лежат файлы:
- app.py — основной файл приложения.
- requirements.txt — список зависимостей Python.
- .dockerignore — файл для исключения ненужных файлов.
Какое содержимое в .dockerignore?
# Исключаем ненужные файлы
.git
__pycache__
*.log
*.pyc
.env
Теперь создадим Dockerfile, который будет следовать всем этим рекомендованным практикам:
# 1. Используем минимальный базовый образ
FROM python:3.9-alpine
# 2. Устанавливаем метаданные
LABEL maintainer="tim@mail.ru"
# 3. Устанавливаем переменные среды
ENV APP_HOME=/app
ENV PYTHONUNBUFFERED=1 # Для немедленного вывода логов
# 4. Создаем рабочую директорию
WORKDIR $APP_HOME
# 5. Копируем зависимости и устанавливаем их
COPY requirements.txt .
RUN apk add --no-cache gcc musl-dev && \
pip install --no-cache-dir -r requirements.txt && \
apk del gcc musl-dev # Удаляем ненужные пакеты после установки
# 6. Копируем остальные файлы приложения
COPY . .
# 7. Создаем пользователя для работы приложения
RUN adduser -D appuser
USER appuser
# 8. Открываем порт
EXPOSE 5000
# 9. Запускаем приложение
CMD ["python", "app.py"]
Разбираем этот файл.
- Минимальный базовый образ:
FROM python:3.9-alpine
Мы использовали alpine версию Python, которая значительно меньше по размеру, чем стандартный образ. 2. Метаданные:
LABEL maintainer="tim@mail.ru"
Мы добавили метку для документации, чтобы было понятно, кто поддерживает образ. 3. Переменные среды:
ENV APP_HOME=/app
ENV PYTHONUNBUFFERED=1
APP_HOME задает рабочую директорию.
PYTHONUNBUFFERED=1 гарантирует, что логи будут выводиться немедленно, без буферизации.
- Рабочая директория:
WORKDIR $APP_HOME
Мы установили рабочую директорию, чтобы все последующие команды выполнялись в ней. 5. Установка зависимостей:
COPY requirements.txt .
RUN apk add --no-cache gcc musl-dev && \
pip install --no-cache-dir -r requirements.txt && \
apk del gcc musl-dev
Копируем только requirements.txt, чтобы кэширование работало эффективно. Устанавливаем временные зависимости (gcc, musl-dev) для сборки Python-пакетов, а затем удаляем их, чтобы уменьшить размер образа.
Используем флаг --no-cache-dir для pip, чтобы избежать сохранения кэша.
- Копирование файлов:
COPY . .
Копируем остальные файлы приложения в рабочую директорию. 7. Безопасность:
RUN adduser -D appuser
USER appuser
Создаём нового пользователя appuser, и используем его для запуска приложения. Это повышает безопасность, так как приложение не работает от имени root. 8. Открытие порта:
EXPOSE 5000
Информируем Docker о том, что приложение будет слушать порт 5000. 9. Команда для запуска:
CMD ["python", "app.py"]
Определяем команду для запуска приложения.
После того, как мы собрали контейнер, нам остаётся перейти в нужную директорию нашего проекта, выполнить docker build и docker run.