引き継いだシステムで最も多い障害が、プロセスが落ちたまま復旧しないというものです。
原因は難しい話ではなく、たいていは「自動で戻る仕組みが入っていない」だけです。
まず systemd に任せる
[Service]
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=300
StartLimitBurst=5
Restart=on-failure… 異常終了時に再起動RestartSec=5… 5秒待ってから(即時だと連続失敗で負荷が跳ねる)StartLimitBurst… 300秒に5回を超えたら諦める
最後の2行が地味に重要です。これが無いと、
設定ミスで起動できない状態のとき、無限に再起動を繰り返して CPU を食い潰します。
再起動で自動復帰させるのも忘れずに。
systemctl enable --now myapp
systemctl is-enabled myapp # → enabled
メモリで落ちる場合
OOM Killer に殺されている場合、Restart=on-failure では戻らないことがあります
(シグナル終了は「失敗」扱いにならない設定があるため)。確実に戻したいなら:
Restart=always
同時に、Gunicorn 側でワーカーを定期的に入れ替えるとメモリリークが緩和されます。
max_requests = 1000
max_requests_jitter = 100
jitter を入れないと全ワーカーが同時に再起動して、その瞬間だけ応答が詰まります。
落ちたことに気づく仕組み
自動復旧が入っていても、復旧できない障害は必ず起きます。
最小の死活監視として、外形監視を1つ入れておきます。
#!/bin/sh
# 5分ごとに cron で実行
CODE=$(curl -s -o /dev/null -w '%{http_code}' -m 10 https://example.com/health/)
if [ "$CODE" != "200" ]; then
echo "health check failed: $CODE" | mail -s "[ALERT] site down" you@example.com
fi
サーバー自身から監視すると、サーバーごと落ちたときに気づけません。
可能なら別のホストから叩くようにします。
Django 側のヘルスチェックは、DB まで見るようにしておきます。
def health(request):
try:
connections["default"].cursor().execute("SELECT 1")
except Exception:
return HttpResponse("db error", status=503)
return HttpResponse("ok")
「Nginx は生きているが DB が死んでいる」状態を検知できるようになります。
ログの肥大でディスクが埋まる事故も防ぐ
意外と多いのがこれです。/var/log が埋まって書き込めなくなり、サービスが止まります。
/var/log/myapp/*.log {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}
まとめ
Restart=とStartLimitBurstをセットで入れる(無限再起動を防ぐ)systemctl enableを忘れない(サーバー再起動で戻らない事故の大半はこれ)- 別ホストからの外形監視を1本。ヘルスチェックは DB まで見る
- logrotate を入れてディスク枯渇を防ぐ
どれも数行の設定ですが、これが入っているかどうかで運用の安心感がまったく変わります。