Коли застосунок масштабується горизонтально (кілька реплік на одному чи кількох серверах), з'являється проблема дублювання: те, що має відбутися один раз, відбувається в кожній репліці.
Планувальник:
1. Окремий сервіс з однією реплікою - найпростіше:
services:
scheduler:
image: myapp:1.4.0
command: php artisan schedule:work
deploy:
replicas: 1
Але «рівно одна репліка» не гарантована: під час rolling update старий і новий контейнери можуть жити одночасно, а на кількох вузлах - збій мережі може дати два активні планувальники.
2. onOneServer() - Laravel бере атомарне блокування в спільному кеші перед запуском задачі:
Schedule::command('reports:generate')
->dailyAt('02:00')
->onOneServer();
Schedule::command('feeds:sync')
->everyFiveMinutes()
->withoutOverlapping(10)
->onOneServer();
- драйвер кешу має бути спільним і з атомарними блокуваннями: Redis, Memcached, database, DynamoDB. З
fileчиarrayу кожного контейнера своє «блокування» - захисту немає; onOneServerзахищає від дублювання в межах однієї хвилини,withoutOverlapping- від паралельного запуску, поки попередній ще працює (з терміном життя блокування на випадок падіння).
Разові artisan-команди:
- не через
docker execу довільну репліку - результат залежить від того, куди потрапили, і команда помре разом з контейнером при деплої; - разовий контейнер з тим самим образом і оточенням:
docker compose run --rm app php artisan users:reindex, у Kubernetes - Job; - довгі операції - розбити на job у черзі: команда лише ставить задачі, а воркери обробляють їх порціями з повторами й видно прогрес;
- ідемпотентність: команду, яку можуть запустити двічі, робити безпечною для повтору.
Що ще потребує координації:
- міграції -
migrate --isolated; queue:restart,horizon:terminate- сигнал через спільний кеш досягає всіх воркерів, незалежно від того, з якого контейнера його надіслано;- кеш-очищення (
cache:clear) очищує спільне сховище - один раз для всіх, а не в кожній репліці.
Діагностика дублювання: логувати ім'я хоста (gethostname() дає ID контейнера) у задачах планувальника - видно, скільки разів і де вони виконались.
Докладніше в документації: Laravel: задачі на одному сервері