Статьи

Docker Flow: сине-зеленое развертывание и относительное масштабирование

С тех пор, как я впервые положил руки на Docker, я начал писать сценарии, которые я выполнял в качестве непрерывного процесса развертывания. Я закончил со сценариями Shell, Ansible playbooks, поваренными книгами Chef, Jenkins Pipelines и так далее. У каждого из них была похожая (не говоря о том же) цель в своем контексте. Я понял, что это огромная трата времени, и решил создать один исполняемый файл, который я смогу запустить независимо от того, какой инструмент я использую для запуска конвейера непрерывного развертывания. Результатом является рождение проекта Docker Flow .

Особенности

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

Я воздержусь от объяснения сине-зеленого развертывания, поскольку я уже написал немало статей на эту тему. Если вы не знакомы с ним, пожалуйста, прочитайте сообщение о развертывании Blue-Green . Более подробный практический пример с Jenkins Pipeline можно найти в публикации Blue-Green Deployment To Docker Swarm with Jenkins Workflow Plugin . Наконец, для другого практического примера в гораздо более широком контексте (и без Дженкинса), пожалуйста, обратитесь к сериям Scaling To Infinity с Docker Swarm, Docker Compose и Consul .

Вторая особенность, которую я хотел представить, — это относительное масштабирование . Docker Compose позволяет легко масштабировать сервисы до фиксированного числа. Мы можем указать, сколько экземпляров контейнера мы хотим запустить и наблюдать, как разворачивается магия. В сочетании с Docker Swarm результатом является простой способ управления контейнерами внутри кластера. В зависимости от того, сколько экземпляров уже запущено, Docker Compose увеличит (или уменьшит) количество запущенных контейнеров, чтобы достичь желаемого количества экземпляров. Однако Docker Compose всегда ожидает фиксированное число в качестве параметра. Я нашел это очень ограниченным при работе с производственными развертываниями. Во многих случаях мне не нужно знать, сколько экземпляров уже запущено, а просто отправить сигнал об увеличении (или уменьшении) емкости на некоторый фактор. Например, у нас может быть увеличение трафика и мы хотим увеличить пропускную способность в три раза. Точно так же, если спрос на какой-либо сервис уменьшается, мы можем захотеть, чтобы количество запущенных экземпляров также уменьшилось и, таким образом, освободились ресурсы для других сервисов и процессов. Эта необходимость становится еще более очевидной, когда мы движемся к автономным и автоматизированным Самовосстанавливающимся Системам, где взаимодействие человека сводится к минимуму.

Запуск Docker Flow

Требования Docker Flow : Docker Engine , Docker Compose и Consul . Идея проекта заключается не в замене какой-либо функциональности Docker, а в предоставлении дополнительных функций. Предполагается, что контейнеры определены в файле docker-compose.yml (путь может быть изменен) и что Consul используется в качестве реестра служб (вскоре будет расширен и на etcd и Zookeeper).

В следующих примерах будет настроена среда с Docker Engine и Consul, и предполагается, что у вас уже установлена Docker Toolbox . Несмотря на то, что примеры будут запускаться через Docker Toolbox Terminal , не стесняйтесь применять их к существующим настройкам. Вы можете выполнять их на любом из ваших серверов Docker или, что еще лучше, в кластере Docker Swarm.

Давайте начнем настраивать среду, которую мы будем использовать в этой статье. Пожалуйста, запустите Docker Toolbox Terminal и клонируйте репозиторий проекта.

1
2
3
git clone https://github.com/vfarcic/docker-flow
 
cd docker-flow

Следующим шагом является загрузка последней версии Docker Flow . Пожалуйста, откройте страницу Docker Flow Releases , загрузите релиз, соответствующий вашей ОС, переименуйте его в docker-flow и убедитесь, что он имеет разрешения на выполнение (например, chmod +x docker-flow ). В остальной части статьи предполагается, что бинарный файл находится в каталоге docker-flow, созданном, когда мы клонировали репозиторий. Если впоследствии вы решите использовать его, поместите его в один из каталогов, включенных в ваш PATH .

Прежде чем мы продолжим, давайте посмотрим на файл docker-compose.yml, который мы будем использовать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
version: '2'
 
services:
app:
image: vfarcic/books-ms${BOOKS_MS_VERSION}
ports:
– 8080
environment:
– SERVICE_NAME=books-ms
– DB_HOST=books-ms-db
 
