По следам статьи Docker + Laravel = ? хочу рассказать о довольно необычном способе использования утилиты docker-compose.


Для начала, для тех кто не знает, зачем нужен docker-compose. Это утилита, которая позволяет запускать на отдельном хосте набор связанных сервисов, запакованных в docker-контейнеры. Изначальная версия была написана на python и могла быть установлена двумя способами:


  • через пакетный менеджер операционной системы (apt install docker-compose для Ubuntu и yum install docker-compose.noarch для Centos)
  • через менеджер зависимостей python (pip install docker-compose)

Проблемой первого способа является то, что обычно в репозиториях операционной системы docker-compose старой версии. Это является проблемой, если необходимо использовать свежую версию демона docker или используются специфические для определенной версии формата файла docker-compose.yaml возможности (матрицу поддерживаемых фич по версиям формата и версиям утилиты docker-compose можно найти на официальном сайте docker).


Сейчас же разработчики docker переписали утилиту на go и предоставляют ее в виде бинарного файла, что позволяет ее устанавливать следующим способом (это текущий рекомендуемый способ):


  1. смотрим последнюю версию на https://github.com/docker/compose/releases и скачиваем ее


    $ sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

  2. устанавливаем права для запуска приложения


    $ sudo chmod +x /usr/local/bin/docker-compose

  3. дополнительно можно установить автодополнение для командных интепретаторов bash и zsh


  4. проверяем установку


    $ docker-compose --version
    docker-compose version 1.22.0, build 1719ceb


Я считаю, что единый бинарник это очень здорово, т.к. нам не нужно тянуть зависимости python. Да, и вообще — может у нас python окружение совсем сломано на целевой машине, которую мы хотим настроить!!!



Пример путаницы в python-окружении


Но есть еще 4-й путь, о котором я и хотел рассказать. Это возможность запускать docker-compose через docker. Действительно, есть уже собранные официальные образы на Docker Hub (https://hub.docker.com/r/docker/compose/). Зачем они могут понадобиться?


  • если мы хотим работать с несколькими версиями docker-compose одновременно (хотя обычно достаточно последней стабильной)
  • если у нас нет python или мы не хотим его использовать (например, у нас облегченный дистрибутив CoreOS Container Linux)
  • использование в CI/CD пайплайнах.

Давайте попробуем!


Как мы делали обычно запуск контейнеров:


$ docker-compose up -d 

Через утилиту, запакованную в docker-контейнер:


$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/rootfs/$PWD" -w="/rootfs/$PWD" docker/compose:1.13.0 up -d

Слишком многословно, да? Мозг можно сломать запоминать все эти параметры. Поэтому попробуем облегчить себе жизнь и напишем обертку (wrapper) на языке оболочки. Но сначала разберемся в передаваемых параметрах:


  • --rm — удаляет временный контейнер после остановки, т.е. мы не оставляем мусора в системе
  • -v /var/run/docker.sock:/var/run/docker.sock — без этого docker-compose не сможет соединиться с docker-демоном на хосте
  • -v "$PWD:/rootfs/$PWD" -w="/rootfs/$PWD" — позволяет пробросить текущий каталог внутрь контейнера, чтобы утилита увидела docker-compose файл

Нам еще не хватает возможности интерполяции значений в docker-compose файле. Это процесс, при котором утилита подставляет переменные окружения в YAML-файл. Например, во фрагменте


version: "2.1"
services:
  pg:
    image: postgres:9.6
    environment:
      POSTGRES_USER: ${POSTGRES_DB_USER}
      POSTGRES_PASSWORD: ${POSTGRES_DB_PASSWORD}

переменные POSTGRES_DB_USER и POSTGRES_DB_PASSWORD будут считаны из окружения. Это позволяет с определенной степенью удобности шаблонизировать docker-compose файлы. Т.е. нам нужно захватить окружение с хост-машины и передать внутрь контейнера.


Решим проблему написанием bash-скрипта.


#!/bin/sh
# создадим временный файл с уникальным именем
TMPFILE=$(mktemp)
# захватим окружение и запишем в файл
env > "${TMPFILE}"
# сохраним версию в отдельную переменную для удобства
VERSION="1.13.0"
# запустим docker-compose
docker run   --rm    -e PWD="$PWD"   --env-file "${TMPFILE}"    -v /var/run/docker.sock:/var/run/docker.sock   -v "$PWD:/rootfs/$PWD"   -w="/rootfs/$PWD"   docker/compose:"${VERSION}"   "$@"

# удаляем временный файл с захваченным списком переменных окружения
rm "{$TMPFILE}"

Появились дополнительные строчки:


  • -e PWD="$PWD" — на всякий случай пробросим текущий каталог
  • --env-file "${TMPFILE}" — здесь передаются все остальные переменные окружения с хост-машины
  • docker/compose:"${VERSION}" — имя образа, версию возьмем из переменной
  • "$@" — эта конструкция позволяет использовать скрипт как будто он и есть утилита docker-compose, т.е. "прозрачно" передает свои аргументы в docker-контейнер.

Скрипт можем сохранить, например, в /usr/local/bin/docker-compose, установить на него флаг eXecute и использовать. Приведенный скрипт не претендует на 100% отсутствие ошибок или недоработок и скорее является иллюстрацией метода.


Сами мы таким способом пользуемся в CI/CD пайплайнах. Это даже до некоторой степени позволяет экономить трафик, т.к. целевой образ docker берется из локального кэша.

Комментарии (5)


  1. Eivind
    04.10.2018 09:45

    Временный файл избыточен, проще сделать так:

    --env-file <(printenv)
    


    1. gecube Автор
      04.10.2018 10:40

      Спасибо, попробую. Я (не)долго думал, как это можно сделать, но не являюсь мастером шелла


  1. Cykooz
    04.10.2018 15:05

    Сейчас же разработчики docker переписали утилиту на go

    А вы в исходники на том самом гитхабе заглядывали? Я вот что-то беглым осмотром не нашёл ничего там про Go, зато там полно питонячих файлов.

    Скорее всего те бинарники, которые у них лежат в релизах — это, «запакованный» в один исполняемый файл, Python со всем нужным для запуска docker-compose.


    1. gecube Автор
      04.10.2018 16:47

      Возможно, что я и ошибся. Я точно знал, что есть проект github.com/docker/libcompose
      Поэтому тенденция для переписывания на Go точно есть.

      Сам бинарь открыл в hex-редакторе, погонял — да, похоже, что это просто python-скрипт упакованный в исполняемый файл. Нужно будет еще посмотреть внимательно.


      1. Cykooz
        04.10.2018 16:54

        Давно слышу эти слухи, про то что compose на Go переписали, но «воз и ныне там».
        Единственное что случилось — Swarm mode в докере научили понимать yaml файлы от docker-compose.