Dockerfile
Docker может создавать образы автоматически, читая инструкции из файла Dockerfile
, по сути это текстовый документ, содержащий все команды, которые пользователь может вызвать в командной строке для сборки образа Images
. С помощью команды docker build
можно создать автоматизированную сборку, которая последовательно выполняет несколько инструкций командной строки.
Создавать новый образ есть смысл когда вы не можете найти подходящий для вашей задачи образ на hub.docker.com. Созданные образы можно также загружать на hub.docker.com и делать их доступными для других разработчиков.
Инструкции, при сборке образа обрабатываются сверху вниз. Слои в итоговом образе создают только инструкции FROM
, RUN
, COPY
, ADD
. Другие инструкции занимаются настройкой, описывают метаданные, или сообщают Docker о том, что во время выполнения контейнера нужно что-то сделать, например — открыть какой-то порт или выполнить какую-то команду.
Этапы создания
- Необходимо создать докер файл это специальный файл с инструкциями для Docker по созданию нового образа, один докер файл для одного образа
- Дальше докер файл помещают в корне папки приложения то есть если у вас уже есть папка в которой находится приложение тогда помещаете докер файл в корень проекта, это удобно тем что если вы например используете
Git
для контроля версии,Dockerfile
всегда будет в вашем репозитории вместе с приложением - Каждая инструкция
FROM
,RUN
,COPY
,ADD
вDockerfile
будет создавать новый слой в новом образе - При создании образа нужно указывать имя и тег для образа
- На основании готового образа, создается контейнер
Типичный вид файла
Теперь давайте посмотрим как выглядит докер файл:
Это пример небольшого Dockerfile
, в нём есть четыре инструкции:
- Инструкция
FROM
указывает на то, какой базовый образ будет использоваться для вашего образа, в данном примере базовый образ называется python а версия alpine - Инструкция
WORKDIR
сдесь создаётся рабочая директория внутри образа. В данном примере указан путь/app
и благодаря такой инструкции внутри образа будет создана папка. Рекомендуется всегда создавать папку внутри образа для вашего приложения, иначе можно например случайно перезаписать системные папки - Инструкция
COPY
мы копируем все файлы из локальной текущей папки (первая точка) в папку указанную в директорииWORKDIR
(вторая точка). Когда необходимо скопировать файл с текущей папки на компьютере в папку внутри образа можно указывать в качестве целевой папке просто точку - Инструкция
CMD
указывает, какая команда будет выполнена когда создается новый контейнер на основании уже созданного образа. В данном примере будет запущен процессpython
и ему передаётся аргументmain.py
. Иными словами мы запустим процессpython
и он выполнит файлmain.py
, этот файл должен находиться в рабочей директории. Подразумевается что файлmain.py
мы скопируем с нашего компьютера на этапе инструкцияCOPY
Синтаксис файла Dockerfile
Работу с Dockerfile
можно разбить на два этапа: описание инструкций и сборка образа. Набор инструкций — последовательность действий, чтобы собрать образ.
FROM
установка родительского базового образаRUN
запуск команд терминалаCOPY
копирование файлов проектаADD
копирует файлы по ссылке и распаковывавает локальныеtar
архивыENTRYPOINT
предоставляет команду с аргументами для вызова во время выполнения контейнера, причем аргументы не переопределяются в командной строке пользователемCMD
предоставляет команду с аргументами для вызова во время выполнения контейнера, причем аргументы переопределяются в командной строке пользователемENV
устанавливает постоянные переменные средыWORKDIR
задаёт рабочую папку проектаUSER
задаёзапуск приложения от имени пользователяEXPOSE
указывает на необходимость открыть портARG
задаёт переменные для передачи Docker во время сборки образаVOLUME
создаёт точку монтирования для работы с постоянным хранилищем
FROM установка базового образа
Dockerfile
обычно начинается с инструкции FROM
. Эта инструкция задаёт базовый образ. В качестве базового образа может быть использован образ с чистой операционной системой, образ с уже установленной и настроенной платформой или вообще любой другой образ. Вот так можно установить Ubuntu 18.04 как базовый образ:
FROM ubuntu:18.04
Для установки Node.js alpine версии используют команду ниже:
FROM node
RUN запуск команд терминала
Инструкция RUN
позволяет запускать команды терминала при сборке. Это самая используемая инструкция, ей можно создать папку, установить недостающие пакеты или запустить shell скрипт. Например, установим платформу Node.js поверх образа с чистой Ubuntu:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
COPY копирование файлов проекта
Инструкции COPY
позволяют перенести файлы с компьютера, который запускает сборку, внутрь образа. Например, перенесём все содержимое папки, где лежит Dockerfile
в папку /app
внутри образа:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
COPY . /app
ADD копирование файлов проекта
Инструкции ADD
умеет скачивать файлы с сервера по ссылке и распаковывать tar-архивы.
Если собрать образ из этого Dockerfile
и запустить контейнер, то окажется что команда ADD
выкачала этот архив:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
ADD https://github.com/bessarabov/Moment/archive/1.3.1.tar.gz /app
В той же папке где лежит Dockerfile
находится файл с архивом. Если собрать образ и запустить контейнер из этого образа то будет видно что ADD
разархивировал этот tar.gz файл:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
ADD 1.3.1.tar.gz /app
Используйте COPY
, она только копирует указанную папку во внутреннюю папку образа. Инструкция ADD
слишком всемогущая и можно случайно использовать её неверно. Например, она может скачать файл из Интернета перед копированием или разархивировать архив.
ENTRYPOINT запуск приложения
После того как образ готов, необходимо запустить приложение, которое в нем содержится. Образы Docker задумывались как упаковка для приложения, поэтому нет ничего удивительного в существовании механизма запуска приложения при старте контейнера на основе собранного образа. Для этого используют инструкцию ENTRYPOINT
. Инструкция используется для запуска приложения при старте контейнера. Вместе с командой запуска контейнера вы можете передавать параметры команде, которая прописана после ENTRYPOINT
. Внутри контейнера можно запустить программу node
и выполнить файл переданный через параметры /app/app.js
:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
COPY . /app
ENTRYPOINT ["node", "/app/app.js"]
Есть две формы записи аргументов ENTRYPOINT
: в виде строки и в виде массива строк. Первый вариант (так называемый shell режим) используется редко, поскольку не позволяет гибко настраивать работу образа. Обычно используется второй вариант (так называемый exec режим) — массив строк, который может состоять из команды и её параметров.
Используйте ENTRYPOINT
, если вы не хотите, чтобы пользователь вашего образа переопределял поведение приложения в контейнере. Используйте CMD
, если записываете команду по умолчанию, которую пользователь с лёгкостью может переопределить на этапе запуска контейнера.
CMD запуск приложения
Инструкция CMD
делает практически то же самое, что и ENTRYPOINT
. Обычно это также команда запуска приложения, она игнорируется в том случае, если пользователь вашего образа прописывает в явном виде, что и как запускать после запуска контейнера на основе образа. Часто CMD
вообще используется для передачи параметров по умолчанию вашему приложению, которые пользователь может переопределить:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
COPY . /app
CMD ["node", "/app/app.js"]
Есть две формы записи аргументов CMD
: в виде строки и в виде массива строк. Первый вариант (так называемый shell режим) используется редко, поскольку не позволяет гибко настраивать работу образа. Обычно используется второй вариант (так называемый exec режим) — массив строк, который может состоять из команды и её параметров. Среди аргументов инструкции CMD
строка с командой может отсутствовать, если эта инструкция идёт после инструкции ENTRYPOINT
. В этом случае строки массива рассматриваются как аргументы по умолчанию для команды, обозначенной в ENTRYPOINT
.
Используйте ENTRYPOINT
, если вы не хотите, чтобы пользователь вашего образа переопределял поведение приложения в контейнере. Используйте CMD
, если записываете команду по умолчанию, которую пользователь с лёгкостью может переопределить на этапе запуска контейнера.
ENV переменные окружения
Переменные окружения задаются инструкцией ENV
. Через переменные окружения передают ключи и пароли к сервисам, режим работы, другие секретные и не очень значения. Например, запуск приложения Node.js для конечного пользователя обозначается дополнительной инструкцией:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
COPY . /app
ENV NODE_ENV=production
CMD ["node", "/app/app.js"]
WORKDIR рабочая папка проекта
Инструкция WORKDIR
задаёт рабочую папку приложения. Все инструкции в Dockerfile
будут выполняться относительно неё. Устанавливать рабочую папку — хороший тон. Она позволяет явно указать место, где будет происходить вся работа. Добавим её в нашу конфигурацию:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
WORKDIR /app
COPY . .
ENV NODE_ENV=production
CMD ["node", "app.js"]
USER запуск от имени пользователя
Если приложение нужно запускать от имени пользователя системы, то используйте инструкцию USER
с именем пользователя. Например, если вы хотите запускать приложение от имени пользователя node_user
, то конфигурационный файл будет выглядеть так:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
WORKDIR /app
COPY . .
ENV NODE_ENV=production
USER node_user
CMD ["node", "app.js"]
EXPOSE проброска порта вовне
Для запуска веб-приложения на компьютере вы используете веб-сервер, запущенный локально. Обычно веб-приложение становится доступным по адресу http://localhost:8080
. Цифры в конце означают порт, открытый для запросов со стороны браузера или других приложений. Чтобы открыть в браузере веб-приложение, запущенное внутри контейнера, нужно «пробросить» запросы от браузера внутрь контейнера, а ответ от веб-приложения из контейнера наружу. Для этого используется перенаправление пакетов в виртуальном сетевом окружении.
EXPOSE
незаменим, когда в образе находится база данных и нам нужен доступ к ней вне контейнера. Запись EXPOSE 8080
означает, что на компьютере, на котором запущен Docker, веб-приложение будет доступно по адресу http://localhost:8080
:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
WORKDIR /app
COPY . .
ENV NODE_ENV=production
USER node_user
EXPOSE 8080
CMD ["node", "app.js"]
ARG Аргументы командной строки
Во время сборки образа не всегда удобно, а иногда даже опасно, описывать все параметры внутри Dockerfile
, поскольку этот файл обычно доступен в репозитории большинству разработчиков. В случае публичного репозитория это недопустимо вовсе. В этом случае следует пользоваться переменными, значения которых задаются на этапе сборки образа.
Передавать данные можно с помощью аргументов команды docker build
на этапе сборки образа. Во время сборки эти аргументы можно использовать как переменные, достаточно их определить инструкцией ARG
. Можно задать и значения по умолчанию на тот случай, если пользователь не укажет нужные аргументы. Например, передать имя пользователя внутрь контейнера можно следующим образом:
docker build --build-arg user=node_user .
В Dockerfile
надо будет добавить соответствующие инструкции:
FROM ubuntu:18.04
RUN sudo apt update && sudo apt install nodejs && sudo apt install npm
WORKDIR /app
COPY . .
ENV NODE_ENV=production
# Значение по умолчанию 'deploy' (можно не указывать)
ARG user=deploy
USER $user
EXPOSE 8080
CMD ["node", "app.js"]
Важно, что так не следует передавать секретные данные, поскольку их можно будет увидеть в истории Docker: docker history
. Для безопасной передачи секретных данных лучше использовать тома Docker.
VOLUME точка монтирования для работы с постоянным хранилищем
Инструкция VOLUME
добавляет тома в образ, том это папка в одном или более контейнерах, проброшенная через Union File System (UFS). Тома могут быть расшарены или повторно использованы между контейнерами. Команда VOLUME
используется для организации доступа вашего контейнера к директории на хосте (тоже самое, что и монтирование директории), команда определяет где контейнер будет хранить постоянные данные и получать к ним доступ:
VOLUME ["/opt/project"]
В примере выше создается точка монтирования /opt/project
для любого контейнера, созданного из образа.
Многоступенчатая сборка образа
С точки зрения оптимизации сборки, уменьшения размера образа и ускорения приложения, образ можно собирать в несколько этапов. Например, с помощью платформы Node.js произвести сборку веб-приложения на первом этапе, а на втором — запустить готовый бандл с помощью веб-сервера. Операция копирования из первого промежуточного образа во второй целевой пройдёт совершенно незаметно. После сборки образ будет занимать мало дискового пространства, в нем будет все самое необходимое для работы веб-приложения:
# Сборка проекта на платформе Node.js
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Запуск приложения на сервере
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Имя промежуточного образа build-stage служит для передачи результата работы первой стадии сборки.
Рекомендации по сборке
Для того чтобы использовать образы эффективнее, необходимо следовать рекомендациям от команды Docker:
- Нужно создавать образы так, чтобы жизненным циклом контейнера можно было удобно управлять. Образ не должен хранить внутреннее состояние. Данные внутрь образа можно передать на этапе сборки с помощью аргументов командной строки, а на этапе работы контейнера можно пользоваться томами Docker
- Необходимо понимать контекст запуска веб-приложения: папка проекта, удалённый ресурс (remote source) или репозиторий
- Надо понимать, что
Dockerfile
может запускаться вне контекста через стандартный поток ввода - Используйте файл
.dockerignore
для того, чтобы в образ попадали только нужные файлы и папки. От всего лишнего лучше избавиться на этапе сборки - Используйте сборку приложения в несколько стадий. Это позволит существенно уменьшить размер образа
- Не устанавливайте то, что не будете использовать в образе
- Необходимо разделять приложения на обособленные части, которые способны выполняться независимо. Этот процесс носит название декаплинга (Decoupling)
- Минимизируйте количество слоёв в образе. Это повышает производительность образа как при сборке, так и при работе контейнера
- Если параметры инструкции записываются в несколько строк (с помощью символа переноса строки
\
) необходимо выстраивать аргументы в алфавитном порядке. Это повышает читаемость файла и упрощает отладку - Используйте кэш Docker только для тех слоёв, которые будут нужны для сборки других образов. Для этого достаточно добавить параметр
--no-cache=true
в команду сборкиdocker build
Сборка образа
Образ Docker можно собрать тремя способами, чаще всего используется первый способ с указанием пути:
- Указав путь к папке
PATH
- Указав путь к репозиторию
URL
- Используя стандартный поток ввода
–
Создаем новый образ, если вы уже находитесь в папке где есть докер файл вам достаточно указать в инструкции точку, docker попытается найти докер файл в текущую директорию если такой файл был найден в текущей директории, запустится процесс создания нового образа и сверху вниз шаг за шагом выполнятся все инструкции которые указаны в докер файл и в конце если всё прошло успешно будет создан новый образ. В данном примере будет создан новый образ но ID
у этого образа будет случайный:
docker build .
Если вы хотите добавить определённое имя созданному образу то нужно использовать опцию -t
. Если тег не указан, Docker сам подставит тег latest
:
docker build . -t my_calendar:4.1.3
После создания образа, можно создавать контейнер используя указанное ранее имя образа my_calendar
.
Использование нескольких Dockerfile
Иногда возникает необходимость использования нескольких вариантов сборок в одном проекте. В этом случае не обойтись без нескольких файлов с инструкциями. При сборке можно указать другое имя для файла конфигурации или относительный путь внутри PATH
, нужно использовать флаг -f
:
docker build -f containers/dockerfile-mode-1 .
Точно так же можно указать относительный путь для проекта или репозитория по некоторому URL. Например, Docker может скачать не только репозиторий GitHub, но и произвольный архив с проектом, распаковать его и собрать образ (поддерживаются архивы форматов bzip2, gzip, xz):
docker build -f ctx/Dockerfile http://server/ctx.tar.gz
Файлы и папки проекта, исполняемый файл приложения, архив или репозиторий Git составляют контекст образа. Но Docker позволяет собирать образы без контекста из стандартного потока ввода. Собрать такой образ можно командой:
docker build - < Dockerfile
Исключение файлов из сборки .dockerignore
Если вам не нужно включать в образ какие-то папки или файлы из контекста, добавьте в папку файл исключений .dockerignore
. В этом файле перечисляются в отдельных строках все пути или маски путей, которые не должны быть помещены в образ. Пример файла:
# Комментарий
*/temp*
*/*/temp*
temp?
*/temp
позволяет не включать в образ файлы или папки, имена которых начинаются наtemp
, и которые находятся в любой папке первого уровня например,/somedir/temporary.txt
или/somedir/temp
*/*/temp*
делает то же, но для папок второго уровняtemp?
позволяет не включать в образ файлы и папки из корневой папки образа, имена которых начинаются наtemp
и состоят из пяти символов, последний из которых может быть любым