今回は本番環境(prdサーバー)に自動処理の実行環境を設定しました。
- 指定した時刻に記事を公開するための「予約公開」の仕組み
- 画面操作とは別に処理を進める「AI審査」に対して採用
⭐︎この記事でわかること⭐︎
1:自動処理を組むとは
2:仕組みとメリット、デメリット
3:自動処理を支える5つの仕組み
4:今回組んだ自動処理とその採用理由
今回使用したのは
cron Laravelスケジューラ キュー Supervisor ワーカー
この5つを活用しています。
1:自動処理を組むとは
ここでいう「自動処理を組む」とは、
人がその都度コマンドを実行しなくても、
必要なタイミングで処理が動く環境を整えること
今回行ったのは、予約公開やAI審査そのものの新規実装ではなく、
アプリに実装済みの処理を自動で動かすためのサーバー設定です。
例えばこんなのがある
「決まったタイミングで繰り返す仕事」や「画面で完了を待たせたくない仕事」が候補になります。
| 機能 | 向いている仕組み | 理由 |
|---|---|---|
| 記事の予約公開 | 定期実行 | 公開時刻に到達した記事を繰り返し確認できる |
| 古い一時ファイルの削除 | 定期実行 | 毎日など、一定間隔で実行できる |
| 定期集計・レポート作成 | 定期実行+必要ならキュー | 作成時刻の管理と重い処理の分離ができる |
| メール通知 | 非同期処理 | 通信待ちを画面操作から分離できる |
| 画像変換・CSV出力 | 非同期処理 | 時間のかかる処理を後から実行できる |
| AI審査・外部API連携 | 非同期処理 | 外部サービスの応答待ちを分離できる |
2:仕組みとメリットデメリット
「定期実行」と「非同期処理」を分けて考える
自動処理を理解するうえでは、次の2つを分けると整理しやすくなりました。
- 定期実行:指定した時刻や間隔で処理を動かす。例:予約公開
-
非同期処理:その場で処理完了を待たず、
仕事を登録して別のプロセスで実行する。例:AI審査、ファイル生成
また、この2つは組み合わせたりもできるので用途に応じて導入しましょう。
メリット
- 手動実行の手間や、実行し忘れを減らせる
- 定期的に確認が必要な処理を、自動で呼び出せる
- 時間のかかる仕事を画面操作から分離できる
- ワーカーが終了した場合も、Supervisorで再起動できる
デメリット・注意点
- 画面から見えないところで動くため、ログや失敗の監視が必要
- 非同期処理は、依頼した直後に結果が出るとは限らない。画面にも「処理中」などの状態が必要になる
- 再試行で同じ仕事が複数回実行される可能性がある。二重投稿などを防ぐ設計も必要
- ワーカーを増やすとDBや外部APIへの負荷も増える
- 常駐ワーカーは読み込んだコードを保持するため、デプロイ後の再起動を考える必要がある
何でもかんでも自動化したら良い、というわけではなさそうです。
3:自動処理を支える5つの仕組み
| 名前 | たとえるなら | 役割 |
|---|---|---|
| cron | 時計係 | 指定した時刻・間隔でコマンドを起動する |
| Laravelスケジューラ | 予定表係 | 登録された予定から、今実行する定期処理を選ぶ |
| キュー | 仕事の受付 | 実行待ちの仕事を保存する |
| ワーカー | 作業員 | キューから仕事を取り出して実行する |
| Supervisor | 現場監督 | ワーカーを起動・監視し、終了したら再起動する |
cron:時間管理のプロ

Unix系オペレーティングシステムのジョブ管理ツール
cronジョブ(コマンドまたはシェルスクリプト)を登録すると、
指定した日時または間隔でジョブを実行してくれます
cronは、主にシステムの管理・維持を自動化する目的で使用されるが、
ファイルを定期的にダウンロードするコマンドを
起動するなどの用途にも応用できるみたいで
めっちゃ優秀です。
Laravelスケジューラ:予定管理係