db:
container_name: books-ms-db
image: mongo
environment:
– SERVICE_NAME=books-ms-db

Как видите, в нем нет ничего особенного. У него две цели. Цель приложения определяет основной контейнер сервера. БД является «побочной» целью, требуемой приложением . Поскольку версия 2 , Docker Compose будет использовать одну из своих новых функций и создаст сеть вокруг этих целей, позволяющую контейнерам взаимодействовать друг с другом (удобно при развертывании в кластере). Наконец, образ приложения использует переменную среды BOOKS_MS_VERSION, которая позволит нам моделировать несколько выпусков. Я предполагаю, что вы уже использовали Docker Compose и что нет причин вдаваться в подробности.

Мы будем использовать docker-machine для создания виртуальной машины, которая будет имитировать нашу производственную среду.

1
2
3
4
docker-machine create \
    --driver virtualbox docker-flow
 
eval "$(docker-machine env docker-flow)"

Docker Flow должен хранить состояние контейнеров, которые он развертывает, поэтому я решил использовать Consul в этой первой итерации проекта. Другие реестры ( etcd , Zookeeper и все, что кто-то просит) скоро появятся.

Давайте приведем пример.

1
2
3
4
5
6
7
docker run -d \
    -p "8500:8500" \
    -h "consul" \
    --name "consul" \
    progrium/consul -server -bootstrap
 
export CONSUL_IP=$(docker-machine ip docker-flow)

Теперь мы готовы начать развертывание сервиса books-ms .

Развертывание с простоями

Прежде чем мы углубимся в функции Docker Flow , давайте посмотрим, как развертывание работает с Docker Compose.

Мы начнем с развертывания версии 1.0 .

1
2
3
BOOKS_MS_VERSION=:1.0 docker-compose up -d app db
 
docker-compose ps

Вывод команды docker-compose ps выглядит следующим образом.

1
2
3
4
Name                Command          State            Ports
--------------------------------------------------------------------------
books-ms-db        /entrypoint.sh mongod   Up      27017/tcp
dockerflow_app_1   /run.sh                 Up      0.0.0.0:32771->8080/tcp

Мы не сделали ничего особенного (пока). Два контейнера ( mongo и books-ms version 1.0) работают.

Давайте посмотрим, что произойдет, если мы развернем новую версию.

1
2
3
export BOOKS_MS_VERSION=:latest
 
docker-compose up -d app db

Вывод команды docker-compose up выглядит следующим образом.

1
2
Recreating dockerflow_app_1
books-ms-db is up-to-date

Проблема заключается в первой строке, которая утверждает, что dockerflow_app_1 воссоздается. Из-за этого может возникнуть немало проблем, две из которых — простои и невозможность протестировать новую версию перед тем, как сделать ее доступной для работы. Мало того, что служба будет недоступна в течение (надеюсь) короткого периода, но она также не будет проверена. В данном конкретном случае я не хочу сказать, что служба вообще не тестировалась, но развертывание не было протестировано. Даже если вы выполняете набор интеграционных тестов в промежуточной среде, нет гарантии, что те же тесты будут проходить в производстве. По этой причине предпочтительным способом обработки этого сценария является применение сине-зеленого процесса развертывания . Мы должны запустить новый выпуск параллельно со старым, выполнить набор тестов, которые подтверждают, что развертывание выполнено правильно и что служба интегрирована с остальной частью системы, и, если все прошло как ожидалось, переключите прокси от старого к новому выпуску. Благодаря этому процессу мы избегаем простоев и в то же время гарантируем, что новая версия действительно работает, как и ожидалось, до того, как она станет доступна нашим пользователям.

Прежде чем мы увидим, как мы можем выполнить сине-зеленое развертывание с Docker Flow , давайте уничтожим контейнеры, которые мы только что запустили, и начнем все сначала.

1
docker-compose down

Сине-зеленое развертывание

Docker Flow — это отдельный двоичный файл, расположенный поверх Docker Compose и использующий обнаружение служб, чтобы решить, какие действия следует выполнить. Это позволяет комбинировать три вида входов; аргументы командной строки, переменные окружения и определение docker-flow.yml . Начнем с аргументов командной строки.

1
2
3
4
5
./docker-flow \
    --consul-address=http://$CONSUL_IP:8500 \
    --target=app \
    --side-target=db \
    --blue-green

