How to handle SIGTERM signal on laravel 8,9,10 deploy with docker containers in regards to scheduler and worker?

huangapple go评论57阅读模式
英文:

How to handle SIGTERM signal on laravel 8,9,10 deploy with docker containers in regards to scheduler and worker?

问题

我看到 Laravel 8、9 和 10 在 SIGTERM 信号上处理队列工作器。但根据下面 Worker 类的注释(Worker.php 第 185 行),Laravel 仅在当前工作器执行期间处理它,如果没有修改 supervisor(或其他使用的监控工具)以考虑到 SIGTERM 并且不再启动工作器,那么它的影响会很小,因为工作器在 Laravel 执行完成后会停止。(还可以在监视程序停止重新启动队列时发出 artisan queue:restart 命令,以便所有作业都能得到优雅停止)。

// 最后,我们将检查是否超过了内存限制,或者基于其他指示队列是否应该重新启动。如果是这样,我们将停止
// 这个工作器,并让“监控”它的东西重新启动进程。

那调度器呢?我如何阻止(在 Laravel 内部)\App\Console\Kernel::schedule 函数在接收到 SIGTERM 信号后启动命令?

基本上,php artisan schedule:run 在接收到 SIGTERM 信号后应该什么都不做。

当 Docker 容器关闭时,它会收到这个信号。我想阻止在该容器上启动任何新的计划命令。这会在部署或缩减场景中发生。我不希望容器在进程中间被终止。

根据 https://stackoverflow.com/a/53733389/7309871(如果自 2018 年以来没有变化),在队列中运行命令可以解决这个问题,但会将调度器限制为仅处理作业。

由于 Laravel 使用 Symfony https://symfony.com/blog/new-in-symfony-5-2-console-signals

如果您更喜欢为所有应用程序命令处理某些信号(例如,记录或分析命令),请定义一个事件监听器或订阅者,并监听新的 ConsoleEvents::SIGNAL 事件。

但无论如何,这不能解决问题,因为:

这可能是内核的一个可能解决方案,但由于其执行窗口非常狭窄,并且因为每分钟都会执行此操作,例如,在第 30 秒发送的 SIGTERM 信号会被错过。一个更好的方法是在接收到 SIGTERM 信号后停止服务器端的 php artisan schedule:run 执行。

\App\Console\Kernel.php

/**
 * @inheritdoc
 */
public function __construct(Application $app, Dispatcher $events)
{
    parent::__construct($app, $events);
    $this->app->singleton(SignalSingleton::class);

    if (!\extension_loaded('pcntl')) {
        return;
    }

    \pcntl_async_signals(true);
    \pcntl_signal(SIGTERM, function (int $signo, mixed $siginfo): void {
        Log::info('SIGTERM received');
        \resolve(SignalSingleton::class)->shedulerIsEnabled = false;
    });
}

/**
 * Define the application's command schedule.
 */
