概要
こんにちは、サーバーサイドエンジニアの佐野きよです。
システムの安定運用のために、データベースのメンテナンスやバージョンアップは欠かせません。しかし、これらの作業には数分から数十分程度のダウンタイムが伴うことがあります。
私たちのシステムでは、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_00
と batch_07
が STOPPING
状態になっていることがわかります。これは終わらない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:*
コマンドでワーカーを再開するのを忘れないようにしましょう。
この記事が、同じような課題を持つ方々のお役に立てば幸いです。
さいごに
ヤプリではサーバーサイドエンジニアを随時募集しています! 興味を持った方、是非一度カジュアル面談を受けてみませんか…??
最後まで読んでいただきありがとうございました!