サーバーは、動いているだけでは不十分で、異常を検知して自分に通知が届く仕組みが必要である。ここでは監視ダッシュボード(Netdata)、通知配信(ntfy)、自動更新(Watchtower)、そして自作の監視スクリプト群の実装を記す。
Netdata: リアルタイム監視ダッシュボード
Netdataは、CPU・メモリ・ディスク・ネットワーク・個々のコンテナのリソース使用状況を、追加設定なしでリアルタイムに可視化する監視ツールである。既定では認証なしで全インターフェースに公開される設定になっているため、待受アドレスをループバックに限定した上で、必要な時だけ手元から専用トンネル経由でアクセスする構成にしている。

Netdataのダッシュボード例(公式配布物)。CPU・メモリ・ネットワーク等のメトリクスが自動でグラフ化される。
ntfy: 通知配信
ntfyはシンプルなプッシュ通知サービスで、HTTPリクエスト一つでスマートフォンに通知を送れる。用途は完全に片方向で、監視スクリプト側が異常を検知した時にのみpublishし、スマートフォン側は購読するだけである。
Netdataに与えている強い権限とその理由
Netdataはコンテナ単位のリソース使用状況だけでなく、ホストOS全体のプロセス・ネットワーク・カーネル統計まで可視化するため、通常のコンテナよりも強い権限(プロセストレース・システム管理権限)を与え、コンテナのセキュリティ制限機構(AppArmor)も一部緩めている。これは監視ツールとしての性質上避けられないトレードオフで、その代わりに待受アドレスをループバックに限定し、専用トンネル経由でのみアクセスできるようにすることで、強い権限を持つこのコンテナ自体への外部からの直接到達を防いでいる。
認証の仕組みもクラウド型の監視サービスとは異なり、外部のアカウント基盤を使わない代わりに、初回アクセス時にコンテナ内部で自動生成されたセッションキーをコンテナのファイルシステムから直接取得し、ブラウザに入力するローカル完結型のセッション認証になっている。データが外部のクラウドサービスへ一切出ていかない代わりに、キーの取得自体にコンテナへのアクセス権が必要という、セルフホスト前提の認証方式である。
運用中に見つかった小さな不具合として、一部のプラグインが起動時に参照するスクリプト置き場のディレクトリがイメージに含まれておらず、毎分エラーログを吐き続けていたことがあった。ディレクトリを作成するだけで解消しており、実害はないが、放置すればログの中の本当に重要な警告がノイズに埋もれる原因になるため、見つけ次第潰す方針にしている。
Watchtower: コンテナイメージの自動更新
画像: Netdataダッシュボード、Watchtowerロゴ、Wikimedia Commons
Watchtowerは、稼働中のDockerコンテナのイメージを定期的にチェックし、新しいバージョンがあれば自動的に再作成する。ただしパスワード管理ソフトウェアなど、セキュリティ上重要で予期しない自動更新が望ましくないサービスは対象から除外している。更新結果はntfy経由で通知される連携は、Watchtower自体が持つ通知フォーマット変換機能(shoutrrr)を使い、ntfyのAPIエンドポイントを直接指定するだけで実現しており、両者の間に追加の仲介サービスを挟んでいない。除外対象のコンテナには、そのサービス定義自体に除外理由をコメントとして残す運用にしている。半年後の自分が「なぜこのサービスだけ自動更新されないのか」を再調査せずに済むようにするためで、飲料や電源のような当たり前の存在ほど、判断の理由を書き残さないと忘れるという実感に基づいている。
自作の監視スクリプト
Watchtowerでは自動更新の対象から意図的に外している重要サービス(パスワード管理・ファイル共有・ホームオートメーション基盤)については、更新の有無だけを別のスクリプトで監視している。GitHub Releases APIで各サービスの最新安定版タグを取得し、稼働中のバージョンと比較して差分があれば通知する。プレリリース版・ドラフト版はAPI側で自動的に除外されるエンドポイントを使っているため、安定版だけを見る処理を自前で書く必要がない。自動更新はしない設計で、これは設定移行の互換性確認やアップグレード手順の実行順序など、無人での自動更新にはリスクが伴う工程が挟まるサービスであるためである。週1回、日曜朝に定期実行しており、GitHub API自体が到達不能・レート制限にかかった場合はログに記録するだけで通知はしない(一時的な取得失敗と、実際のバージョン更新を区別するため)。
ヘルスチェック: 定期的な自己点検
15分おきに実行されるヘルスチェックスクリプトは、以下を確認する。
- ディスク空き容量(複数のストレージ、それぞれ異なる閾値)
- 常時起動していてほしいコンテナ群が実際に稼働しているか
- 関連するsystemdサービスがfailed状態でないか
- 直近のバックアップが一定時間以内に作成されているか(自動実行が止まっていないかの検知)
- バックアップが本来の保存先(専用ディスク)ではなく、フォールバック先のローカルディスクに書かれていないか(専用ディスクが未接続だった場合の兆候)
- LAN遮断用のファイアウォールルールが有効なままか(OSの更新等で意図せず削除・無効化されていないか)
監視スクリプト自身のバグ
最後のファイアウォールルール確認は、ホストOS(Windows)側のコマンドをWSL2側から呼び出す形で実現している。ところが導入当日、このチェックが数時間にわたって「ルールが見つからないか無効化されている」という警告を15分おきに繰り返し出し続ける事象が発生した。実際にはルールは有効なままで、誤検知だった。
原因を追うと、ホストOS側コマンドの実体をパス名で(フルパス指定せずに)呼び出していたことに行き着いた。対話シェルからスクリプトを手動実行するとWSL統合機能が提供するパス解決が効くため問題なく動くが、systemdのサービスとして実行される場合はこの対話シェル用のパス解決が存在せず、コマンドが見つからずに常に空文字列を返す結果になっていた。これがそのまま「ルールが見つからない」という誤判定につながっていた。修正はコマンドをフルパスで指定するだけの単純なものだが、原因の特定には、systemd実行時の環境変数を再現した状態でスクリプトを手動実行するという検証が必要だった。教訓: WSL2経由でホストOS側の実行ファイルを呼び出すスクリプトをsystemd管理下に置く場合、対話シェルでの動作確認だけでは不十分で、必ずフルパス指定を徹底する必要がある。
バックアップ: 重要データと大容量データの分離
バックアップは、パスワード・設定ファイル・データベースダンプなど「小容量だが代替不可能」なデータと、写真・動画など「大容量だがローカルの複数世代保持で許容できる」データを、別の保存先に分離している。前者はクラウドストレージの無料枠にも収まる規模のためオフサイト保存(クラウドへのアップロード)も行い、後者は容量が無料枠を大幅に超えるためローカル保存のみとしている。動画配信で使っているライブラリ本体(映画・TV番組)は、この大容量データのバックアップ対象からも意図的に除外している。配信元から再取得可能なコンテンツであり、バックアップの優先度は「代替不可能かどうか」を基準に判断すべきという方針を反映したものである。
写真・動画は元々圧縮済みでgzipの効果が薄いため、単一コアの圧縮では大容量データの処理に非常に時間がかかる問題があった(実測でおよそ50分規模)。これを全コア並列で圧縮するツールに切り替えて解決している。また、バックアップジョブが実行されるタイミングによっては、対象のコンテナが起動直後で内部初期化中のためファイルが一時的に読み取れないことがある。実際に、深夜のPC起動直後にジョブが走った際、コンテナ内部所有のファイルが一時的に読み取り拒否になり失敗する事象が発生した。当時は12分後の手動再実行では問題なく成功したことから、一時的な初期化競合と判断し、スクリプト側に一定時間待ってから1回だけ再試行するリトライ処理を組み込んでいる。
重要データのアーカイブには、データベースダンプ以外にも、各サービスの定義ファイル・主要な設定ファイル・パスワード/連絡先/カレンダーの実データを個別に指定して含めている。専用ディスクが接続されていない場合はローカルディスクへのフォールバックで大容量バックアップを継続する設計だが、この状態が長引くと気づかないまま専用ディスク側の世代保持が更新されなくなるリスクがある。そのため、ヘルスチェック側でフォールバック生成物の有無を検知する仕組みと対にして運用している(前節参照)。実際に、この分離設計を導入する前は、両方のバックアップが同じディスクに書かれており、ディスクの空き容量を一時的に逼迫させたことがあった。
バックアップの実効性を確かめる
バックアップは取得して終わりではない。取得したデータベースダンプを、本番とは別のテスト用データベースコンテナに実際にリストアし、リストア後のレコード件数が本番の実データと一致することを確認する検証を行っている。バックアップの取得ログが正常終了していることと、そのバックアップから実際にサービスを復元できることは別の話であり、検証を経て初めて「使えるバックアップ」だと言える。
参考リンク
- Netdata 公式サイト / GitHub
- ntfy 公式ドキュメント / GitHub
- Watchtower GitHub(2025年12月にアーカイブされ開発終了。ドキュメント自体は参照可能)