protected function schedule(Schedule $schedule): void
{
    if (!\resolve(SignalSingleton::class)->shedulerIsEnabled) {
        return;
    }
...

SignalSingleton 类应只包含一个公共 bool $shedulerIsEnabled = true;

英文:

I saw that Laravel 8,9,10 handles the queue worker on SIGTERM signal. But according to the bellow comment from Worker class (Worker.php on line 185), laravel handles it only for the current worker execution, if supervisor (or other monitoring tool used) is not altered to take into account that SIGTERM and to NOT start the worker again, then it has little effect because the worker is stopped from laravel after he finishes the execution.(also artisan queue:restart can be issued when the supervisor stops restarting the queue, so that all jobs are gracefully stopped).

        // Finally, we will check to see if we have exceeded our memory limits or if
        // the queue should restart based on other indications. If so, we'll stop
        // this worker and let whatever is "monitoring" it restart the process.

How about the scheduler? How can I prevent (from within Laravel) the \App\Console\Kernel::schedule function to start the commands if a SIGTERM signal has been received?

Basically

php artisan schedule:run

should do nothing after SIGTERM signal.

When the docker container will be shut down, it receives this signal. I want to stop any new scheduled comands on that container from starting. This happens on a deploy or on a scale down scenario. I don't want the container to be killed in the middle of a process.

Acc. to https://stackoverflow.com/a/53733389/7309871 (if nothing changed since 2018) running the comands in a queue would solve this problem but restricts the scheduler to jobs only.

Since Laravel uses symfony https://symfony.com/blog/new-in-symfony-5-2-console-signals

> If you prefer to handle some signals for all application commands
> (e.g. to log or profile commands), define an event listener or
> subscriber and listen to the new ConsoleEvents::SIGNAL event.

But this can't handle it anyway because:

This could be a possible solution for the kernel but as its window of execution if very narrow and because this is executed each minute missing the SIGTERM sent at second 30 for example, a better way would be to stop execution of php artisan schedule:run server side after SIGTERM signal is received.

\App\Console\Kernel.php

/**
 * @inheritdoc
 */
public function __construct(Application $app, Dispatcher $events)
{
    parent::__construct($app, $events);
    $this->app->singleton(SignalSingleton::class);

    if (!\extension_loaded('pcntl')) {
        return;
    }

    \pcntl_async_signals(true);
    \pcntl_signal(SIGTERM, function (int $signo, mixed $siginfo): void {
        Log::info('SIGTERM received');
        \resolve(SignalSingleton::class)->shedulerIsEnabled = false;
    });
}

/**
 * Define the application's command schedule.
 */
protected function schedule(Schedule $schedule): void
{
    if (!\resolve(SignalSingleton::class)->shedulerIsEnabled) {
        return;
    }
...

The SignalSingleton class should contain only a public bool $shedulerIsEnabled = true;

答案1

得分: 1

大多数人在supervisord设置中运行他们的队列工作进程,但这有一些缺点,比如不支持延迟启动(除非您添加一个带有睡眠的脚本),并且无法处理致命崩溃,这意味着队列工作进程会崩溃,而监视程序不会重新启动它(有没有看到队列堆积的情况? How to handle SIGTERM signal on laravel 8,9,10 deploy with docker containers in regards to scheduler and worker?

我刚刚开始使用的解决方案是,在php-fpm docker容器的CMD下运行下面的脚本。

好处:

  • 无需将supervisord添加到php-fpm容器中(节省了大量资源)
  • 队列在失败时一定会重新启动
  • 可以通过捕获信号来处理自动缩放事件中的优雅关闭
  • 它简单而高效。
英文:

Most people run their queue workers in supervisord setups, which has some downfalls like not supporting delayed startups (unless you add a script w/ a sleep), and is unable to handle FATAL crashes, which means the queue worker will crash and supervisor won't restart it (ever seen your queues fill up? How to handle SIGTERM signal on laravel 8,9,10 deploy with docker containers in regards to scheduler and worker?

There's a solution I just started using, running the below script as the CMD of a php-fpm docker-container.

benefits:

  • no need to add supervisord to the php-fpm container (saves lots of resources)
  • queues will for sure be restarted on failure
  • can handle graceful shutdown during auto-scaling events by trapping signals
  • it's simple and efficient
#!/usr/bin/env bash

work=true

function stopQueues() {
    echo "Stopping queues..."
    work=false
    php artisan queue:restart
    sleep 300
    exit 0
}

# handle graceful shutdown
trap "" SIGPIPE
trap stopQueues SIGTERM SIGINT SIGHUP

echo "Starting PHP queue workers..."
# SQS_QUEUE (bg)
(while $work; do
    php artisan queue:work --queue="$(grep -oP 'SQS_QUEUE=\K.*' .env)" --no-interaction
    sleep 1
done &)

# SQS_FALLBACK_QUEUE (bg)
(while $work; do
    php artisan queue:work --queue="$(grep -oP 'SQS_FALLBACK_QUEUE=\K.*' .env)" --no-interaction
    sleep 1
done &)

# SQS_NOTIFICATION_QUEUE (fg) blocking
while $work; do
    php artisan queue:work --queue="$(grep -oP 'SQS_NOTIFICATION_QUEUE=\K.*' .env)" --no-interaction
    sleep 1
done

答案2

得分: 0

经过几天的研究,结果表明这不是一个PHP/Laravel的工作,而是一个DevOps工作。
因此,当要关闭一个容器时,需要使用SIGTERM信号,该信号必须被捕获(不是在PHP中),然后立即执行以下操作:

  1. 停止负载均衡器将新的HTTP请求发送到目标容器。
  2. 停止监督程序或任何其他用于重新启动工作进程的工具。
  3. 运行php artisan queue:restart,它将优雅地停止所有工作进程。
  4. 通过不再每60秒调用php artisan schedule:run来停止或阻止调度程序运行。
  5. 当来自负载均衡器、调度程序和工作进程(队列)的所有启动进程都完成时,终止容器。

这将防止任何进程被终止。

更新:Leonardo的答案涵盖了停止工作进程。我猜可以以类似的方式创建一个用于调度程序的方法。https://stackoverflow.com/a/76488746/7309871

英文:

After some days of research it turns our this is not a php/laravel job but rather a devops one.
So when a container is to be shut down using a SIGTERM signal, the signal must be caught (not in php) and immediately after that:

  1. Stop all new http requests from the load balancer to the target container.
  2. The supervisor or any other tool that is used should be stopped from restarting the workers.
  3. Run php artisan queue:restart that will gracefully stop all workers
  4. Stop or prevent the scheduler from running by not calling anymore: php artisan schedule:run every 60 seconds
  5. When all started processes from the load balancer, scheduler and worker(queue) are finished, kill the container.

This will prevent any processes from being killed.

UPDATE Leonardo's answer covers stopping the workers. I guess it can be created in an analog way for scheduler. https://stackoverflow.com/a/76488746/7309871

huangapple
  • 本文由 发表于 2023年6月13日 15:19:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76462520.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定