Laravelのコードで「どの処理を、いつ実行するか」を管理する仕組みです。
「毎分」「毎日」「毎週」などの実行予定を登録できます。
cronから呼び出されると、登録された予定を確認し、
その時点で実行対象になっている処理を選んで実行します。
処理ごとの予定をLaravel側にまとめられるので、アプリのコードとして管理しやすいのが
便利なところです。
今回の予約公開では、スケジューラが公開用の
コマンドを呼び出し、
その処理が公開時刻を過ぎた記事を確認します。
スケジューラ自身が記事の内容まで判断しているわけではありません。
キュー:仕事の受付係
後から実行する仕事を保存しておく、待ち行列の仕組みです。
この仕事の単位を、Laravelでは「ジョブ」と呼びます。
例えば、画面からAI審査を依頼したときに、その依頼をジョブとして登録しておきます。
キューは依頼を預かる場所なので、キューだけ用意しても、仕事は実行されません。
今回はDBにジョブを保存する方式を使いました。
通常処理用とAI審査用でキューを分け、それぞれの担当ワーカーが仕事を取り出す構成です。
ワーカー:従順な作業員
キューに登録された仕事を取り出し、実際に処理するプロセスです。
キューが受付なら、ワーカーは依頼票を受け取って動く作業員にあたります。
Laravelでは、queue:work コマンドなどでワーカーを起動します。
起動したワーカーは担当するキューを確認し、仕事があれば処理を進めます。
今回は、通常処理用とAI審査用にワーカーを1つずつ用意しました。
担当を分けることで、AIの応答を待っている間も、
通常処理を進められるようにしています。
スーパーバイザー:現場監督
Supervisorは、プロセスを起動・監視するためのツールです。
今回はワーカーを常駐させ、終了した場合に自動で起動し直す役割を任せました。
手動でワーカーを起動するだけでは、終了したときに気づかず、
仕事がキューにたまってしまう可能性があります。
そこで、ワーカーを動かし続けるための管理をSupervisorに任せる構成にしました。
ただし、Supervisorが監視するのはプロセスの状態です。
ワーカーが動いていても個々のジョブは失敗することがあるため、
処理結果やエラーログは別に確認が必要です。
4:今回採用した自動処理、採用理由
予約公開などの定期処理:cron+Laravelスケジューラ
今回は、cronから毎分 schedule:run を起動する構成にしました。
役割を会話にすると、こんなイメージです。
cron:「1分経ったよ! 今、実行する予定はある?」
Laravelスケジューラ:「予定を確認するね。予約公開の処理を動かす時間だ!」
予約公開処理:「公開時刻を過ぎた記事があるか確認して、対象の記事を公開するよ!」
cronには定期的に呼び出す役割を任せ、どの処理を
いつ動かすかはLaravel側で管理します。
処理ごとにcronを増やすよりも、
アプリのコードで予定を追いやすくするためです。
ただし、確認は毎分です。
「予約時刻になった瞬間、秒単位で必ず公開される」
という構成ではないのでアプリとの整合性確認は
しっかりした上で実装しましょう。

