Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

ダウンタイム発生時にLaravelの長時間実行Jobを安全に制御する手順

概要

こんにちは、サーバーサイドエンジニアの佐野きよです。

システムの安定運用のために、データベースのメンテナンスやバージョンアップは欠かせません。しかし、これらの作業には数分から数十分程度のダウンタイムが伴うことがあります。

私たちのシステムでは、Laravelのキューワーカーを利用して非同期処理を実行しています。特に、数十分かかるような長時間実行されるバックグラウンド処理(Job)がデータベースのダウンタイムと重なってしまうと、処理が中断されてデータの不整合を引き起こしたりするリスクがあります。

この記事では、そのような事態を未然に防ぐため、データベースのダウンタイムが発生する際に、Laravelの長時間実行Jobを安全に停止&再開するための具体的な手順について解説します。

前提

この記事で紹介する手順は、以下の環境を前提としています。

  • インフラ: AWS EC2インスタンス上でキューワーカープロセスが稼働している。

  • プロセス管理: Supervisorを使い、php artisan queue:work コマンドでキューワーカープロセスを常駐させている。

  • キュー: Laravelの QUEUE_DRIVER に はdatabase を利用している。弊社では default キューと batch キューの2種類が存在しており、defaultキューは短時間用のキュー、batchキューは長時間実行用のキューとして運用している。

  • 状況: データベースのメンテナンスにより、数分程度のダウンタイムが計画されている。

停止手順

それでは、具体的な停止手順を解説します。 今回の手順では、即時性が求められる default キューは停止せず、長時間実行される batch キューのみを計画的に停止します。これにより、ユーザー影響を最小限に抑えつつ、システムの整合性を保ちます。

手順は大きく分けて「通常の停止手順」と、予期せず停止に時間がかかってしまいメンテナンス時間内に終わらなかったときを想定した「強制的な停止手順」の2段階で説明します。


batchキューワーカーの通常停止手順

まずは、基本的な停止手順です。

1. サーバーへの接続と準備

作業対象のサーバーにSSHで接続し、作業用のユーザーに切り替えます。また、途中でSSHセッションが切れても作業が中断しないように、screen コマンドで仮想セッションを開始します。screenコマンドであればだいたい標準で入っているはずなので手軽に利用することが可能です。

# batchサーバーへsshで入る
# (中略)

# ec2-userへ切り替え
sudo su -l ec2-user

# screenでセッションを開始
screen -S hoge_session

もし途中でセッションが切れてしまった場合は、以下のコマンドでセッションに再接続できます。

# screenセッションの一覧を確認
screen -ls

# 切断されたセッションに再接続
screen -d -r hoge_session

次に、アプリケーションのルートディレクトリに移動します。

cd /path/to/laravel-project

2. 事前確認

停止作業に入る前に、キューが正常に機能しているか、そして現在どのワーカーが稼働しているかを確認します。

Jobの動作確認

事前につくって置いた簡単なテストJobを投入し、default キューが正常に処理されることをログで確認します。

# 確認用のJobを投入
php artisan job:ping-query
batchキューワーカーの稼働状況確認

supervisorctlコマンドを使い、batch キューワーカーが RUNNING 状態であることを確認します。

sudo /usr/local/bin/supervisorctl status

以下のように、laravel-worker-batch で始まるプロセスが RUNNING と表示されていれば正常です。 laravel-worker-batch_xxとあるのはSupervisorの設定ファイルにあるキューワーカーのグループ名です。

laravel-worker-batch:laravel-worker-batch_00       RUNNING   pid 10336, uptime 0:00:52
laravel-worker-batch:laravel-worker-batch_01       RUNNING   pid 10345, uptime 0:00:49
laravel-worker-batch:laravel-worker-batch_02       RUNNING   pid 10353, uptime 0:00:48
laravel-worker-batch:laravel-worker-batch_03       RUNNING   pid 10328, uptime 0:00:53
laravel-worker-batch:laravel-worker-batch_04       RUNNING   pid 10320, uptime 0:00:54
laravel-worker-batch:laravel-worker-batch_05       RUNNING   pid 10378, uptime 0:00:45
laravel-worker-batch:laravel-worker-batch_06       RUNNING   pid 10362, uptime 0:00:47
laravel-worker-batch:laravel-worker-batch_07       RUNNING   pid 10370, uptime 0:00:46

3. batchキューワーカーの停止

supervisorctlコマンドでワーカーを停止します。

sudo /usr/local/bin/supervisorctl stop laravel-worker-batch:*

この stop コマンドは、現在実行中のJobがある場合、そのJobの処理が完了するのを待ってからワーカープロセスを停止します。また、キューに待機中のJobがあっても、新しいJobは実行せずに安全に停止できます。停止中にJobがエンキューされた場合、そのJobはキューワーカーを再起動したときに拾われます。

4. 停止状態の確認

最後に、batch キューワーカーが完全に停止したことを確認します。

Supervisorでの確認
sudo /usr/local/bin/supervisorctl status

以下のようにlaravel-worker-batch のステータスが 全てSTOPPED に変わっていればOKです。

laravel-worker-batch:laravel-worker-batch_00       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_01       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_02       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_03       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_04       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_05       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_06       STOPPED   Jun 23 06:11 PM
laravel-worker-batch:laravel-worker-batch_07       STOPPED   Jun 23 06:11 PM
psコマンドでの確認

念のため、ps コマンドでもプロセスが残っていないか簡単に確認します。

ps -aux | grep php

default キューのプロセスは残っていますが、batch キューを処理するプロセスが表示されなければ成功です。

