Ловушки на пути сине-зеленых развертываний с Docker
Docker (а также Kubernetes) предлагает вам способ обновлять ваши приложения без простоев с помощью общей стратегии, называемой
Сине-зеленое развертывание.
Сине-зеленые развертывания работают следующим образом:
- Ваше текущее развернутое приложение («Зеленый») обслуживает входящий трафик.
- Новая версия вашего приложения развернута («синяя») и протестирована, но еще не получает никакого трафика.
- Когда «Синий» будет готов, мы можем начать отправлять входящий трафик и на «Синий».
- На данный момент у нас есть две копии нашего приложения, работающие параллельно («Зеленая» и «Синяя»).
- Теперь мы должны перестать отправлять входящий трафик в «Зеленое» приложение, «Синее» обрабатывает весь входящий трафик.
- Поскольку «Зеленый» больше не получает трафика, его можно безопасно удалить.
- «Синий» будет помечен как «Зеленый», что позволит в будущем развернуть более новую версию с использованием той же стратегии.
Сине-зеленое развертывание Docker Swarm
Если вы используете Docker Swarm, это может быть файл стека, реализующий сине-зеленую стратегию развертывания.
version: '3.4'
services:
app:
image: acme/todo-list:${VERSION}
deploy:
update_config:
order: start-first
acme/todo-list
это простое веб-приложение списка дел. update_config.order: start-first
инструктирует docker swarm
используйте сине-зеленую стратегию развертывания.
Разверните v1
нашего списка задач в кластере Docker-Swarm, который мы можем запустить:
VERSION=v1 docker stack deploy todolist_app -c app.yml
Если мы хотим обновить приложение до v2
мы можем запустить:
VERSION=v2 docker stack deploy todolist_app -c app.yml
Обновление будет следовать сине-зеленой стратегии развертывания, как описано выше.
Докер сохранит v1
работает и будет развернут v2
.
Когда v2
готов, он будет перенаправлять весь трафик на v2
и удалит v1
. Аккуратный!
Но если мы посмотрим на журналы нашего балансировщика нагрузки, мы увидим что-то вроде:
1.2.3.4 - - [13/Jun/2019:06:00:10 +0000] "GET /toto/list HTTP/1.1" 502 150 ....
Код состояния 502
«Плохой шлюз».
Этот код состояния HTTP означает, что балансировщик нагрузки получил недопустимый ответ от сервера приложений (или не получил ответа).
Почему это?
Удалять v1
после прекращения отправки входящего трафика Docker отправляет SIG_TERM
сигнал к приложению и ждет
до 10 секунд, чтобы приложение корректно завершило работу.
Если v1
все еще работает через 10 секунд, Докер жестоко убивает v1
приложение.
Это прервет любое соединение, которое было у приложения (и ожидающие запросы получат ошибку 502).
Изящная остановка
Мы можем изменить количество секунд, в течение которых Docker будет ждать перед удалением контейнера, настроив параметр stop_grace_period
параметр:
version: '3.4'
services:
php:
image: acme/todo-list:${VERSION}
stop_grace_period: 120s
deploy:
update_config:
order: start-first
При такой конфигурации после отправки SIG_TERM
сигнал, докер будет ждать до двух минут, прежде чем закрыть приложение.
В зависимости от конкретной логики и времени отклика вашего приложения вы можете указать докеру, как долго
подождите, пока ваше приложение завершится, прежде чем принудительно его выполнить.
Сине-зеленые развертывания и PHP-FPM
Если вы используете PHP-FPM, предыдущих конфигураций может быть недостаточно.
К сожалению (?) PHP-FPM настроен по умолчанию на завершение сразу после получения SIG_TERM
сигнал.
Даже если докер готов ждать 10 секунд (или любое другое значение, которое вы могли настроить с помощью stop_grace_period
)
PHP завершит себя (и все обслуживаемые запросы) без ожидания.
Это снова приведет к 502
ошибки.
Чтобы решить эту проблему, мы также должны указать PHP, чтобы у него было достаточно времени для завершения обслуживания ожидающих запросов,
тюнинг process_control_timeout
параметр (проверьте здесь полный список конфигураций PHP-FPM).
Установив process_control_timeout = 5
,
PHP-FPM будет ждать до 5 секунд, прежде чем выйти и убить все процессы, которые обслуживали запросы.
Мы можем добавить этот параметр в Dockerfile
при создании нашего образа PHP.
FROM php:fpm
RUN { \
echo '[global]'; \
echo 'process_control_timeout = 5'; \
} | tee /usr/local/etc/php-fpm.conf
Точно так же, как докер ждал завершения работы контейнера, теперь PHP будет делать то же самое и ждать до 5 секунд.
чтобы его дочерние процессы завершили обслуживание запросов.
Таким образом мы настроили, как долго докер должен ждать перед завершением контейнера, а также как долго PHP
будет ждать завершения запросов.
Если PHP может остановить работу менее чем за 5 секунд, он это сделает (например, когда все ожидающие запросы будут быстро обработаны).
То же самое относится и к докеру.
Таким образом, эти тайм-ауты применяются только в худшем случае.
Этот пост был впервые опубликован на https://www.goetas.com/blog/traps-on-the-way-of-blue-green-deployments/.