コンテンツにスキップ

オブザーバビリティ

監視できない監視システムは、ダッシュボードの付いた死角にすぎません。

そこで Yagra のバイナリは、コアもポーラーも、追加の設定なしで 3 つのシグナルを出します。stdout への構造化ログ、Prometheus の /metrics エンドポイント、そして OpenTelemetry のトレースです (トレースだけは任意)。

このページでは、その 3 つに加えて、それらをまとめて判定してくれる組み込みの 設定 ▸ Yagra ヘルスページを扱います。

自己観測のためのシグナルを一覧すると次のとおりです。

シグナル コア ポーラー 既定
構造化ログ(stdout) ✔ ✔ 有効(info レベル)
Prometheus /metrics ✔ — API ポート上 ✔ — ポート 9100 上 有効
ヘルスプローブ /healthz / /readyz ✔ — 有効
OpenTelemetry トレース ✔ ✔ 無効 — YAGRA_OTEL_ENDPOINT でオプトイン
ホストリソース(CPU / ロード / メモリ / ディスク / ネットワーク) ✔ ✔ 有効 — 設定 ▸ Yagra ヘルスにグラフ表示

ここに挙げたものは、どれも追加のコンテナを必要としません。単一ノードのスタックなら、 docker compose logs と 2 つの HTTP エンドポイントを見るだけで、中の様子が全部分かります。 コレクタが要るのはトレーシングだけで、それも送り先を指定するまでは無効のままです。

コアは、API のポート(同梱の compose ファイルでは 8080)で /metrics を提供します。REST API やヘルスプローブと同じポートです。追加で公開するポートはありません。API に届くものなら、何でも コアをスクレイプできます。

各ポーラーは、0.0.0.0:9100 固定で自分の /metrics を提供します。ただし既定の単一ノード compose は、このポートをホストに公開しません。届くようになるのは、ポーラーをホストネットワーク で動かしたとき(リモート拠点ポーラーの構成がこれに当たります)か、ネイティブで動かしたときです。 ポートの全一覧はポートとファイアウォールにあります。

どんなものが出ているか感じをつかめるよう、代表的な系列をいくつか挙げます。

メトリクス 何がわかるか
yagra_core_is_leader アクティブなコアで 1、スタンバイで 0 — 高可用性のリーダーゲージであり、/readyz が答えているのと同じシグナルです
yagra_mcp_tool_calls_total{tool,outcome} MCP を有効にしているときの、ツール別・結果別の MCP ツール呼び出し数
yagra_forward_dropped_total{reason} 転送で捨てたレコード数と、その理由(queue_full、rate_limit、circuit_open など)。中継先が遅れ始めたとき、最初に見る場所です
yagra_llm_calls_total{provider,outcome} AI 根本原因分析の、プロバイダ別の呼び出し数。課金される外部呼び出しなので、アラートを張る価値があります
yagra_core_backfill_results_total ネットワークが切れたあと、store-and-forward の再送で取り込んだポーリング結果

これは一覧ではなく、あくまで例です。種類は機能が増えるたびに増えます。動いているバージョンが実際に 出しているものをすべて見たいときは、エンドポイントをスクレイプしてください。

コアの API ポートには、認証の要らないプローブが 2 つあります。これらは「プロセスは生きているか」と 「トラフィックをここへ送ってよいか」を切り分けます。

  • GET /healthz — 生きているか。 動いているコアなら常に 200 ok を返します。高可用性の スタンバイでも同じです。バックエンドのストアには一切触れません。ですからデータベースの障害を きっかけに、まったく健全なプロセスが再起動される、ということが起きません。コンテナの再起動 ポリシーには、こちらを使ってください。
  • GET /readyz — リーダーとして受け付けられるか。 いまリーダーを持っているコアだけが 200 を返します。スタンバイは 503 です。ロードバランサのヘルスチェックをここに向ければ、 フェイルオーバーのときにトラフィックがリーダーへ付いていきます。詳しくは 高可用性を見てください。

単一コアのデプロイ(HA 無効、既定)では、区別すべきスタンバイがありません。ですから /readyz は、コアが起動していれば単純に 200 を返します。

コアのコンテナイメージには、これを使ったヘルスチェックが入っています。同梱の compose ファイルは、 コアが応答を始めてから web コンテナを起動します。ですから起動直後の最初のリクエストがエラーに なることはありません。