apache    7344  0.0  4.0 515448 161468 ?       S    Jun20   1:45 php /var/local/crm-backend/current/artisan queue:work --sleep=5 --tries=3
apache    7377  0.0  4.0 515420 161388 ?       S    Jun20   0:50 php /var/local/crm-backend/current/artisan queue:work --queue=default4 --sleep=10 --tries=3
apache    7379  0.0  4.0 515332 161452 ?       S    Jun20   0:50 php /var/local/crm-backend/current/artisan queue:work --queue=default7 --sleep=10 --tries=3
... (defaultキューワーカーは表示される) ...
ec2-user 11504  0.0  0.0 119424   916 pts/0    S+   18:12   0:00 grep --color=auto php

これで、安全なワーカーの停止は完了です。


batchキューワーカーを正常に停止できない場合の対処法

supervisorctl stop コマンドは安全にJobを停止できるので非常に便利ですが、逆に言うと長時間実行Jobがいつまでも終わらなかった場合、完了までずっと待たされることになります。メンテナンス時間は限られた時間の中で行うため、Jobが終わらないことでメンテナンス枠を超えてしまうリスクがあります。そういった事態に備えて、状況によっては強制的にキューワーカーを停止する必要もあるかと思いますので、こちらについても紹介します。(この場合データ不整合のリスクと、メンテナンス枠を超えるリスクを天秤にかけて判断する必要あり)

1. 実行中Jobのデータを手動で退避

まず、ワーカーが掴んでしまっているJobを、後で再実行できるように jobs テーブルから failed_jobs テーブルへ手動で移動させます。これによりキューの損失を防ぎます。

-- トランザクションを開始
START TRANSACTION;

-- ステップ1: jobsテーブルから対象JobをSELECTし、failed_jobsテーブルにINSERT
INSERT INTO failed_jobs (
    uuid,
    connection,
    queue,
    payload,
    exception,
    failed_at
)
SELECT
    -- jobsテーブルのpayloadからuuidを特定して指定
    'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    'database',
    -- jobsテーブルのqueueカラムの値
    queue,
    -- jobsテーブルのpayloadカラムの値
    payload,
    -- 失敗理由(手動移動の記録)
    'MySQLバージョンアップ時に強制的にJobを停止させたため手動でfailed_jobsテーブルへ移動',
    -- 日本時間(JST)で記録
    ADDTIME(UTC_TIMESTAMP(), '09:00:00')
FROM
    jobs
WHERE
    -- 移動対象のJobのIDを指定
    id = 12345;

-- ステップ2: 元のjobsテーブルから対象レコードを削除
DELETE FROM jobs WHERE id = 12345;

内容に問題がなければ、変更を確定します。

-- 変更をコミット
COMMIT;

もし間違いがあった場合は、ROLLBACK; で処理を取り消してください。

2. 停止コマンドの強制終了

supervisorctl stop コマンドが実行中のままになっているはずなので、これは普通にCtrl + C を押してこのコマンドを強制的に中断します。

3. 不整合プロセスの特定と再起動

supervisorctl stop コマンドを2で強制終了したことで、一部のワーカープロセスが STOPPING という不整合な状態に陥ってしまいます。この状態になっているワーカーは、supervisorctlコマンドによるプロセスの再起動や停止、再開などをしたときにすべてエラーになってしまいます。これを解消します。

STOPPING 状態のプロセスを特定

supervisorctl status を実行し、STOPPING になっているプロセスを探します。

sudo /usr/local/bin/supervisorctl status

以下だと、batch_00batch_07STOPPING 状態になっていることがわかります。これは終わらないJobを掴みつづけていたワーカープロセスです。

laravel-worker-batch:laravel-worker-batch_00       STOPPING
laravel-worker-batch:laravel-worker-batch_01       RUNNING   pid 19886, uptime 0:00:28
...
laravel-worker-batch:laravel-worker-batch_07       STOPPING
...
プロセスIDを特定して強制終了 (KILL)

ps コマンドで STOPPING 状態のワーカーのプロセスID(PID)を特定し、kill -9 コマンドで強制終了します。 プロセスをKILLしたあとstatusを確認すると、すべてのbatchキューワーカーがSTTOPEDな状態になるはずです。

# 例: queue=batch0 と queue=batch7 のプロセスを探す
ps -aux | grep -E "queue=batch(0|7)"

# 特定したプロセスIDを強制終了
sudo kill -9 12345 67890
最終確認

再度ステータスを確認し、すべての batch キューワーカーが STOPPED になったことを確認します。

sudo /usr/local/bin/supervisorctl status

STOPPING だったプロセスが STOPPED に変わっていれば、OKです。

laravel-worker-batch:laravel-worker-batch_00       STOPPED   Jun 24 05:37 PM
laravel-worker-batch:laravel-worker-batch_01       STOPPED   Jun 24 05:37 PM
...
laravel-worker-batch:laravel-worker-batch_07       STOPPED   Jun 24 05:37 PM
...

まとめ

今回は、データベースのダウンタイムに備えて、Laravelの長時間実行Jobを安全に停止する手順を解説しました。

計画メンテナンス時にこの記事の手順を活用することで、データ不整合などのリスクを回避し、システムの安定性を高めることができます。特に、正常に停止しない場合の対処法を知っておくことは、いざという時の大きな助けになるはずです。

メンテナンス完了後、sudo /usr/local/bin/supervisorctl start laravel-worker-batch:* コマンドでワーカーを再開するのを忘れないようにしましょう。

この記事が、同じような課題を持つ方々のお役に立てば幸いです。

さいごに

ヤプリではサーバーサイドエンジニアを随時募集しています! 興味を持った方、是非一度カジュアル面談を受けてみませんか…??

open.talentio.com

最後まで読んでいただきありがとうございました!