MENU

systemd ウォッチドッグで守る本番サービス|sd_notify 統合・応答タイムアウト設計とアラート連携

目次

本番環境でウォッチドッグが必要になる背景

サービスが「プロセスとして生きている」ことと「正常に応答できている」ことは、別の問題です。多くの運用現場では、デーモンが起動したままフリーズし、外部からの HTTP リクエストに無応答になるという障害を経験しています。従来のプロセス監視ツールは生死しか見ておらず、こうした「ゾンビ状態」を検知できません。

systemd のウォッチドッグ機能は、この問題をカーネルレベルのタイマーで解決します。サービスユニットに WatchdogSec= を設定すると、アプリケーション側が定期的に「生存通知」を送り続けなければ、systemd が自動的にそのサービスを再起動します。単なるプロセス監視を超えた「ヘルスチェック付き自動回復」が、追加エージェント不要の標準機能として利用できる点が大きな利点です。

systemd ウォッチドッグの仕組み――WatchdogSec と WATCHDOG_USEC

ウォッチドッグの核心は、systemd とアプリケーションの間で行われるハンドシェイクにあります。ユニットファイルに WatchdogSec=30s と記述すると、systemd は起動時に環境変数 WATCHDOG_USEC=30000000(マイクロ秒単位)をサービスプロセスに渡します。アプリケーションはこの値を読み取り、インターバルの半分以下の周期でキープアライブ通知を送る実装が推奨されています。

通知が途絶えた場合、systemd は WatchdogSec で指定した時間が経過した時点でサービスを停止し、Restart= の設定に従って再起動します。デフォルトのシグナルは SIGABRT(v252 以降)であり、コアダンプの収集に使えます。ハングアップ時の原因調査にそのまま役立てられる点は、運用上の大きなメリットです。

ユニットファイルの基本的な記述例は以下のとおりです。

[Service]
Type=notify
ExecStart=/usr/local/bin/myapp
WatchdogSec=30s
Restart=on-failure
RestartSec=5s

Type=notify は必須の設定です。この指定がなければ systemd はウォッチドッグ通知ソケットをサービスプロセスに公開しません。既存サービスに後付けする際に見落とされやすい点なので、設定変更後は systemctl statusWatchdog: の行が表示されることを確認してください。

sd_notify でアプリケーション側からキープアライブを送る

アプリケーション側の実装は、libsystemdsd_notify() 関数、または Unix ドメインソケット($NOTIFY_SOCKET)への直接書き込みで行います。言語を問わず対応できるのがソケット直書きアプローチの利点です。

C/C++ であれば libsystemd をリンクして次のように呼び出せます。

#include <systemd/sd-daemon.h>

// 起動完了通知
sd_notify(0, "READY=1");

// メインループ内でウォッチドッグキック(WatchdogSec の半分以下の周期で)
sd_notify(0, "WATCHDOG=1");

Python や Go など libsystemd バインディングを使わない場合は、NOTIFY_SOCKET 環境変数が示す Unix ドメインソケットに文字列 WATCHDOG=1\n を送信するだけです。Go での実装例は次のとおりです。

package main

import (
    "net"
    "os"
    "time"
)

func watchdogKick(interval time.Duration) {
    sock := os.Getenv("NOTIFY_SOCKET")
    if sock == "" {
        return
    }
    conn, _ := net.Dial("unixgram", sock)
    defer conn.Close()
    ticker := time.NewTicker(interval)
    for range ticker.C {
        conn.Write([]byte("WATCHDOG=1\n"))
    }
}

ここで重要なのは、キープアライブをメインループの健全性チェックと連動させることです。単にタイマーで WATCHDOG=1 を送り続けるだけでは、アプリケーションがデッドロックに陥っても検知できません。データベース接続の疎通確認やキューの消費確認など、実際の処理パスを通過した後に通知を送る設計が必要です。この「確認してから通知」の流れを省略すると、ウォッチドッグは形骸化します。

タイムアウト値の設計指針

WatchdogSec の値は、短すぎると一時的な高負荷で誤検知が発生し、長すぎると障害の検知が遅れます。現場では以下の基準で設定する運用者が多くいます。

  • 10〜30秒:Web API・マイクロサービスなど応答速度が重要なサービス。ヘルスチェック応答のタイムアウトより長めに設定し、ネットワーク遅延での誤検知を防ぎます。
  • 60〜120秒:バッチ処理・定期ジョブ。処理の1サイクルが長い場合でも、ループの終端で必ず通知を送る実装とセットで運用します。
  • 300秒以上:長時間トランザクション・ETL パイプライン。この場合は別途プロセス外ヘルスチェック(Prometheus Blackbox Exporter 等)と併用するのが現実的です。

また、StartLimitIntervalSecStartLimitBurst を組み合わせて、短時間での再起動ループを抑制することも重要です。

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

