Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як запускати задачі планувальника й разові artisan-команди, коли контейнерів застосунку кілька?

Коли застосунок масштабується горизонтально (кілька реплік на одному чи кількох серверах), з'являється проблема дублювання: те, що має відбутися один раз, відбувається в кожній репліці.

Планувальник:

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: задачі на одному сервері

1

Схожі питання