Laravel Horizonでジョブキュー監視・運用を強化する:導入から本番運用・障害対応まで
- 作成日 2026.03.06
- その他
Laravel Horizonは、Redisベースのキューを「見える化」し、ワーカーのプロセス管理・スループット監視・失敗ジョブの追跡を一つにまとめる運用基盤。キュー処理が増えるほど、ログだけでは追いづらい「滞留」「再試行の嵐」「特定キューだけ遅い」「ワーカーが落ちている」などが発生しやすい。Horizonを入れると、待ち行列の状態・実行時間・失敗の傾向・プロセス構成を短時間で判断でき、復旧手順も定型化しやすくなる。
- 1. Horizonの前提:Redisキューが必須
- 2. インストールと初期セットアップ(最短手順)
- 3. 基本の起動:ローカルで動作確認する
- 4. キュー・接続・リトライの基本:Horizonに載せる前に整える
- 5. horizon.phpの読み方:supervisor と balancing を理解する
- 6. 環境別設定:local/staging/production でプロセス数を分ける
- 7. ダッシュボードの見どころ:滞留・失敗・実行時間を短時間で判断する
- 8. アクセス制御:/horizon を本番で安全に公開する
- 9. 本番常駐:Supervisor(systemd)でHorizonをプロセス管理する
- 10. デプロイ手順:設定変更を反映し、古いワーカーを安全に落とす
- 11. 失敗ジョブの扱い:原因特定→再実行→再発防止をループで回す
- 12. よくあるエラーと発生条件:Horizonが動かない/見えない/処理されない
- 13. 実務で効く設計:キュー分割・優先度・重い処理の隔離
- 14. サンプル:ジョブ投入→キュー分割→監視の流れ
- 15. まとめ:Horizonを運用に組み込むための要点
Horizonの前提:Redisキューが必須
Horizonは Redis ドライバ向けの監視・管理ツール。database/sqs 等を使っている場合、そのままではHorizonの恩恵が出ない。まずはキュー接続をRedisに寄せる。
# .env(例)
QUEUE_CONNECTION=redis
REDIS_CLIENT=phpredis発生条件(導入時につまずきやすい例):
・QUEUE_CONNECTION が database のまま → Horizon画面は出ても期待した監視にならない
・Redis拡張が無い/接続できない → ワーカーが起動しない、処理が流れない
インストールと初期セットアップ(最短手順)
基本は composer で追加し、設定ファイルを公開して管理する。
composer require laravel/horizon
php artisan horizon:install
php artisan migrate※ migrate は Horizon用テーブルが必要な構成(例:failed_jobs等)を含む場合があるため、プロジェクトの状態に合わせて実行。
基本の起動:ローカルで動作確認する
まずはローカルで「ジョブを投げる→処理される→ダッシュボードで見える」を通す。
# Horizon起動(開発)
php artisan horizonブラウザでは通常 /horizon にアクセス(ルーティングはHorizonが提供)。
キュー・接続・リトライの基本:Horizonに載せる前に整える
ジョブの滞留や失敗は「timeout」「tries」「バックオフ」「例外設計」でかなり変わる。Horizonは監視・管理であって、ジョブ設計が弱いと結局荒れる。
// 例:ジョブ側でリトライ/タイムアウトを制御
class ImportUsers implements ShouldQueue
{
public int $tries = 3;
public int $timeout = 120;
public function backoff(): array
{
return [10, 30, 60];
}
public function handle(): void
{
// 処理
}
}発生条件(失敗が増えやすい例):
・外部API呼び出しをタイムアウト無しで実行 → ワーカー占有→滞留
・リトライ間隔が短すぎる → 失敗の連打でスループット低下
horizon.phpの読み方:supervisor と balancing を理解する
config/horizon.php は Horizonの心臓部。基本は「どのキューを」「何プロセスで」「どの戦略で」回すかを決める。
# 設定を公開済みの場合
# config/horizon.php を編集重要ポイント:
・supervisor:ワーカープロセス群の定義(キュー、プロセス数、メモリ制限など)
・balancing:キューの負荷に応じてプロセスを配分する考え方(環境による)
・timeout / tries / sleep:ワーカー側の動作パラメータ
環境別設定:local/staging/production でプロセス数を分ける
開発は少数、ステージングは検証用、本番は負荷に合わせて増やす。環境別に supervisor 設定を分けておくと、デプロイ後の調整が速い。
// config/horizon.php(概念例)
'environments' => [
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'simple',
'processes' => 1,
'tries' => 1,
],
],
'production' => [
'supervisor-default' => [
'connection' => 'redis',
'queue' => ['default', 'emails'],
'balance' => 'auto',
'processes' => 10,
'tries' => 3,
'timeout' => 120,
],
'supervisor-long' => [
'connection' => 'redis',
'queue' => ['long'],
'balance' => 'simple',
'processes' => 3,
'tries' => 2,
'timeout' => 600,
],
],
],運用上のコツ:
・重いジョブ用キュー(long等)を分け、defaultの遅延を防ぐ
・emails/notifications なども分けると遅延切り分けが楽
ダッシュボードの見どころ:滞留・失敗・実行時間を短時間で判断する
Horizonでまず見るべきもの:
・Queues:キューごとの待ち数(Backlog)と処理速度
・Jobs:どの種類のジョブが多いか、どれが遅いか
・Failed Jobs:失敗が特定ジョブに偏っていないか
・Supervisors/Workers:プロセスが落ちていないか、メモリが伸びていないか
監視の軸を「キュー」「ジョブ種別」「プロセス」に分けておくと原因特定が速い。
アクセス制御:/horizon を本番で安全に公開する
Horizonは運用情報の塊なので、必ずアクセス制限する。基本は環境で切り替え、認証済みユーザーや特定IPのみ許可に寄せる。
// app/Providers/HorizonServiceProvider.php(概念例)
use Laravel\Horizon\Horizon;
public function boot(): void
{
Horizon::auth(function ($request) {
// 例:本番は管理者のみ
return auth()->check() && auth()->user()->is_admin;
});
}発生条件(セキュリティ事故):
・/horizon を無制限公開 → キュー状況や内部情報が漏れる
本番常駐:Supervisor(systemd)でHorizonをプロセス管理する
本番では php artisan horizon を手動実行しない。プロセスマネージャで自動起動・自動再起動にする。
; /etc/supervisor/conf.d/horizon.conf(例)
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/horizon.log
stopwaitsecs=3600デプロイ時は、コード更新後に Horizon を適切に再読み込みする(後述)。
デプロイ手順:設定変更を反映し、古いワーカーを安全に落とす
Horizonはプロセスを抱えるため、設定変更やコード更新を「安全に反映」する手順が必要。
# Horizonに再起動指示(ワーカーを段階的に入れ替える)
php artisan horizon:terminate発生条件(デプロイ後に古いコードが動き続ける):
・terminate を打たず、常駐プロセスが旧コードのまま処理を継続
・config:cache を更新したのに supervisor 設定が反映されない
失敗ジョブの扱い:原因特定→再実行→再発防止をループで回す
失敗ジョブは「再試行」だけでは収束しない。
・例外の種類(認証切れ、外部API障害、データ不整合)を分類
・再試行して良い失敗/ダメな失敗を分ける
・冪等性(同じジョブが2回走っても壊れない)を担保する
・失敗を通知(Slack/メール)し、一定閾値で止血できるようにする
# failed_jobs の再実行(標準の仕組みを使う例)
php artisan queue:retry all
よくあるエラーと発生条件:Horizonが動かない/見えない/処理されない
・Horizonの画面は出るが処理されない
発生条件:ワーカー未起動、QUEUE_CONNECTIONがredisではない、supervisor設定ミス
・Redis接続エラー(Connection refused / timed out)
発生条件:REDIS_HOST/PORT/認証不整合、ネットワーク、Redis停止、TLS要否ズレ
・ジョブが途中で落ちる(timeout / memory)
発生条件:timeoutが短い、外部I/Oが遅い、巨大データをメモリに載せる
・失敗が連発してキューが増え続ける
発生条件:外部API障害、レート制限、データ不整合、リトライ間隔が短すぎる
・特定キューだけ詰まる
発生条件:重いジョブと軽いジョブを同じキューに混在、プロセス配分が不足
実務で効く設計:キュー分割・優先度・重い処理の隔離
おすすめの切り方:
・default:軽量(通知、更新、短い計算)
・emails:メール送信
・long:重い集計、外部連携、ファイル生成
・critical:即時性が高い処理(ただし濫用しない)
キューを分けるだけで「重いジョブが全体を止める」を回避でき、Horizonの監視も読みやすくなる。
サンプル:ジョブ投入→キュー分割→監視の流れ
コントローラ等からジョブを投入し、キュー名を明示する。
use App\Jobs\ImportUsers;
ImportUsers::dispatch($tenantId)->onQueue('long');
use App\Jobs\SendInvoiceMail;
SendInvoiceMail::dispatch($invoiceId)->onQueue('emails');これにより、Horizon上で long と emails の滞留や失敗を分離して追える。
まとめ:Horizonを運用に組み込むための要点
・Redisキュー前提を満たし、接続設定を固定する
・supervisorでキューとプロセス設計を分け、重い処理を隔離する
・/horizon は必ずアクセス制御し、本番はプロセスマネージャで常駐させる
・デプロイ時は horizon:terminate を手順化し、旧コード残留を防ぐ
・失敗ジョブは分類・冪等性・再試行設計までセットで回し、再発を潰す
-
前の記事
Laravel『TokenMismatchException』の原因と対処法 2026.03.05
-
次の記事
記事がありません
コメントを書く