通常処理とAI審査:キュー+ワーカー
DBキューを使い、通常処理用と
AI審査用にワーカーを1つずつ用意しました。
アプリ:「AI審査をお願いしたいので、仕事を登録します!」
キュー:「依頼を預かったよ。担当のワーカーが取り出すまで保存しておくね!」
AI審査ワーカー:「審査の仕事を取り出したよ。AIに問い合わせて、結果を反映します!」
通常ワーカー:「その間も、こちらは通常の仕事を進めるよ!」
担当を分けることで、AIの応答待ちで通常の仕事まで
詰まるのを避けています。
キューは保存先、ワーカーは実行担当という違いが
あります。
また、すべての処理がキューを通るわけではありません。
今回の予約公開では、次のようにつながっています。
予約公開処理:「記事を公開したよ。必要なSNS投稿はキューに登録しよう!」
通常ワーカー:「登録されたSNS投稿の仕事は、こちらで取り出して処理するね!」
ワーカーの常駐管理:Supervisor
ワーカーを毎回手動で起動する運用にせず、
Supervisorに起動・監視・再起動を任せました。
Supervisor:「ワーカーを起動します。動いているかも見守るよ!」
ワーカー:「起動しました! キューの仕事を確認します!」
Supervisor:「ワーカーが終了したことを検知したので、起動し直します!」
これで、ワーカーが終了した場合にも自動で再起動できます。
でもSupervisorが見ているのは「プロセスの状態」
なので注意です。
ワーカー:「プロセスは動いているけれど、この仕事はエラーになりました……」
運用担当:「それは処理結果やエラーログを確認する必要があるわね!」
ワーカーが動いていることと、すべての仕事が成功して
いることは別なので、ジョブの失敗確認は引き続き必要になります。
参考:色々なプロセスの状態
| 表示例 | 意味 |
|---|---|
STARTING |
起動している途中 |
RUNNING |
起動して稼働中 |
STOPPED |
停止している |
EXITED |
実行が終了した |
FATAL |
起動を繰り返し試しても成功しなかった |
設定の流れ(本番環境向けの架空例)
実際に設定・確認したのはSTGです。以下はその構成をもとに、本番環境向けの架空名で説明した例です。本番環境への適用実績を示すものではありません。
cronとSupervisorは導入・起動済みで、アプリのDBキューを利用できることを前提とします。
Supervisorの設定は、読み込み対象の /etc/supervisor/conf.d/example-worker.conf へ記述する想定です。
※このコマンド群は環境によって異なるため
基本的には全てexampleとしています。各々の環境に合わせ記述する必要があります
まずは本番環境に潜り込む
1:まずは本番環境に潜り込む
# 本番環境へSSH接続する(接続先は架空の例)
ssh prod.example.com
cron(時間管理のプロ)を設定
cronの設定ファイルを管理者権限で編集する
コード・設定例を表示
sudoedit /etc/cron.d/example-scheduler
cronがコマンド実行に使うシェル
コマンドを探すディレクトリの設定。
PHPの子プロセスなどにも使われる
コード・設定例を表示
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
* * * * * appuser cd /srv/example-app/current && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
ここむずいので下に書きます。
| 部分 | 意味 |
|---|---|
* * * * * |
毎分実行する |
appuser |
このユーザーの権限で実行する |
cd /srv/example-app/current |
アプリのフォルダへ移動する |
&& |
前のコマンドが成功したら、次を実行する |
/usr/bin/php |
使用するPHPの実行ファイル |
artisan schedule:run |
Laravelに実行予定を確認させる |
>> /dev/null 2>&1 |
通常出力とエラー出力を破棄する |
なぜ破棄する必要があるんです?
毎分の出力でcronの通知メールなどが大量に発生するのを避けるため、
捨てる設定にしている
ログを残すときはこんな感じに
『>> /書き込み可能なパス/scheduler.log 2>&1』
実際にはコマンドの末尾に付けます。実行ユーザーが
書き込める保存先を使い、ログの容量管理も必要です。
スーパーバイザーの設定
「この作業員を1人起動して、終了したら補充し、活動記録も残してね」
という設定をします。
コード・設定例を表示
; ── ① 何を動かすか ──
; Supervisor内で使う、このワーカーの名前
[program:example-worker]
; アプリのフォルダ
directory=/srv/example-app/current
; このコマンドでワーカーを起動する
command=/usr/bin/php /srv/example-app/current/artisan queue:work --sleep=3 --tries=3 --timeout=600 --max-time=3600
; 実行するユーザーと、起動する作業員の人数
user=appuser
numprocs=1
; ── ② どう管理するか ──
; Supervisorの起動時に、ワーカーも起動する
autostart=true
; 稼働後にワーカーが終了したら、起動し直す
autorestart=true
; 停止要求後、最大660秒は終了を待つ
stopwaitsecs=660
; 子プロセスもまとめて停止の対象にする
stopasgroup=true
killasgroup=true
; ── ③ どこに記録するか ──
; エラーも同じログへまとめる
redirect_stderr=true
; ログの保存先
stdout_logfile=/srv/example-app/storage/logs/worker.log
; 50MBごとにログを切り替え、過去5世代を保存する
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
| オプション | 意味 |
|---|---|
--sleep=3 |
仕事がなければ3秒待つ |
--tries=3 |
最大3回試す。Job側に指定があればそちらが優先 |
--timeout=600 |
1件の処理の制限時間。Job側の指定も確認 |
--max-time=3600 |
約1時間で、処理中の仕事が終わってからワーカーを終了する |
AI審査には専属の作業員を用意する
通常の仕事とAI審査を同じ作業員に任せると、AIの返事を待っている間、
通常の仕事が後ろで待つことがあります。
そこで、AI審査のキューだけを担当する作業員をもう1人用意しました。
通常ワーカー:「こちらは通常の仕事を進めます!」
AI審査ワーカー:「AIの返事待ちはこちらで引き受けます!」
設定ファイルは同じで大丈夫です。通常ワーカーの設定の下に、別の名前で追加します。
AI審査用ワーカーの設定例
; ── ① AI審査の仕事だけを担当する ──
[program:example-review-worker]
directory=/srv/example-app/current
; --queue=example-review:この名前のキューだけを確認する
; --timeout=180:処理時間の上限を180秒にする
command=/usr/bin/php /srv/example-app/current/artisan queue:work --queue=example-review --sleep=3 --tries=3 --timeout=180 --max-time=3600
; 実行ユーザーと人数は通常ワーカーと同じ
user=appuser
numprocs=1
; ── ② 起動・停止を管理する ──
autostart=true
autorestart=true
; 停止要求後は、最大240秒待ってから強制終了する
stopwaitsecs=240
stopasgroup=true
killasgroup=true
; ── ③ AI審査用のログへ記録する ──
redirect_stderr=true
stdout_logfile=/srv/example-app/storage/logs/example-review-worker.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
通常ワーカーとの違いは、この4つです。
| 変える場所 | どう変える? |
|---|---|
[program:...] |
別のワーカーとして管理できるよう、名前を変える |
command |
AI審査用キューを指定し、制限時間を180秒にする |
stopwaitsecs |
停止要求後に待つ時間を240秒にする |
stdout_logfile |
AI審査専用のログへ分ける |
「受付を分ける」だけでなく、その受付を担当する作業員も別に起動するのがポイントです。
ジョブを登録する側のキュー名も、example-review に合わせる必要があります。
同じ仕事を早く配り直さないようにする
ここで、もう1つ時間の設定が登場します。
キューには、作業員が途中で止まってしまっても仕事が
放置されないように、一定時間が過ぎたら、処理中の仕事を
再び取り出せるようにする仕組みがあります。
ただ、その時間が短すぎると……。
受付:「時間が過ぎたので、この依頼はもう一度取り出せます!」
作業員A:「待って! まだ処理してます!」
作業員B:「同じ依頼を受け取ってしまいました……」
これでは、同じ仕事を重複して実行する可能性があります。
そこで、処理を打ち切るまでの時間より、
再取得できるようになるまでの時間を長くします。
| 設定 | 今回の値 |
|---|---|
| 通常処理の制限時間 | 600秒 |
| AI審査の制限時間 | 180秒 |
| 処理中の仕事を再取得可能にするまでの時間 | 660秒 |
DBキューの環境設定例
# 処理中のジョブを、再び取り出せるようにするまでの秒数
# 実行周期や、失敗後の待ち時間(backoff)とは別の設定
DB_QUEUE_RETRY_AFTER=660
既存の環境設定の該当項目を変更します。ファイル全体を
この1行で上書きするわけではありません。
「毎回660秒待ってから仕事を始める」という意味ではないので、
ここは混乱しやすいところでした。
この設定は重複実行のリスクを抑えるもので、完全に防ぐ保証ではありません。
処理側でも、同じ仕事を再実行したときの対策が必要です。
書いた設定を読み込ませる
設定ファイルを書いて終わり、ではありません。
LaravelとSupervisor、それぞれに変更を反映します。
設定を反映するコマンド例
# ① アプリのフォルダへ移動
cd /srv/example-app/current
# ② Laravelの設定キャッシュを作り直す
sudo -u deployuser /usr/bin/php artisan config:cache
# ③ Supervisorに「設定が変わっていないか確認して」と伝える
sudo supervisorctl reread
# ④ 対象ワーカーに設定を適用する
# 追加したワーカーは起動し、設定が変わったワーカーは再起動する
sudo supervisorctl update example-worker example-review-worker
reread は変更の確認、update は変更の適用です。
なお、Laravelの環境設定だけを変えた場合、Supervisorの設定に
差分がなければ、update だけでは既存ワーカーは再起動しません。
すでに動いているワーカーへ新しい設定やコードを読み込ませる場合は、
次のように再起動を要求します。
処理を終えてからワーカーを再起動する
# 処理中の仕事が終わったら終了するよう、ワーカーへ要求する
sudo -u appuser /usr/bin/php \
/srv/example-app/current/artisan queue:restart
ワーカー:「今の仕事を終えたので、一度終了します!」
Supervisor:「終了を確認したので、新しく起動します!」
こうすることで、新しいコードや設定を読み込んだワーカーが起動します。
この要求はLaravelのキャッシュを使うため、コマンドとワーカーが
同じキャッシュを参照していることも必要です。
最後に、本当に動いているか確認する
まずは、作業員が出勤しているかを確認します。
ワーカーの状態を確認する
sudo supervisorctl status
表示イメージはこんな感じです。名前は公開用に置き換えています。
ワーカーの表示例
example-worker RUNNING
example-review-worker RUNNING
2つとも RUNNING なら、ワーカーは起動しています。
次に、時計係が毎分スケジューラを呼び出しているかを確認します。
cronの実行ログを確認する
# スケジューラに関するログの末尾6行を表示する
sudo grep 'artisan schedule:run' /var/log/cron | tail -6
cronログの表示例(省略)
03:00:01 CMD (... artisan schedule:run ...)
03:00:03 CMDEND (... artisan schedule:run ...)
03:01:01 CMD (... artisan schedule:run ...)
03:01:03 CMDEND (... artisan schedule:run ...)
| 表示 | 分かること |
|---|---|
RUNNING |
ワーカーが起動している |
CMD |
cronがコマンドを開始した |
CMDEND |
cronがコマンドを終了した |
監督:「作業員2人、出勤しています!」
時計係:「毎分、予定の確認を呼び出しています!」
運用担当:「では、記事が公開されたか、AI審査の結果が反映されたかも確認しましょう!」
「プロセスが動いた」と「機能が期待どおり動いた」は別
ということです。
実際の作業では、2026年11月6日に本番環境で、
ワーカー2つの稼働・cronの毎分実行・Webの正常応答まで
確認しました。
テスト記事の予約公開、AI審査結果の反映、実際のサーバー再起動後の
復帰は、その時点では未確認です。
上のログは当時の結果をもとにした省略例で、現在の稼働状況を
示すものではありません。
今回、設定しただけで満足せず、
「どこまで確認できたか」も分けて残す必要があると学びました。
まとめ
今回は、本番環境(prod)で、実装済みの予約公開や
AI審査を自動で動かすための設定を行いました。
役割を整理すると、次の5つになります。
- cron:指定した間隔でコマンドを起動する
- Laravelスケジューラ:今実行する定期処理を選ぶ
- キュー:実行待ちの仕事を保存する
- ワーカー:キューから仕事を取り出して実行する
- Supervisor:ワーカーの起動・監視・再起動を管理する
通常処理とAI審査のワーカーを分け、AIの応答待ちで通常処理が
詰まらない構成にしました。また、処理時間と再試行間隔を調整し、
同じ仕事が重複して実行されるリスクを抑えています。
今回学んだのは、自動処理は「勝手に動くようにする」だけでなく、
「失敗に気づき、確認できるようにする」ことも大切ということです。
ワーカーの稼働とcronの実行は確認できましたが、予約公開やAI審査の結果
まで正しいかは別の確認になります。
今後は、実際の機能を通した動作確認も進めていきたいと思います。







