Skip to content

Основы Docker: Dockerfile

Dockerfile — это текстовый документ, содержащий все команды, которые пользователь мог бы вызвать в командной строке для сборки Docker-образа. Docker последовательно выполняет инструкции из Dockerfile для создания образа.

Структура Dockerfile

Dockerfile состоит из последовательности инструкций (обычно по одной на строку), которые выполняются для создания слоев образа. Общий формат инструкции выглядит так:

dockerfile
INSTRUCTION argument

Инструкции регистронезависимы, но общепринятой практикой является написание их заглавными буквами для лучшей читаемости.

Основные инструкции Dockerfile

Вот некоторые из наиболее часто используемых инструкций в Dockerfile:

  • FROM: Указывает базовый образ, на основе которого будет строиться ваш новый образ. Это должна быть первая некомментированная инструкция в Dockerfile. Вы можете указать имя образа из Docker Hub или локально созданный образ.

    dockerfile
    FROM ubuntu:latest
    FROM node:18-alpine
    FROM php:8.2-fpm-alpine
  • RUN: Выполняет команды в новом слое поверх текущего образа и фиксирует результаты. RUN используется для установки программного обеспечения, настройки системы и выполнения других действий, необходимых для вашего приложения.

    Существует два формата инструкции RUN:

    • RUN <command> (shell form): Команда выполняется в оболочке (/bin/sh -c на Linux или cmd /S /C на Windows).
    • RUN ["executable", "param1", "param2"] (exec form): Команда выполняется напрямую без оболочки. Этот формат рекомендуется для ясности и избежания проблем с экранированием.
    dockerfile
    RUN apt-get update && apt-get install -y curl wget unzip
    RUN ["npm", "install"]
    RUN ["php", "-r", "echo 'Hello from PHP!';"]
  • COPY: Копирует файлы и директории из <src> в <dest> в файловой системе образа. <src> может быть файлом или директорией на хост-машине (относительно контекста сборки) или URL-адресом. <dest> — это абсолютный путь внутри образа.

    dockerfile
    COPY . /app
    COPY config.ini /etc/app/
    COPY ["./data.json", "/var/data/"]
    COPY [https://example.com/app.tar.gz](https://example.com/app.tar.gz) /tmp/
  • ADD: Похожа на COPY, но имеет некоторые дополнительные возможности:

    • Если <src> является локальным tar-архивом (gzip, bzip2, xz), он будет автоматически распакован в <dest>.
    • Если <src> является URL-адресом, Docker скачает файл по этому URL-адресу и скопирует его в <dest>.

    Рекомендуется использовать COPY вместо ADD для простых операций копирования файлов и директорий, так как COPY более явно указывает на выполняемое действие. Используйте ADD только тогда, когда вам действительно нужна функциональность распаковки tar-архивов или скачивания файлов по URL.

    dockerfile
    ADD app.tar.gz /app/
    ADD [https://example.com/image.jpg](https://example.com/image.jpg) /images/
  • WORKDIR: Устанавливает рабочую директорию для последующих инструкций RUN, CMD, ENTRYPOINT, COPY и ADD. Если директория не существует, она будет создана. Можно использовать несколько инструкций WORKDIR, и они будут выполняться относительно предыдущей.

    dockerfile
    WORKDIR /app
    RUN npm install
    COPY . .
    WORKDIR /app/config
    RUN echo "Configuration done" > config.log
  • ENV: Устанавливает переменные окружения в образе. Эти переменные будут доступны запущенным контейнерам.

    Существует два формата:

    • ENV <key> <value>: Устанавливает одну переменную. Значение после первого пробела будет считаться значением переменной. Пробелы в значении нужно экранировать или заключать значение в кавычки.
    • ENV <key>=<value> ...: Устанавливает одну или несколько переменных в формате ключ=значение.
    dockerfile
    ENV APP_HOME /opt/app
    ENV PATH "$PATH:$APP_HOME/bin"
    ENV DB_HOST=localhost DB_USER=admin DB_PASS=secret
  • EXPOSE: Информирует Docker, на каких сетевых портах приложение внутри контейнера прослушивает соединения. EXPOSE не публикует порт хост-машины на порт контейнера — для этого необходимо использовать флаг -p при запуске контейнера (docker run -p host_port:container_port).

    dockerfile
    EXPOSE 80
    EXPOSE 443 8080
  • CMD: Указывает команду, которая будет выполнена при запуске контейнера из образа. В Dockerfile может быть только одна инструкция CMD. Если указано несколько, то только последняя будет иметь эффект.

    Существует три формата:

    • CMD ["executable", "param1", "param2"] (exec form): Рекомендуемый формат для большинства случаев.
    • CMD <command> <param1> <param2> (shell form): Команда выполняется в оболочке.
    • CMD <parameter> (entrypoint as executable): Предоставляет аргументы для инструкции ENTRYPOINT.

    CMD предоставляет команду по умолчанию, которую можно переопределить при запуске контейнера с помощью docker run <image> <command> <args>.

    dockerfile
    CMD ["node", "server.js"]
    CMD ["python", "app.py", "--debug"]
    CMD apachectl -DFOREGROUND
  • ENTRYPOINT: Настраивает исполняемый файл, который будет запущен при запуске контейнера. Похожа на CMD, но имеет некоторые отличия в поведении при передаче аргументов через docker run. В Dockerfile может быть только одна инструкция ENTRYPOINT.

    Существует два формата:

    • ENTRYPOINT ["executable", "param1", "param2"] (exec form): Рекомендуемый формат.
    • ENTRYPOINT <command> <param1> <param2> (shell form).

    Аргументы, переданные через docker run <image> <args>, будут добавлены в конец инструкции ENTRYPOINT в формате exec form. При использовании shell form аргументы могут быть проигнорированы или интерпретированы как часть команды оболочки.

    Часто ENTRYPOINT используется для запуска основного приложения, а CMD предоставляет аргументы по умолчанию для этого приложения.

    dockerfile
    ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
    CMD ["nginx", "-g", "daemon off;"]

    В этом примере при запуске контейнера выполнится /usr/local/bin/docker-entrypoint.sh nginx -g daemon off;. Если при запуске контейнера передать дополнительные аргументы (docker run <image> -c /etc/nginx/my.conf), то команда станет /usr/local/bin/docker-entrypoint.sh nginx -g daemon off; -c /etc/nginx/my.conf.

  • USER: Устанавливает имя пользователя или UID, который будет использоваться для выполнения последующих инструкций RUN, CMD и ENTRYPOINT.

    dockerfile
    USER nobody
    USER 1001
  • VOLUME: Создает точку монтирования с указанным именем и отмечает ее как внешний том, управляемый Docker. Это полезно для сохранения данных, которые должны переживать жизненный цикл контейнера, или для обмена данными между контейнерами.

    dockerfile
    VOLUME /data
    VOLUME ["/var/www", "/var/log/apache2"]
  • ARG: Определяет переменную-аргумент, которую можно передать во время сборки образа с помощью флага --build-arg команды docker build.

    dockerfile
    ARG VERSION=latest
    FROM ubuntu:$VERSION
    RUN echo "Using Ubuntu version: $VERSION"

    При сборке: docker build --build-arg VERSION=20.04 -t my-ubuntu:custom .

  • ONBUILD: Указывает команду, которая будет выполнена при сборке образа, который наследуется от текущего образа. ONBUILD команды не выполняются при сборке текущего Dockerfile.

    dockerfile
    # В базовом образе
    ONBUILD COPY . /app
    ONBUILD WORKDIR /app
    ONBUILD RUN npm install
    
    # В производном образе
    FROM my-base-image:latest
    # При сборке этого образа будут выполнены COPY, WORKDIR и RUN из базового образа

Порядок инструкций

Рекомендуется располагать инструкции в Dockerfile таким образом, чтобы минимизировать перестройку слоев при изменении файла. Общие рекомендации:

  1. Начните с наиболее редко меняющихся инструкций (например, установка базовых пакетов).
  2. Затем добавьте инструкции, которые меняются чаще (например, копирование исходного кода приложения).
  3. Инструкции, зависящие от часто меняющихся файлов, должны идти последними.

Это позволяет Docker использовать кэшированные слои для неизменившихся инструкций, что значительно ускоряет процесс сборки.

Пример Dockerfile для Node.js приложения

dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install --only=production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
  1. Начинаем с базового образа node:18-alpine.
  2. Устанавливаем рабочую директорию /app.
  3. Копируем файлы package.json и package-lock.json (если есть) для кэширования установки зависимостей.
  4. Устанавливаем production-зависимости.
  5. Копируем остальной исходный код приложения.
  6. Информируем Docker, что приложение слушает порт 3000.
  7. Указываем команду для запуска приложения при старте контейнера.

Контекст сборки

При выполнении команды docker build, Docker отправляет контекст сборки на Docker daemon. Контекст сборки включает файлы и директории, расположенные в указанном пути (. в большинстве случаев). Инструкции COPY и ADD могут ссылаться только на файлы и директории внутри контекста сборки.

Игнорирование файлов (.dockerignore)

Вы можете создать файл .dockerignore в корне контекста сборки, чтобы указать файлы и директории, которые не нужно включать в контекст сборки. Это может значительно ускорить процесс сборки и уменьшить размер образа. Синтаксис .dockerignore похож на синтаксис .gitignore.

Пример .dockerignore:

node_modules
.git
.DS_Store

Dockerfile является ключевым элементом в процессе контейнеризации с помощью Docker. Понимание его инструкций и лучших практик позволяет создавать эффективные и воспроизводимые Docker-образы для ваших приложений.