Комментарии 17
Как я понимаю, продакшен у вас - это один сервер? Даже в таком случае можно включить Docker Swarm на сервере и тогда вам будет доступен deploy режим с его фичами по плавному апдейту и роллбеку. Есть и минусы - вы сразу теряете depends_on.
Может полезно будет docker compose --profile green ...
И темплейты возможно, не сильно ими пользовался
K3s поставьте поэкспериментируйте, там совсем другой уровень.
Там хоть сине зеленое, хоть канареечное развертывание можно сделать просто меняя labels у service.
Расписывать не буду, инфы море. Но для старта ваш проект хороший конечно
получился вполне рабочий blue‑green деплой с нулевым даунтаймом
Но есть побочка: принудительный NACK посреди обработки может порождать непредвиденые и нетестированные состояния системы.
мы не прерываем уже выполняющиеся задачи, мы просто не принимаем новые, т.е отправляем их обратно в очередь на обработку нашему новому активному фоновому процессу
Возможно я неправильно понял вот это:
если False - переносим задачу на повторную обработку (например, в rabbitmq делаем сразу nack(requeue=true))
В каких случаях потребуется NACK ?
У нас это сделано как обёртка над consumer - проверка is_accepting происходит до начала обработки сообщения. сценарий такой:
мы получаем сообщение в нашем фоновом процессе, назовем его GREEN
мы сразу проверяем is_accepting
если False - мы даже не заходим в бизнес логику - сразу делаем nack(requeue=true)
сообщение уходит обратно в очередь и его обработает уже новый активный фоновый процесс BLUE, у которого при инициализации is_accepting=True
сам GREEN процес при этом находится в режиме graceful shutdown: он перестает принимать новые задачи, но при этом корректно завершает уже запущенные
получается, что выполняющиеся задачи никогда не прерываются - nack выполняется только к новым сообщениям
Неплохое решение, сам что-то подобное сделал. Но, как по мне, реализацию healthcheck лучше вынести в Dockerfile, в Вашем случае может быть так HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 CMD wget -qO- http://localhost:8000/v1/health || exit 1 При этом не придётся выставлять порты наружу. Это более универсальное решение, healthcheck может быть любым и ему самое место в Dockerfile. И если что-то пойдёт не так, то не придётся прогонять все 30 итераций, цикл сразу прервётся. В самом скрипте проверять статус контейнера через docker inspect, вынес в отдельную функцию, :
wait_for_healthy_or_rollback() { # ($1: название_контейнера)
for ((i = 1; i <= $MAX_ATTEMPTS; i++)); do
HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' $1)
if [[ "$HEALTH_STATUS" == "healthy" ]]; then
echo ">>> Контейнер $1 успешно запущен ✅️"
return 0
fi
if [[ "$HEALTH_STATUS" == "unhealthy" ]]; then
echo ">>> Ошибка при запуске контейнера $1, IMAGE_TAG=$IMAGE_TAG ❌" >&2
return 0
fi
echo ">>> Жизнеспособность $1: '$HEALTH_STATUS'. Следующая проверка через $DELAY сек..."
if (( i < $MAX_ATTEMPTS )); then
sleep "$DELAY"
fi
done
echo ">>> Ошибка: превышено максимальное количество попыток ($MAX_ATTEMPTS) ❌" >&2
return 1
}
Бонусом ко всему можно будет так же проверять, с помощью docker inspect, погасла ли старая версия контейнера. Не гадая, какие задержки нужно выставить. Запуск миграций у меня в том же скрипте, с их откатом, если что-то пошло не так.
Почему бы просто не поднять воркеров с новым образом внутри и не переключить трафик на них? Если все хорошо - старые убиваются, если что-то не так - переключаешь трафик обратно.
В нашем случае “просто поднять новые воркеры и переключить трафик” проблематично, потому что система построена как event-driven DAG-пайплайн с 7-9 стадиями на каждый доменный агрегат. Т.е для обработки каждой стадии у нас есть consumer, который прослушивает определенную очередь. Это сделано для того, чтобы пользователь мог постепенно контролировать и взаимодействовать с процессом генерации.
В rabbitmq переключить трафик можно:
Через bindings (создание + удаление) - сразу отметаем по понятным причинам
через разные blue/green exchange + прокидывать в приложение current_deploy(blue/green) - но это может привести к такому состоянию, когда веб и воркер могут работать с разными версиями current_deploy
в любом случае в самом приложении(не на уровне инфры) мы должны будем реагировать на sigterm и ожидать выполнения текущих задач

Как я реализовал Blue-Green деплой с нулевым даунтаймом на Docker Compose