[Service]
WatchdogSec=30s
Restart=on-failure
RestartSec=10s

この設定では、5分間に5回以上の再起動が発生した段階でサービスの自動再起動が停止し、人間による介入が促されます。ウォッチドッグによる無限再起動ループを防ぐ安全弁として機能し、障害の本質的な調査を促す効果もあります。

ジャーナルとアラートシステムへの連携

ウォッチドッグがトリガーされた事実を検知・通知する仕組みがなければ、自動回復は「問題を隠すだけ」になりかねません。systemd はウォッチドッグタイムアウト発生時に systemd-journald へ専用のログエントリを書き込みます。

journalctl でウォッチドッグ関連イベントを絞り込む場合は次のように実行します。

# ウォッチドッグ関連イベントを絞り込む
journalctl -u myapp.service -g "watchdog" --since "1 hour ago"

# systemd 自身のメッセージも含めて確認する
journalctl _SYSTEMD_UNIT=myapp.service SYSLOG_IDENTIFIER=systemd

Prometheus + Alertmanager 環境では、node_exporter の systemd コレクターが提供する node_systemd_unit_state メトリクスを用いた監視が一般的です。

- alert: ServiceWatchdogRestarting
  expr: |
    increase(node_systemd_unit_state{name="myapp.service",state="failed"}[10m]) > 0
  for: 0m
  labels:
    severity: critical
  annotations:
    summary: "{{ $labels.name }} がウォッチドッグタイムアウトにより再起動しています"

Grafana Loki を使ったログベースアラートも有効です。rate({unit="myapp.service"} |= "watchdog" [5m]) > 0 のような LogQL クエリでウォッチドッグイベントを捕捉し、PagerDuty や Slack へ通知するパイプラインを構築する運用者も増えています。いずれの方法でも共通するのは、「アラートは再起動完了後ではなくウォッチドッグタイムアウト発生時点」でトリガーする設計にすることです。回復後にアラートを送っても、その間の影響範囲(リクエストのドロップ数・キューの積み残し等)を把握するための調査起点が失われます。

2026年現行環境での注意点と最新動向

2026年時点で主要ディストリビューションが採用する systemd は v255〜v257 系です。この系列ではいくつかの変更点があり、既存設定を見直す必要が生じる場合があります。

  • WatchdogSignal のデフォルト変更:v252 以降、WatchdogSignal=SIGABRT がデフォルトです。旧来の SIGKILL 動作を期待している設定は明示的に WatchdogSignal=SIGKILL と書き直す必要があります。コアダンプの収集を意図しない場合は特に確認してください。
  • sd_notify の拡張ステータス:v253 で追加された WATCHDOG_TRIGGER=error-message 形式により、タイムアウト理由をジャーナルに記録できるようになっています。アプリケーション側でヘルスチェック失敗の原因を文字列で渡すことで、障害調査が格段に容易になります。
  • NotifyAccess のセキュリティ強化NotifyAccess=main(デフォルト)では、メインプロセスの PID 以外からの通知を拒否します。コンテナ内のサイドカープロセスやスレッドから通知する場合は NotifyAccess=all への変更が必要ですが、攻撃面が広がる点を把握した上で設定してください。
  • コンテナ環境での動作:systemd を init として起動するコンテナ(systemd-nspawn・一部の Kubernetes ノード設定)では通常通り動作しますが、Docker/containerd のデフォルト設定では systemd が PID 1 にならないため、ウォッチドッグ機能は利用できません。コンテナ内のヘルスチェックは HEALTHCHECK ディレクティブや Kubernetes の livenessProbe で代替する必要があります。

ウォッチドッグ設定を本番へ投入する前に、systemd-analyze security myapp.service でセキュリティスコアを確認し、NotifyAccessPrivateTmp などのサンドボックス設定とのバランスを取ることも重要です。過度に制限したサンドボックス設定が NOTIFY_SOCKET のアクセスを阻害し、ウォッチドッグが機能しないまま稼働するケースが報告されています。設定変更後は必ずテスト環境で SIGSTOP を使ってハング状態を模擬し、タイムアウト後に正しく再起動されることを確認する手順を運用フローに組み込むことをお勧めします。

本番サービスの安定運用を実現するには、ウォッチドッグの設定単体ではなく、アプリケーション内のヘルスチェックロジック・タイムアウト設計・アラート連携を一体として設計することが求められます。systemd の標準機能を最大限に活用することで、外部の監視エージェントへの依存を減らし、より堅牢な自律回復基盤を構築できます。

「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。

ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。

>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)

※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。

PR・広告

[試して理解]Linuxのしくみ 増補改訂版(Amazon)

プロセス・権限・ファイルシステムなどLinux内部を図解で理解できる一冊。運用判断の勘所に。

Amazonで見る

※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次