И еще один кусочек пазла:

Что мы уже имеем:

  • Контейнер создан и работает на базе образа с symfony api
  • Внутри работает runit, который перезапускает consumer сообщений (тут мы выиграли в скорости, потому что рестарт воркера теперь не приводит к полному пересозданию контейнеров)

Новые вводные: когда происходит релиз новой версии docker образа с приложением, надо мягко закончить активную задачу consumer'а и перезапустить контейнер на базе нового образа. Самым простым решением - переписать run скрипт следующим образом: #!/bin/sh exec 2>&1 cd /var/www/html

RELEASE_FILE="/var/www/shared/current_release"

#1. Запоминаем версию релиза ПЕРЕД запуском воркера

START_RELEASE="unknown" if [ -f "$RELEASE_FILE" ]; then START_RELEASE=$(cat "$RELEASE_FILE") fi

echo "Starting Symfony consumer (Release: $START_RELEASE)..."

#2. Запускаем воркер. Он обработает 100 сообщений и завершится

php bin/console messenger:consume async --limit=100

#3. Читаем версию релиза ПОСЛЕ завершения воркера

CURRENT_RELEASE="unknown" if [ -f "$RELEASE_FILE" ]; then CURRENT_RELEASE=$(cat "$RELEASE_FILE") fi

#4. Сверяем версии

if [ "$START_RELEASE" != "$CURRENT_RELEASE" ]; then echo "Release changed from $START_RELEASE to $CURRENT_RELEASE. Killing container..."

#Завершаем PID 1, чтобы Docker пересоздал контейнер

kill -TERM 1 exit 0 fi

echo "Release is the same. Runit will restart the worker inside this container."

#Небольшая пауза в 1 секунду перед выходом из скрипта (чтобы Runit не спамил #рестартами в случае моментального падения PHP, например, из-за ошибки в БД)

sleep 1 Идея такая: вместе с образом на хост доставляется хэш последнего коммита, на базе которого построен образ. Этот хэш в виде файла смонтирован через -v внутрь каждого консьюмера. Однако, limit 100 заставит выполниться все оставшиеся задачи и только потом произойдет обновление. Так что воспользуемся встроенной механикой symfony messenger: https://symfony.com/doc/current/messenger.html#deploying-to-production А именно: bin/console messenger:stop-workers

Таким образом, имеем следующую последовательность:

  • Сборка образа и доставка на прод меняет хэш коммита
  • CI/CD скрипт выполняет bin/console messenger:stop-workers в каждом воркере
  • Воркер заканчивает текущую задачу и выходит
  • run скрипт обнаруживает изменение хэша коммита и выходит, из-за чего пересоздается контейнер уже на основе нового образа