HN 日本語サマリー

← 一覧へ戻る
インフラ・DevOps

Goバイナリ1つ、YAMLファイル1つ、SQLiteデータベース1つ:自作の監視ツール

One Go binary, one YAML file, one SQLite database: I wrote my monitoring tool (rvier.fr)

8 pointsby brvier2 コメント

要約

著者は、Go言語で「Gjallar」というシンプルで自己完結型の監視ツールを開発しました。複雑な依存関係やプラットフォームを避け、単一のバイナリ、YAML設定ファイル、SQLiteデータベースで構成されています。HTTPエンドポイント、データベース、メトリクスなど多様なサービスを監視し、TelegramやSMSで通知します。このツールは、シンプルさ、再起動への耐性、運用しやすさを重視しており、クラスター化やエージェントのような機能は意図的に省略されています。

全文翻訳

Goバイナリ1つ、YAMLファイル1つ、SQLiteデータベース1つ:自作の監視ツール July 9, 2026 HTTPエンドポイント、PostgreSQLデータベース、いくつかのOracleインスタンス、Redis、最新の状態を保つ必要があるElasticsearchインデックス、pingに応答すべきマシン、そしていくつかのPrometheusメトリクスなど、多様なサービスのフリートを監視する必要がありました。そして、何か障害が発生したとき、復旧したときに、Telegram、SMS、Signalで通知を受ける必要がありました。 一般的な解決策は監視プラットフォームです。PrometheusとAlertmanagerとGrafana、そしていくつかのエクスポーター、あるいはデータベースを持つNode.jsアプリを実行するコンテナなどです。これらはすべて素晴らしいツールです。 しかし、数十個のチェックのために、最初のシステムが稼働しているかどうかを知るためだけに、第二の分散システムを運用したくはありませんでした。そして、軽量なオプションのどれも、OracleクライアントライブラリをどこかにインストールすることなくOracleにクエリを実行できませんでした。 そこで私はGjallarを書きました。KISS(Keep It Simple, Stupid)監視サービスです。単一の静的バイナリ、単一のYAML設定ファイル、単一のSQLiteファイル。履歴付きの黒と赤のステータスページで、HTMXでリフレッシュされます。Goで約3,400行です。MITライセンスです。 意図的にCGOなし ツール全体はCGO_ENABLED=0でビルドされます。 CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" これは、伝統的にCライブラリにバインドされるすべての依存関係が、現在では純粋なGoの代替品を持っており、それらが優れているためのみ可能です。 pgx for PostgreSQL: libpq不要 go-ora for Oracle: Oracle Instant Client不要。これだけでプロジェクトの正当性が証明されます。もしあなたが最小限のボックスにOracleクライアントをデプロイしたことがあるなら、その苦労はわかるでしょう。 pro-bing for ICMP echo: 権限ありまたはなし modernc.org/sqlite for storage: SQLiteを純粋Goにトランスパイルしたもの。libsqlite3不要。 Redisにはドライバーは全く必要ありません。チェックはプロトコルを直接話します。TCP接続、オプションで認証、PING、+PONGを期待します。 結果として、単一の自己完結型バイナリ(約36MB、そのほとんどがSQLiteとOracleドライバー)が得られます。これは私のラップトップから任意のターゲットにGOOS/GOARCHでクロスコンパイルでき、scpでデプロイできます。Docker不要、パッケージマネージャー不要、共有ライブラリ不要、「私のマシンでは動く」という問題もありません。 ロックフリーのアラートパイプライン 監視ツールは本質的に並行処理を行います。各モニターはほとんどの時間ネットワークを待機しており、並行処理はサイドプロジェクトが最初のミューテックスのジャングルを成長させる場所になりがちです。Gjallarはパイプラインの形状のおかげで、状態の周りにロックを一切持ちません。 one goroutine per monitor ──▶ results channel ──▶ single consumer (state machine + SQLite writes) 各モニターは独自のゴルーチンでチェックループを実行し、check.Result値を共有チャネルに送信します。単一のコンシューマーゴルーチンが、ステートマシン、インシデント行、履歴書き込みなど、すべてを下流で所有します。単一のゴルーチンだけがステートマップとデータベース接続に触れるため、ロックするものは何もありません。また、同時書き込みを嫌うSQLiteには、まさに1つの書き込みだけが与えられます。 モニターごとの状態は小さく、明示的です。 type monitorState struct { down bool consecFails int downSince time.Time lastNotified time.Time threshold int // DOWNを発火させるまでの連続失敗回数 realert time.Duration // ダウン中のリマインダー間隔; 0 = 無効 notifiers []string } 本番環境で役立った2つの設計ポイントがあります。 状態は再起動を生き延びます。起動時に、各モニターの状態はSQLite内の開いているインシデントからシードされます。ダウン中に再起動しても、DOWNアラートが再発火したり、復旧通知が漏れたりすることはありません。障害中に新しいバージョンをデプロイしても、何も起こりません。 通知は非同期にディスパッチされます。コンシューマーは決してブロックしてはなりません。遅いSMTPサーバーやレート制限されたTelegram APIが、パイプライン全体をバックプレッシャーで圧迫することはできません。送信は15秒のタイムアウトを持つ独自のゴルーチンで行われます。 アラートはN回の連続失敗後に発火し、最初の小さな変動で発火するのではなく、フラッピングノイズがありません。また、インシデントが開いている間はオプションでリリアルート間隔がリマインドしてくれます。 運用を尊重する設定 すべてが1つのYAMLファイルに、デフォルト値、通知名、モニターグループとともに収められています。 defaults: interval: 60s timeout: 10s failure_threshold: 3 alerts: - ops-telegram alerts: ops-telegram: url: "telegram://TOKEN@telegram?chats=123456789" monitors: - name: app-db type: postgres dsn: "postgres://monitor:${PG_PASSWORD}@db1:5432/app" query: "SELECT count(*) FROM jobs WHERE status = 'stuck'" rule: "== 0" 運用を快適にする3つの小さな機能があります。 SIGHUPによるホットリロード:systemctl reload gjallar は、完全に検証された後に新しい設定を適用します。壊れたYAMLは、監視を停止させる代わりに、実行中の設定を維持し、エラーをログに記録します。あなたの監視者は、タイプミスで最後に死ぬべきものです。 秘密情報のための${VAR}環境変数展開。参照された変数が未定義の場合、明確な起動時失敗が発生しますが、ベアの$(例えば、正規表現ルール `~ ^OPEN$` のようなもの)は変更されません。 CIがサーバーに到達する前に設定をリントできるように、ドライラン検証のための-checkフラグ。 意図的に行わないこと クラスター化なし、エージェントなし、プラグインシステムなし、時系列ダッシュボードなし、ユーザーアカウントなし。履歴は設定可能な保持期間(デフォルトで30日)後に削除されるため、SQLiteファイルは常に小さく保たれます。 既存のシンプルなメカニズム(サービスライフサイクルのためのsystemd、私が決して使用しないであろう20の通知サービスのためのshoutrrr URL)でニーズがうまく満たされる場合、Gjallarは再実装するのではなく委任します。 これが私が最も強く擁護する部分です。長年にわたって私が放棄したすべての監視ツールは、同じ病気で死にました。それはゆっくりとプラットフォームになり、ある日監視が監視を必要とするようになったのです。 そのすべての状態が1つのSQLiteファイルに収まり、そのすべての動作が1つのYAMLファイルに収まるツールは、あなたがそれを書いた18ヶ月後の午前3時でも、まだ理解できるツールです。 コードとドキュメント:github.com/brvier/Gjallar。