Несколько малоизвестных возможностей docker-compose

?v=1

Во многих инструкциях с просторов интернета описывают некий минимум действий, и как следствие минимум команд и возможностей.
Я решил сделать некую подборку мало освещенных возможностей, особенностей. Статья не претендует на уникальность, это и мне, как памятка, и возможно некоторым падаванам поможет, начинающий свой путь с docker-compose.


Использование нескольких docker-compose.yml файлов.

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

Опишем пример базового docker-compose-base.yml.
Предположим это настроенный образ nginx с сертификатами, тюнингом, и скажем метриками. И exporter для prometheus:

version: '2'
services:
  nginx:
    image: nginx
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter

Теперь опишем пример нашего приложения docker-compose-app.yml:

version: '2'
services:
  backend:
    image: internal.local/super-app:0.1.2

Для запуска нужна привычная нам команда с одним отличием указывать будем 2 docker-compose файла :

docker-compose up -d -f docker-compose-base.yml -f docker-compose-app.yml

И вуаля, мы получаем набор сервисов, как если бы они были описаны в едином docker-compose файле!

Так же есть второй вариант использования нескольких файлов, через использование директивы extends.

docker-compose-base.yml:

version: '2'
services:
  nginx:
    image: nginx
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter

docker-compose-app.yml:

version: '2'
services:
  backend:
    image: internal.local/super-app:0.1.2
  ### Добавляем секцию с веб сервером
  web:
    extends:
      # В какой файл смотрим (относительный или полный путь)
      file: docker-compose-base.yml
      # Какой сервис берем оттуда к нам
      service: nginx
  web-exporter:
    extends:
      file: docker-compose-base.yml
      service: nginx-exporter

Какой вариант выбрать — выбирать вам. Все индивидуально, я лишь хотел показать варианты =)


Наследование в docker-compose

Следующий пример требует версию docker-compose >= 2.4
Тоже довольно интересная особенность, причем действительно мало где упоминается.
Этот функционал позволяет нам описывать несколько однотипных сервисов в docker-compose файле, при этом не дублируя их описание, а именно наследуя.
Например у нас есть такой файл:

version: '2.4'
services:
  backend:
    image: internal.local/super-app:0.1.2
    ports:
      - 8080:8080
      - 9090:9090
    volumes:
      - ./conf/some.conf:/etc/app/some.conf:ro

И появилась необходимость поднимать несколько контейнеров, но с некоторыми различиями, можем конечно «накопипастить» и поменять, а можем сделать так:

version: '2.4'
services:
  backend:
   &base-app #все что под данным указателем будет доступно по его имени
    image: internal.local/super-app:0.1.2
    ports:
      - 8080:8080
      - 9090:9090
    volumes:
      - ./conf/some.conf:/etc/app/some.conf:ro

  backend-2:
  <<: *base-app #наследуемся
  ports: # переопределяем опубликованные порты
     - 8081:8080

Таким образом мы получаем возможность изменять в одном месте, чем править в описании каждого контейнера.
Есть еще вариант вынести в корневую область, например:

version: '2.4'
services:

x-backend: #Секции начинающиеся с "x-" будут игнорироваться, но их можно переиспользовать.
  &back-app
  image: internal.local/super-app:0.1.2
  ports:
    - 8080:8080
    - 9090:9090
  volumes:
    - ./conf/some.conf:/etc/app/some.conf:ro

  backend:
   <<: *base-app #наследуемся

  backend-2:
  <<: *base-app #наследуемся
  ports: # переопределяем опубликованные порты
     - 8081:8080


Ограничения по ресурсам

Начиная с версии 2.2 можно использовать ограничения по ресурсам для контейнеров, на самом деле с версии 2.1, но там еще не все завезли =)
Есть нюанс! В версии 3 эти возможности убрали! Там уже упор на docker swarm.

Самый простой пример ограничения ресурсов по CPU, MEM:

version: '2.2'
services:
  backend:
    cpus: 1.5 #Позволяем использовать полтора ядра.
    cpuset: '0,3' #Использовать первое и четвертое ядро системы.
    mem_limit: 1000000000 #Позволяем использовать 1Гб памяти
    memswap_limit: 2000000000 #Ограничиваем SWAP двумя Гб памяти.
    oom_kill_disable: true # В редких случаях, надо гарантировать что OOM Killer не убьет наше приложение в случае нехватки памяти, вот так можем запретить ему убивать контейнер.

    image: internal.local/super-app:0.1.2
    ports:
      - 8080:8080
      - 9090:9090
    volumes:
      - ./conf/some.conf:/etc/app/some.conf:ro


Упаковка образов в архив

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

#!/bin/bash

dc=${1}

if [ ! -z ${dc} ] && [ -f ${dc} ]; then
  echo "Saving docker images from file ${dc}..."
  images=`grep image: ${dc} | awk '{print $2}'`
  docker save ${images} | gzip > docker-images.gz
  echo "Success!"
else
  echo "ERROR! You must set path to docker-compose.yml as argument!"
fi

Сохраняем в файл скажем docker-compose-images-save.sh
Даем права на исполнение:
chmod +x docker-compose-images-save.sh
Запускаем, и в качестве аргумента передаем путь до docker-compose файла:
./docker-compose-images-save.sh /home/some_user/docker-compose-app.yml
На выходе получим в папке откуда вызвали скрипт архив с образами — docker-images.gz
Любым доступным образом отправляем на удаленный сервер.
Теперь на удаленном сервере достаточно выполнить:
gzip -cd docker-images.gz | docker load
Все образы загрузятся в локальный реестр, после чего можно тут смело запускать
docker-compose up -d, по скольку все образы есть в локальном реестре в интернет уже докер не полезет.


Пробрасываем IPv6

В определенных задачах ipv6 бывает крайне полезен, взять хотя бы нюанс, что Роскомнадзор пропускает по ipv6 без проблем весь трафик, и тот же телеграм бот работает без проблем.
Я рассмотрю ситуацию, когда ipv6 нет на вашей машине, будь то виртуалка, или сервер в интернете.
Неоходимо убедиться, что ipv6 на уровне системы включен:

    sysctl net.ipv6.conf.all.disable_ipv6

Значение должно быть равно 0, если не так, изменяем:

    sysctl -w net.ipv6.conf.all.disable_ipv6=0

Устанавливаем miredo (Это сервис с встроенным впн до сервера, который выдаст нам публичный ipv6)

    apt-get install miredo -y

Проверяем, что сервис запушен:

    systemctl status miredo

Проверяем, что мы получили ipv6 адрес:

    ifconfig teredo

Прописываем в /etc/docker/daemon.json

    {
        "ipv6": true,
        "fixed-cidr-v6": "2001:db8:1::/64"
    }

Рестартуем докер:

    systemctl restart docker

Ну и осталось включить NAT для ipv6, чтобы внутренние адреса нашего контейнера смогли выходить в внешний мир через наш teredo интерфейс:

    ip6tables -t nat -A POSTROUTING -o teredo -j MASQUERADE

Поднимаем docker контейнер нужный нам, и он может выходить в свет через ipv6 адрес.


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

Надеюсь кому-то предоставленная информация здесь будет полезна.

© Habrahabr.ru