Давайте посмотрим, какие контейнеры работают.

1
2
docker ps \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
IMAGE                     STATUS              NAMES
vfarcic/books-ms:latest   Up About a minute   dockerflow_app-blue_1
mongo                     Up About a minute   books-ms-db
progrium/consul           Up 32 minutes       consul

Существенным отличием по сравнению с примерами без Docker Flow является имя развернутой цели. На этот раз контейнер называется dockerflow_app-blue_1 . Поскольку это было первое развертывание, работает только синяя версия.

Давайте посмотрим, что произойдет, когда мы развернем второй выпуск. На этот раз мы будем использовать комбинацию переменных среды и файла docker-flow.yml . Последнее заключается в следующем.

1
2
3
4
target: app
side_targets:
– db
blue_green: true

Как видите, аргументы в файле docker-flow.yml (почти) совпадают с аргументами, которые мы использовали в командной строке. Разница в том, что ключи используют тире ( ) вместо подчеркивания ( _ ), чтобы они следовали соглашениям YML. Второе отличие состоит в том, как списки (в данном случае side_targets ) определены в YML.

Переменные среды также следуют тем же наименованиям, но, как и YML, отформатированы в соответствии с соответствующими соглашениями об именах. Все заглавными буквами. Другое отличие — префикс FLOW . Он добавлен для того, чтобы переменные среды, используемые в Docker Flow , не перекрывали другие переменные, которые могут использоваться вашей системой.

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

Давайте повторим развертывание. На этот раз мы укажем адрес Консула в качестве переменной среды и будем использовать docker-flow.yml для остальных аргументов.

1
2
3
4
5
6
export FLOW_CONSUL_ADDRESS=http://$CONSUL_IP:8500
 
./docker-flow --flow=deploy
 
docker ps \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
5
IMAGE                     STATUS              NAMES
vfarcic/books-ms:latest   Up 24 seconds       dockerflow_app-green_1
vfarcic/books-ms:latest   Up 4 minutes        dockerflow_app-blue_1
mongo                     Up About an hour    books-ms-db
progrium/consul           Up 2 hours          consul

Как видите, на этот раз оба релиза работают параллельно. Зеленый выпуск присоединился к синему выпуску, который мы запускаем ранее. В этот момент вы должны запустить свои интеграционные тесты (я склонен называть их тестами после развертывания ) и, если кажется, что все работает правильно, изменить прокси-сервер так, чтобы он указывал на новый выпуск ( зеленый ). Выбор, как это сделать, остается за вами (я склоняюсь к шаблону Consul, чтобы изменить конфигурацию моего nginx или HAProxy ). Планируется включить реконфигурацию и перезагрузку прокси-сервера в Docker Flow , но до этого вы должны делать это самостоятельно.

После того, как развертывание протестировано, и прокси-сервер переконфигурирован, ваши пользователи будут перенаправлены на новый выпуск, и вы сможете остановить старый. Docker Flow может помочь вам и в этом.

1
2
3
4
./docker-flow --flow=stop-old
 
docker ps -a \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
5
IMAGE                     STATUS                        NAMES
vfarcic/books-ms:latest   Up 5 minutes                  dockerflow_app-green_1
vfarcic/books-ms:latest   Exited (137) 38 seconds ago   dockerflow_app-blue_1
mongo                     Up About an hour              books-ms-db
progrium/consul           Up 2 hours                    consul

Как видите, остановился старый релиз ( синий ). Контейнеры были намеренно только остановлены и не удалены, так что вы можете легко откатиться, если обнаружите, что что-то пошло не так после перенастройки прокси.

Давайте посмотрим на вторую функцию Docker Flow .

Относительное масштабирование

Как и в случае с Docker Compose, Docker Flow позволяет масштабировать службу до фиксированного числа экземпляров.

Давайте масштабировать наш сервис до двух экземпляров.

1
2
3
4
5
6
./docker-flow \
    --flow=deploy \
    --scale=2
 
docker ps \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
5
6
IMAGE                     STATUS              NAMES
vfarcic/books-ms:latest   Up 4 seconds        dockerflow_app-blue_2
vfarcic/books-ms:latest   Up 4 seconds        dockerflow_app-blue_1
vfarcic/books-ms:latest   Up About a minute   dockerflow_app-green_1
mongo                     Up 4 hours          books-ms-db
progrium/consul           Up 5 hours          consul

