夜中に誰も気づかないまま本番サーバーのメモリが限界に達している「静かな障害」。その原因と、Laravel/WordPress環境における具体的な監視設計を解説します。
「朝出社したらサイトが落ちていた」──その原因、夜中にありました
こんな経験はありませんか?
- 朝イチで「サイトが見られない」と連絡が来た
- ログを見ても、障害の直前に何が起きたかよくわからない
- 再起動すれば直るが、「なぜ落ちたか」が解明できないまま
- Mackerelや CloudWatchを入れているのに、なぜか気づけなかった
これは「サイレント障害」と呼ばれる現象の典型例です。派手なエラーログが残るわけでも、アラートが鳴るわけでもなく、じわじわとメモリが消費されていき、深夜0〜4時ごろにサーバーが静かに限界を超える──そういった障害が、LaravelやWordPressを運用している中小企業のサーバーで実際によく起きています。
この記事では、「なぜ夜中に急上昇するのか」という原因の深掘りから、実際に使えるアラート設計・監視コードまでを具体的に解説します。エンジニアや社内Web担当者の方が「明日から実装できる」レベルで書いていますので、ぜひ最後までお読みください。
なぜ「夜中」にメモリが急上昇するのか? 原因の正体
日中は安定しているのに、夜中だけ跳ね上がる──この挙動には、いくつかの典型的な原因があります。
1. cronバッチ処理によるメモリリーク
Laravelでよく見られるのが、app/Console/Kernel.php に定義されたスケジュールタスクが深夜に実行され、その中でメモリリークが発生するケースです。特に「大量レコードの一括処理」「外部APIの連続呼び出し」などは要注意です。
// 危険なパターン:全件取得してメモリが膨張する
$users = User::all(); // 数万件あるとそれだけでメモリを圧迫
foreach ($users as $user) {
$this->sendNotification($user);
}
この書き方は件数が少ない開発時には問題になりませんが、本番で10万件のデータが溜まったとき、突然崩壊します。
2. WordPressのwp-cronによる連鎖実行
WordPressには「疑似cron」である wp-cron が存在します。これはアクセスがあったタイミングで実行される仕組みで、日中はアクセスの都度こまめに処理されますが、深夜にアクセスが激減すると翌朝の最初のアクセスで大量のタスクが一気に実行されます。
さらに、マルチサイト構成やプラグインが多い環境では、wp-cronが意図せず重複起動するケースも確認されています。
3. PHPのOPcacheとセッションのガベージコレクション
OPcacheの memory_consumption が上限に達すると、キャッシュのリセットが発生し、その直後にPHPプロセスがすべてのファイルを再コンパイルするためメモリスパイクが起きます。セッションのGC(ガベージコレクション)も同様で、深夜の低トラフィック時にまとめて走ることがあります。
実案件での出来事:気づいた時には手遅れだった
あるクライアントのECサイト(Laravel + MySQL構成)で、毎朝9時ごろに「500エラーが出た」という報告が週に1〜2回届くようになりました。再起動すれば即座に復旧するため、最初は「一時的な問題」として放置されていたのです。
ログを遡ってみると、深夜2時台に毎回メモリ使用率が95%を超えており、php-fpm のワーカーが次々とkillされていました。原因は、在庫同期バッチが Product::all() で全件取得した後、外部APIに1件ずつリクエストを送るという実装でした。商品数が3,000件を超えたころから症状が顕在化したのです。
解決策:チャンク処理への変更とアラートの実装
// 修正後:chunk()でメモリ消費を抑制
Product::chunk(200, function ($products) {
foreach ($products as $product) {
$this->syncWithExternalApi($product);
}
// 各チャンク処理後にメモリ状況をログへ
Log::info('Chunk processed. Memory: ' . memory_get_usage(true) / 1024 / 1024 . 'MB');
});
この変更だけで、深夜のメモリピークが95%→52%に下がりました。さらに翌週から監視アラートを実装し、同様の事象が再発した場合は即座に検知できる体制を整えました。
具体的な監視設計:「気づける仕組み」を作る
Step 1:Linuxのメモリ使用率を定期的にログへ記録する
まず基礎として、サーバー上でメモリ使用率を記録するシェルスクリプトをcronで動かします。
#!/bin/bash
# /usr/local/bin/mem_check.sh
THRESHOLD=80
USAGE=$(free | awk '/^Mem:/ {printf "%.0f", $3/$2 * 100}')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "${TIMESTAMP} memory_usage=${USAGE}%" >> /var/log/mem_monitor.log
if [ "$USAGE" -ge "$THRESHOLD" ]; then
echo "[ALERT] ${TIMESTAMP} メモリ使用率が${USAGE}%に達しました" | \
mail -s "[本番サーバー警告] メモリ急上昇" [email protected]
fi
# crontabに追加(5分ごとに実行)
*/5 * * * * /usr/local/bin/mem_check.sh
Step 2:Laravelバッチにメモリ監視を組み込む
スケジュールタスク自体にメモリ上限チェックを組み込むことで、異常を早期に検知できます。
<?php
// app/Console/Commands/SyncInventory.php
public function handle()
{
$memoryLimit = 256 * 1024 * 1024; // 256MB
Product::chunk(200, function ($products) use ($memoryLimit) {
foreach ($products as $product) {
$this->syncProduct($product);
}
$currentUsage = memory_get_usage(true);
if ($currentUsage > $memoryLimit) {
Log::critical('メモリ使用量が上限に近づいています', [
'usage_mb' => round($currentUsage / 1024 / 1024, 2),
'limit_mb' => 256,
]);
// Slackへの通知など追加可能
$this->notifySlack($currentUsage);
return false; // チャンク処理を中断
}
});
}
private function notifySlack(int $memoryUsage): void
{
Http::post(config('services.slack.webhook_url'), [
'text' => sprintf(
':warning: [本番] SyncInventoryのメモリ使用量が %.1fMB に達しました。処理を中断しました。',
$memoryUsage / 1024 / 1024
),
]);
}
Step 3:WordPressのwp-cron問題に対処する
WordPress環境では、wp-cronをサーバーのcronに置き換えることで安定性が大きく向上します。
# wp-config.php に追加
define('DISABLE_WP_CRON', true);
# サーバーのcrontabに追加
*/5 * * * * php /var/www/html/wp-cron.php > /dev/null 2>&1
これだけで「深夜の未処理タスクが朝に爆発する」問題はほぼ解消されます。
flowchart TD
A[夜間バッチ・cronが起動] --> B{メモリ使用率チェック}
B -->|80%未満| C[正常処理を継続]
B -->|80%以上| D[Slackアラート送信]
D --> E[処理を安全に中断]
E --> F[翌朝エンジニアが確認]
C --> G[処理完了ログ記録]
G --> H[ログ監視ツールで可視化]よくある失敗パターンと対処法
失敗1:「閾値80%でアラート」→ 実は手遅れだった
よくあるのが、「メモリ80%でアラート」という設定を入れたのに障害が防げなかったケース。原因は、バッチ処理中のメモリスパイクが数十秒の間に80%→100%まで駆け上がるため、アラートが届いてから対応する時間がないのです。
対処法は「警告(70%)」と「緊急(85%)」の2段階アラートを設けること。70%の段階で翌日確認できれば、障害に至る前に対処できます。
失敗2:ログを取ってるのに見ていない
ログは記録されているが、「誰も定期的に確認していない」という状況は非常によく見られます。アラートがメールで来ても、迷惑メールに紛れてしまうことも。SlackやLINE WORKSへの通知に変更するだけで、気づきやすさが劇的に向上します。
失敗3:OPcacheの設定を放置している
php.ini のOPcache設定がデフォルトのまま(memory_consumption=128MB)というケースも多いです。コードベースが肥大化するにつれ、このデフォルト値では不足し、頻繁なリセットが発生します。opcache_get_status() で実際の使用量を確認し、余裕を持った値に設定し直すことを推奨します。
// 現在のOPcache状況を確認
$status = opcache_get_status();
echo 'メモリ使用: ' . round($status['memory_usage']['used_memory'] / 1024 / 1024, 2) . 'MB';
echo '空き: ' . round($status['memory_usage']['free_memory'] / 1024 / 1024, 2) . 'MB';
サーバー管理、丸ごとお任せください
サーバー保守・運用
監視・障害対応・パフォーマンス改善まで、安定稼働をサポートします
※ 通常1営業日以内にご返信します
まとめと次のステップ
「静かな障害」は、仕組みを理解して適切な監視を設計すれば、十分に防げます。大切なのは「落ちてから気づく」から「予兆で気づく」への転換です。まず今週できることから着手してみてください。
「監視の設計はわかったけど、自社のサーバー構成に合わせて実装するのが難しい」「そもそも今の環境がどんな状態かも把握できていない」という場合は、ぜひFivenine Designにご相談ください。サーバー診断から監視設計の実装まで、20年以上の実績をもとに対応いたします。