インフラ・DevOps
Goバイナリ1つ、YAMLファイル1つ、SQLiteデータベース1つ:自作の監視ツール
One Go binary, one YAML file, one SQLite database: I wrote my monitoring tool (rvier.fr)
要約
著者は、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。