Как и ожидалось, два экземпляра новой версии ( синие ) были развернуты. Это поведение аналогично тому, что предлагает Docker Compose (за исключением добавления сине-зеленого развертывания). То, что позволяет Docker Flow , — это предварительно добавить значение шкалы со знаками плюс ( + ) или минус ( ). Давайте посмотрим на это в действии, прежде чем обсуждать преимущества.

1
2
3
4
5
6
./docker-flow \
    --flow=deploy \
    --scale=+2
 
docker ps \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
5
6
7
8
9
IMAGE                     STATUS              NAMES
vfarcic/books-ms:latest   Up 6 seconds        dockerflow_app-green_4
vfarcic/books-ms:latest   Up 7 seconds        dockerflow_app-green_3
vfarcic/books-ms:latest   Up 7 seconds        dockerflow_app-green_2
vfarcic/books-ms:latest   Up 22 minutes       dockerflow_app-blue_2
vfarcic/books-ms:latest   Up 22 minutes       dockerflow_app-blue_1
vfarcic/books-ms:latest   Up 24 minutes       dockerflow_app-green_1
mongo                     Up 5 hours          books-ms-db
progrium/consul           Up 5 hours          consul

Поскольку было два экземпляра предыдущего выпуска, новый выпуск был развернут, а количество экземпляров было увеличено на два (всего четыре). Хотя это полезно, когда мы хотим развернуть новый выпуск и заранее знаем, что число экземпляров должно быть масштабировано, гораздо более распространенным вариантом будет запуск Docker Flow с аргументом –flow = scale . Он будет следовать тем же правилам масштабирования (и удаления масштаба), но без развертывания нового выпуска. Прежде чем мы попробуем это, давайте остановим старый выпуск.

1
./docker-flow --flow=stop-old

Давайте попробуем --flow scale чтобы --flow scale количество экземпляров на один.

1
2
3
4
./docker-flow --scale=-1 --flow=scale
 
docker ps \
    --format "table{{.Image}}\t{{.Status}}\t{{.Names}}"

Вывод команды docker ps выглядит следующим образом.

1
2
3
4
5
6
IMAGE                     STATUS              NAMES
vfarcic/books-ms:latest   Up 19 minutes       dockerflow_app-green_3
vfarcic/books-ms:latest   Up 19 minutes       dockerflow_app-green_2
vfarcic/books-ms:latest   Up 43 minutes       dockerflow_app-green_1
mongo                     Up 5 hours          books-ms-db
progrium/consul           Up 5 hours          consul

Количество запущенных экземпляров было сокращено на один (с четырех до трех). Масштабирование и удаление накипи с использованием относительных значений имеет много применений. Например, вы можете запланировать масштабирование некоторых своих сервисов каждое утро понедельника, потому что по опыту знаете, что именно в это время они получают увеличенный трафик. В соответствии с тем же сценарием вы можете захотеть удалять накипь каждый понедельник после обеда, потому что в это время трафик возвращается к нормальному состоянию и вы хотите освободить ресурсы для некоторых других служб. Когда масштабирование / удаление накипи автоматизировано, использование абсолютных чисел опасно. У вас может быть сценарий, который масштабируется от четырех до шести экземпляров в часы пик. Через некоторое время обычные часы могут потребовать восемь экземпляров, а масштабирование на пиковые часы до шести приведет к обратному эффекту, фактически удаляя накипь. Необходимость относительного масштабирования и удаления накипи становится еще более очевидной в случае Самовосстанавливающихся Систем .

Дорожная карта

Хотя в примерах из этой статьи использовался один сервер, основной сценарий использования этих функций находится внутри кластера Docker Swarm. Для получения информации обо всех аргументах, которые могут использоваться, пожалуйста, обратитесь к README Docker Flow .

Это самое начало проекта Docker Flow . Основная идея заключается в том, чтобы предоставить простой способ выполнения процессов (например, сине-зеленое развертывание) и обеспечить простую интеграцию с другими инструментами (такими как Consul). У меня есть огромный список функций, которые я планирую добавить. Однако, прежде чем объявить их, я хотел бы получить некоторые отзывы. Как вы думаете, этот проект будет полезен? Какие функции вы хотели бы видеть дальше? С какими инструментами вы хотели бы интегрировать его?