Картофельные лонгриды

Назад, к списку

Собираем оркестр контейнеров с docker-compose

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

Например, в моей первой статье про докер я передавал докеру флаг -p 3000:3000, чтобы указать, какой порт из контейнера опубликовать на моей машине. Если я отдам свой образ кому-то ещё, мне обязательно нужно сообщить, к какому из портов привязываться, а им — не забыть передать этот флажок. Docker-compose позволяет нам передавать свои контейнеры вместе со всеми настройками, которые им нужны для корректной работы.

Важно: Compose не поставляется вместе с докером, поэтому вам придется установить его отдельно.

Конфигурация контейнеров с docker-compose

Вся настройка конфигурации производится в yaml файле, который мы размещаем в корне нашего проекта, обычно он называется docker-compose.yml.

В нём обязательно должен лежать ключ version, в котором мы указываем используемую версию синтаксиса. Они довольно сильно отличаются, а мажорные версии не совместимы между собой, поэтому в проектах разного возраста могут лежать абсолютно разные по структуре файлы. Этого не стоит пугаться: концепт работы от этого не меняется. В статье я использую версию 3.

Для подавляющего большинства проектов достаточно будет всего одного дополнительного ключа: services. В нём мы описываем поднимаемые контейнеры и их настройки. Если мы вернемся к примеру из моей прошлой статьи и попытаемся собрать образ и поднять контейнер с опубликованным 3000 портом, то файл docker-compose.yml будет выглядеть так:

В объекте services лежит объект api, который и является описанием нашего сервиса. Название у него может быть каким угодно, главное, чтобы нам самим оно было понятно. В нём по ключу build мы указываем, как будет собираться наш образ: context отвечает за то, в какой папке будет производиться сборка, а dockerfile — название нашего докерфайла. В данном случае мы соберем докерфайл по пути api/prod.Dockerfile. Этот ключ не является обязательным, при его отсутствии мы бы собрали api/Dockerfile. Ниже, в поле ports мы указываем нужные нам привязки портов, так же, как если бы мы это делали через флаг -p .

Теперь я могу отправить свой проект кому-то вместе с этим файлом, потребуется лишь написать в терминале docker-compose up, а контейнер соберется и запустится с нужными настройками сам!

Поднимаем несколько контейнеров разом

Иногда для разработки фронта необходимо поднимать локально и бекенд, которому может потребоваться ещё и локальная база данных или какие-то дополнительные сервисы. Чтобы не следить за необходимым окружением на локальной машине, мы можем просто описать compose файл с необходимыми контейнерами! Давайте добавим к нашему примеру выше postgres как базу данных и посмотрим на получившийся конфиг:

Мы просто добавили новое поле описывающее сервис БД в services, и теперь docker-compose up поднимет сразу оба! Давайте разберемся с новыми полями, которые появились в конфиге:

depends_on

При запуске приложений из нескольких сервисов часто случаются ситуации, когда один из сервисов требует при запуске наличия другого. Например, бекенд не может встать без базы данных, как в нашем случае. Для таких ситуаций в описании сервиса можно дописать ключ depends_on и в значение передать все зависимости, старта которых он должен дождаться.

image

Если нужный нам образ уже собрали за нас и положили в Docker hub, мы можем просто указать его название в поле image вместо локальной сборки с build.

restart

В случае, когда контейнер падает, то докер не будет пытаться его поднимать снова. Поле restart позволяет переопределить это поведение: со значением always каждый раз, когда контейнер будет завершаться, докер будет пытаться его перезапустить. Подробнее о restart здесь.

environment

Если при запуске контейнера нам хочется передать ему переменные среды, мы можем сделать это при помощи объекта environment.

volumes

Вся файловая система внутри контейнера умирает вместе с ним. Если же мы хотим сохранять некоторые файлы или папки между запусками контейнера, то нам поможет ключ volumes. Грубо говоря, мы привязываем путь до двоеточия на нашей машине, к пути после двоеточия внутри контейнера. В нашем примере, всё, что контейнер положит по пути /var/lib/postgresql/data будет лежать в папке postgres-data нашего проекта. Подробнее про volumes в докере можно почитать здесь.

Рецепты

Вместо заключения покажу немного полезных команд docker-compose.

docker-compose up -d

Как и докер, compose поддерживает флаг -d, для того чтобы не подключаться в свежесозданные контейнеры, а запускать их в фоновом режиме.

docker-compose up <название сервиса>

Если нам нужно поднять не все, а лишь один из сервисов, описанных в compose файле, мы можем просто указать его название команде up, например, docker-compose up api.

docker-compose logs

Команда logs покажет логи из всех контейнеров сразу. В неё, как и в up, можно передать название сервиса, чтобы получить логи только из него. Отличие этой команды от обычного docker logs в том, что название не сгенерировано докером из двух случайных слов, а совпадает с названием нужного контейнера из compose файла.

docker-compose down

Команда-антипод up — останавливает все созданные контейнеры. В неё тоже можно передать название нужного сервиса, если мы хотим остановить только его.