設定 ▸ Yagra ヘルスは、「Yagra 自身は大丈夫か」への WebUI の答えです。ブラウザ側で判定を 組み立て直すことはしません。サーバー自身の判定を、そのまま表示します。

  • バックエンドサービスに届いているか — ストアごとに 1 行出ます(PostgreSQL、Redis、 VictoriaMetrics、そして任意の VictoriaLogs と ClickHouse)。バスの行もあります。上には 「All reachable」/「Degraded」の総合バッジが付きます。これはサーバー側で計算した値です。

    ですから、将来の依存先をこのページが表示し損ねても、黙って健全に見えることはありません。 バッジとの食い違いとして、目に見える形で出ます。

  • ポーリングループの健全性 — スケジューラと受信のカウンタです。各プールがどう処理されて いるかも分かります。作業セットで処理されているか、ジョブを 1 件ずつ配る昔のやり方に落ちて いるかです。後者で、そのプールに生きた登録済みポーラーがいない場合は、琥珀色になります。

  • ホストリソース — コアとすべてのポーラーについて、CPU・ロード・メモリ・ディスク・ネットワーク 通信量を時系列のグラフで表示します。今の値を見るだけでなく、余裕が減っていく様子を追えます。

    ネットワークのカードは、コアとポーラーがバスでやり取りした分と、それ以外にインターフェースを 通った分を分けて描きます。

ディスクのグラフがどのファイルシステムを見るかは、プロセスごとに YAGRA_DISK_WATCH_PATHS で 決めます。書き方は path または path=alias をカンマで並べる形です(既定は /=root)。alias が 系列のラベルになります。詳しくは設定リファレンスにあります。

どちらのバイナリも、構造化ログ(Rust の tracing 形式)を stdout に書き出します。ですから保存と 転送は、コンテナのランタイムが受け持ちます。

Terminal window
docker compose logs -f core
docker compose logs -f poller

詳細度は標準の RUST_LOG 変数(既定 info)でフィルタします。コンポーネント単位の指定も 可能です。

Terminal window
RUST_LOG=yagra-core=debug,yagra-poller=debug

ログの行に認証情報が入ることはありません。SNMP コミュニティ、SNMPv3 の認証情報、API トークンは、 ログでも、API のレスポンスでも、メトリクスのラベルでも、同じように伏せられます。

docker logs を読むには、ホスト上のシェルが必要です。しかし閉じた環境では、それがまさに許可され ていません。そのままだと、パニックや OOM の痕跡が一切残りません。

そこでコアは、stdout に加えて YAGRA_LOG_DIR へもログを書きます。形式は 1 時間ごとの JSON Lines ファイルで、YAGRA_LOG_RETAIN_HOURS 本ぶん残します(既定は 48 本で、古いものは自動で 消えます)。

このディレクトリを名前付きボリュームに置いてください。ログがコンテナより長生きします。ですから 復旧したあとに取ったサポートバンドルにも、落ちた実行のログが入ります。

同梱の compose ファイルでは、既定で有効です。無効にしたい場合は、.env で YAGRA_LOG_DIR を空に してください。書き込みは待たずに進む方式です。ポーリングループを止めるくらいなら、ログのほうを 落とします。ディレクトリに書けない場合も、起動には失敗しません。警告を出して、stdout だけに戻ります。

ポーラーも同じファイルを書きます。そして v0.2.12 から、バンドルはそれも運びます。機器を実際に walk しているのはポーラーなので、そのログが無いと収集した側の痕跡が 1 行も残らないからです。 同梱の compose ファイルは、ポーラーの出力先を /var/log/yagra/pollers に向けます。これはコアが 読み戻すボリュームのサブディレクトリです。リモート拠点のポーラーは自分専用のボリュームに書き、 コアから要求されたときに一定期間分をバス経由で送ります。ポーラーの YAGRA_LOG_DIR を空にすると stdout のみになり、バンドルはその拠点を待たずに「未収録」と記録します。

外部から届かない環境では、診断でいちばん厄介なのが「全体像を 1 つにまとめて外へ出すこと」です。

