Оптимизируем воркеров внутри докер контейнера: Вот так выглядит файл сервиса до наших правок: [Unit] Description=consumer.dock After=docker.service Requires=docker.service
[Service] Type=simple Environment=CONTAINER_NAME=consumer.dock Restart=always RestartSec=10s
ExecStartPre=-/usr/bin/docker stop "${CONTAINER_NAME}" ExecStartPre=-/usr/bin/docker rm "${CONTAINER_NAME}" ExecStart=/usr/bin/docker run -i --name "${CONTAINER_NAME}" 'api' bin/console messenger:consume --limit=10 --time-limit=3600
ExecStop=/usr/bin/docker stop "${CONTAINER_NAME}" В целом, здорово. Но: когда воркер заканчивает свою работу, контейнер полностью пересоздается. Целевая идея: поднять внутри supervisor, который уже будет переподнимать воркера. Таким образом, контейнер не будет постоянно ребутаться. Создаем supervisord.conf рядом с докерфайлом: [supervisord] nodaemon=true user=root logfile=/dev/stdout logfile_maxbytes=0
[program:messenger-consumer] command=php bin/console messenger:consume --limit=10 --time-limit=3600 directory=/var/www/html autostart=true autorestart=true numprocs=1 startsecs=1 stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 Обновляем докерфайл: FROM php:8.2-cli # или ваш базовый образ 'api'
#1. Устанавливаем supervisorRUN apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/*
#2. Копируем код вашего приложенияCOPY . /var/www/html WORKDIR /var/www/html
#3. Копируем конфиг супервизораCOPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
#4. Запускаем supervisor (он станет PID 1 и будет следить за воркером)CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] Ну и обновляем сервис: [Unit] Description=consumer.dock After=docker.service Requires=docker.service
[Service] Type=simple Environment=CONTAINER_NAME=consumer.dock Restart=always RestartSec=10s
ExecStartPre=-/usr/bin/docker stop "${CONTAINER_NAME}" ExecStartPre=-/usr/bin/docker rm "${CONTAINER_NAME}"
#Убран флаг -i и команда Symfony (она теперь внутри supervisor)ExecStart=/usr/bin/docker run --name "${CONTAINER_NAME}" 'api'
ExecStop=/usr/bin/docker stop "${CONTAINER_NAME}"
[Install] WantedBy=multi-user.target
Остается нерешенным еще один вопрос: мы релизим новую версию приложения. Если раньше воркер заканчивал работу, контейнер пересоздавался, новая версия образа вставала автоматически.
Модифицируем конфиг supervisord: [program:messenger-consumer] command=php bin/console messenger:consume --limit=10 --time-limit=3600 stopsignal=TERM ; <--- Обязательно (Symfony фсловят этот сигнал) stopwaitsecs=3600 ; <--- Обязательно (Даем воркерам время доделать задачи) autorestart=true ; Во время обычной работы пусть вечно их поднимает
А внутрь контейнера положим скрипт остановки (мягкой) #!/bin/bash
echo "[Deploy Hook] Начинаю плавную остановку воркеров..."
#1. Посылаем команду останова всем группам в Supervisor. #Supervisor пошлет им SIGTERM и будет ЖДАТЬ время, указанное в stopwaitsecs.supervisorctl stop all
#2. Ждем, пока все процессы перейдут в статус STOPPEDecho "[Deploy Hook] Ожидаю завершения текущих задач..." while true; do
#Проверяем, есть ли процессы в статусе RUNNING или STOPPINGRUNNING_COUNT=$(supervisorctl status | grep -E "RUNNING|STOPPING" | wc -l)
if [ "$RUNNING_COUNT" -eq 0 ]; then echo "[Deploy Hook] Все воркеры успешно завершили работу." break fi
echo "[Deploy Hook] Воркеров в работе: $RUNNING_COUNT. Ждем 5 секунд..." sleep 5 done
#3. Когда все воркеры выключились сами — гасим основной Supervisorecho "[Deploy Hook] Выключаю Supervisord. Контейнер завершает работу." supervisorctl shutdown
Снаружи остается лишь вызвать docker exec consumer.dock /usr/local/bin/stop-workers.sh