設定 ▸ サポートバンドルから、サポートバンドルをダウンロードできます。 GET /api/v1/system/support-bundle?since_hours=N&node_id=<uuid> でも取れます。中身は次のとおりです。

  • いま実際に動いているバイナリの正体(バージョンだけでなく、イメージの source ref とビルド プロファイルも)
  • Yagra ヘルスの全セクション
  • 許可リスト方式で選んだ環境変数
  • チェックサム付きの、適用済みマイグレーション一覧
  • テーブルごとのサイズと接続数
  • アクティブなアラート
  • 監査ログの末尾
  • コアの Prometheus スクレイプ
  • ローテートしたログ — コア自身のもの、コアと同じホストにいるポーラーのもの、そして収集の 締め切りまでにバス経由で答えたリモート拠点のポーラーのもの
  • node_id を付けた場合は、そのノード 1 台についての node/ 節 — 登録内容と担当ポーラー、 保存されているインタフェースの行そのもの、収集する設定になっているメトリクス、そのうち実際に 届いているもの、そしてアラート

これは、データを軽々しく外へ出せない現場のための機能です。ですからアーカイブは、外に出す前に 中身を確認できるように作ってあります。全エントリが JSON かプレーンテキストです。MANIFEST.json には、何を入れたかに加えて、何を意図的に外したかが理由付きで書いてあります。

秘匿値の扱いは 2 段構えです。

環境変数は、許可リストで選びます。パスワードらしい名前を弾く拒否リストでは足りません。実際に 漏れるのは YAGRA_DATABASE_URL の userinfo に埋まった認証情報で、拒否リストはそれを取りこぼすから です。

そのうえで、組み立てた全バイトを走査します。認証情報が見つかったら、伏字にするのではなく書き出し そのものを中止します。想定していない経路で入り込んだものも捕まえるためです。中止したときは 500 support_bundle_redaction_failed を返し、どのファイルのどのルールかを添えます(値そのものは 返しません)。

生成には ManageConfig + ManageCredentials + ViewAudit の 3 つすべてが必要です。監査ログや認証 情報のレポートを、そのどちらの名前も付いていないエンドポイント経由で読む抜け道にしないためです。 実質的には、管理者だけが使えるということになります。

トレーシングは、有効にするかどうかを自分で選ぶ機能です。YAGRA_OTEL_ENDPOINT(または標準の OTEL_EXPORTER_OTLP_ENDPOINT)に OTLP/HTTP のコレクタを設定すると、コアとポーラーの両方がスパンを 送り出します。

設定しなければ、トレーシングによる負荷はゼロです。出るのはログだけで、単一ノードのスタックに コレクタは要りません。

得られるのは、プロセスをまたいだ「ポーリング 1 回ぶん」のトレースです。コアのディスパッチ → ポーラーのプローブ → コアの結果取り込み、と並びます。ノースバウンド API のリクエストごとにも スパンが出ます。

トレースの文脈は、ジョブと結果に相乗りしてバスを流れます。トレーシングが無効なら、通信路には そもそも載りません。古いポーラーはそれを無視します。ですからローリングアップグレードの最中でも 安全です。

ローカルで試す。 同梱の compose ファイルには、Jaeger のプロファイルが入っています。

Terminal window
docker compose --profile tracing up

そのうえで docker-compose.yml の core と poller の両方のサービスで YAGRA_OTEL_ENDPOINT: http://jaeger:4318 のコメントを外し、Jaeger UI を http://localhost:16686 で開きます。

規模が大きいときは、間引いてください。 そうしないと、数万ノードがポーリングするたびに、 1 回につき 1 本のトレースが出てしまいます。次を設定します。

Terminal window
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.01

parentbased_* のサンプラは、1 本のトレースについての採否を、コアとポーラーの間で一貫させます。 ですから断片ではなく、まるごと揃ったトレースが 1% 得られます。

本番では、送り先を OpenTelemetry Collector にしてください。そこから自分のトレーシング バックエンド(Tempo、Jaeger、Honeycomb など)へ転送します。

リモート拠点のポーラーには、そこから届くコレクタのアドレスを別途与えてください。トレースの 送信は、NATS のバスの上を流れません。

  • 設定リファレンス — YAGRA_OTEL_ENDPOINT、 YAGRA_DISK_WATCH_PATHS、RUST_LOG、そしてこのページで挙げたその他すべての変数。
  • 高可用性 — 2 コア構成で /readyz とリーダーゲージが どう振る舞うか。
  • ポートとファイアウォール — 8080、9100、そしてコレクタの エンドポイントがファイアウォールルールのどこに収まるか。