上級テクニック 読了目安 18分

Xray APIで実現するノード監視と自動フェイルオーバー設定

Xray-coreのgRPC APIを使い、プロキシノードの遅延測定、死活監視、トラフィック統計、自動切替を構築する実践ガイドです。StatsServiceとHandlerServiceの使い分けから、貼り付けて使えるJSON設定、スクリプト、systemd運用、ログ解析、復旧処理までを開発者向けに詳しく解説します。

Xrayで複数のノードを運用していると、単純に「接続できるか」だけを確認する構成では不十分です。TCPポートが開いていても、TLSやREALITYのハンドシェイクに時間がかかる、接続後にパケットロスが増える、特定の宛先だけ応答しない、といった状態は発生します。このようなノードを手動で切り替える代わりに、Xray APIから統計情報を取得し、一定回数の失敗や高遅延を検出したら、次のノードへ経路を変更する仕組みをLinuxサーバー上に構築できます。

この構成では、Xrayのデータプレーンと監視スクリプトの役割を分けます。Xrayはクライアントからの通信を受け付け、複数のアウトバウンドを管理します。監視スクリプトはAPIサービスから状態を取得し、ノードの検査、障害判定、設定ファイルの更新、設定テスト、サービスの再読み込みを担当します。APIポートを外部へ公開せず、ローカルホストまたは管理用ソケット相当の閉じた経路だけで利用することが、安全な運用の基本です。

この記事の要点

Linux上のXrayでAPIサービスを有効にし、StatsServiceによる通信統計と外部のヘルスチェックを組み合わせて、障害ノードを隔離し、正常化したノードを段階的に復旧させる方法を説明します。Xray APIだけで万能な品質判定を行うのではなく、設定変更の検証、切り替え間隔、復旧条件、ログと権限まで含めて設計します。

自動フェイルオーバーの全体設計を先に決める

最初に決めるべきなのは、どの通信を監視し、どの単位で切り替えるかです。1つのプロキシアウトバウンドだけを定義して監視スクリプトがその中身を書き換える方法は、設定が単純に見える一方、切り替え時に既存接続へ影響しやすくなります。より安全なのは、proxy-node-aproxy-node-bproxy-node-cのようにノードごとにタグを分け、ルーティング側に通常経路と予備経路を明示する構成です。

監視タイマー起動API統計取得外部ヘルスチェック障害状態を判定設定を検証経路を再読み込み

監視対象は、少なくとも次の3層に分けて考えます。第1層はプロセスの稼働状態です。Xrayプロセスが停止している、APIポートが応答しない、設定読み込みに失敗している場合は、ノードの品質以前にサービス障害として扱います。第2層はノードへの到達性です。対象ホストの名前解決、TCPポート、TLSまたはREALITYのハンドシェイクを確認します。第3層は実通信の品質です。プロキシ経由のHTTPSリクエスト、応答時間、連続失敗回数を確認し、単なる一時的な遅延と継続的な障害を区別します。

フェイルオーバーの判定を1回の失敗だけにすると、混雑や短時間のパケットロスで頻繁に切り替わります。例えば、30秒間隔で検査し、3回連続して失敗した場合に隔離、復旧側は5回連続成功かつ平均遅延が500ミリ秒未満の場合に候補へ戻す、といった異なるしきい値を設定します。障害にする条件と復旧にする条件を同じにしないことが、フラッピング防止に有効です。

10085
API待受ポート例
30秒
監視間隔例
3回
連続失敗で隔離
500ms
復旧候補の遅延目安

結論:切り替えより隔離条件を先に決める

自動化の品質は、切り替え処理の速さよりも、短時間の揺らぎを障害と誤認しない判定に左右されます。失敗回数、検査間隔、クールダウン、復旧回数を最初に固定してください。

Xray APIサービスを安全に有効化する

XrayのAPIは、すべての機能を1つのHTTPエンドポイントで提供する仕組みではありません。APIインバウンドを用意し、必要なサービスをapi.servicesに登録して、gRPC経由で統計取得やハンドラー操作を行います。監視だけなら、まずStatsServiceを中心に構成し、設定の追加・削除や動的なハンドラー変更が必要な場合だけHandlerServiceを追加します。API用のインバウンドを通常のプロキシ通信と同じポートへ混在させないでください。

APIインバウンド

タグ
api
プロトコル
dokodemo-door
待受
127.0.0.1:10085
ネットワーク
tcp

外部NICでは待ち受けず、監視プロセスと同じホストからだけ接続します。

APIサービス

統計
StatsService
経路操作
RoutingService
ハンドラー操作
HandlerService
ログ操作
LoggerService

利用しないサービスを登録せず、権限と監査範囲を小さくします。

設定ファイルの骨格は次のようにできます。既存のinboundsoutboundsroutingを削除して置き換えるのではなく、現在の設定へ必要なオブジェクトを統合してください。API用のポートが既に別のプロセスで使われていないことも確認します。

{
  "api": {
    "tag": "api",
    "services": [
      "StatsService",
      "RoutingService",
      "HandlerService"
    ]
  },
  "inbounds": [
    {
      "tag": "api",
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    }
  ],
  "stats": {},
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  }
}

StatsServiceで有用な値を得るには、統計対象のインバウンドまたはアウトバウンドに対応するタグが必要です。タグ名は設定内で一貫させ、監視スクリプトに直接書く場合も、サーバー名や表示名ではなく機械的に変化しない識別子を使います。XrayのバージョンによってAPIコマンドの表示や利用できるサービスに差があるため、設定反映後は必ず実行中のバイナリで構文テストを行います。

sudo xray run -test -config /etc/xray/config.json
sudo systemctl restart xray
sudo systemctl status xray --no-pager
sudo journalctl -u xray -n 50 --no-pager

ノードJSONとルーティングを切り替え可能な形にする

自動切り替えでは、各ノードのJSONを「人間が読む設定」と「スクリプトが操作する設定」に分離すると管理しやすくなります。表示名、地域、回線種別などはコメントで補足したくなりますが、JSON標準にはコメントがありません。運用ではnodes.jsonを入力データ、生成後のconfig.jsonをXrayが読む設定として扱い、生成前に必ずJSON構文を検証します。

推奨方式:固定タグと状態ファイルを分離する

ノード定義
  • 固定されたアウトバウンドタグ
  • アドレスとポート
  • UUID、SNI、flow
  • トランスポートとTLS設定
監視状態
  • 連続失敗回数
  • 最後の成功時刻
  • 隔離期限
  • 現在の優先ノード

ノードの認証情報を状態ログへ書き出さず、設定と判定履歴の責任範囲を分けます。

例えば、VLESSとREALITYを使うノードは、アドレス、ポート、UUID、flow、serverName、publicKey、shortId、fingerprintを一致させる必要があります。VMessとWebSocket、TLSを使うノードなら、ユーザーID、WebSocketのパス、ホストヘッダー、TLSのサーバー名を別の形式で保持します。VLESSのフィールドをVMessへ流用したり、REALITYの公開鍵を通常のTLS設定へ記述したりすることはできません。

{
  "tag": "proxy-node-a",
  "protocol": "vless",
  "settings": {
    "vnext": [{
      "address": "node-a.example.net",
      "port": 443,
      "users": [{
        "id": "00000000-0000-0000-0000-000000000000",
        "encryption": "none",
        "flow": "xtls-rprx-vision"
      }]
    }]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "reality",
    "realitySettings": {
      "serverName": "www.example.com",
      "fingerprint": "chrome",
      "publicKey": "REPLACE_WITH_PUBLIC_KEY",
      "shortId": "REPLACE_WITH_SHORT_ID"
    }
  }
}

経路の選択は、固定のデフォルトアウトバウンドを直接書き換えるより、現在有効なタグを管理する方法が分かりやすくなります。小規模な構成では、監視スクリプトがテンプレート内のactive-outboundを更新して設定を再生成します。より高度な構成では、XrayのRoutingServiceHandlerServiceを使って実行中の設定を操作できますが、利用可能なAPIメソッドやコアバージョンを事前に確認してください。APIから変更できることと、通信中の既存接続が新しいノードへ移ることは同じではありません。多くの場合、既存フローはそのまま残り、新規接続から新しい経路が使われます。

Linuxで監視スクリプトと切り替え処理を動かす

監視スクリプトは、検査、判定、設定変更を1回の処理として直列化します。複数のタイマー実行が重なると、1つのプロセスがノードAを隔離している間に、別のプロセスが古い状態からノードAを復帰させる可能性があります。ロックファイルを使用し、前回の実行が残っている場合は終了させてください。シェルだけでJSONを書き換えるとエスケープ処理が複雑になるため、実際の設定生成にはPythonなどのJSON対応ツールを使うほうが安全です。

  1. APIを確認する

    127.0.0.1:10085の待受、StatsServiceの有効化、Xrayのログを確認します。APIポートが外部アドレスで待ち受けていないことも確認してください。

  2. ノードを検査する

    各ノードへプロキシ経由のHTTPSリクエストを送り、HTTPステータス、接続時間、TLSエラー、タイムアウトを記録します。TCPポートだけの検査を合格条件にしません。

  3. 状態を更新する

    失敗回数、成功回数、最終検査時刻、隔離期限を状態ファイルへ保存します。認証情報やサブスクリプションURLは状態ファイルへ書き出さないでください。

  4. 候補を選ぶ

    隔離中でなく、連続成功回数と遅延条件を満たすノードから優先順位に従って選びます。現在のノードを無条件に毎回再選択しないことが重要です。

  5. 設定を検証する

    一時ファイルへ生成してxray run -test -configを実行し、成功した場合だけ本番ファイルへ置き換えます。その後にサービスをリロードまたは再起動します。

外部検査の例は次のような考え方です。実際のノードアドレスや認証情報をスクリプトへ直接埋め込まず、Xrayのローカルプロキシポートを通してテスト対象URLへアクセスします。ローカルHTTPプロキシが127.0.0.1:10809、検査先が管理下のHTTPS URLである場合、次のコマンドでおおよその応答時間を測定できます。

curl --proxy http://127.0.0.1:10809 \
     --connect-timeout 8 \
     --max-time 15 \
     --silent --show-error --output /dev/null \
     --write-out '%{http_code} %{time_connect} %{time_starttransfer}\n' \
     https://health.example.net/ping

ただし、ローカルプロキシの現在の出口だけを検査すると、候補ノードごとの差を測定できません。候補ごとの専用ローカルポートを用意するか、APIまたは設定生成によって検査対象のアウトバウンドを明示的に指定する必要があります。監視スクリプトが通常の利用トラフィックと同じ経路を使っている場合、検査自身が障害ノードの影響を受けるため、管理用の直接接続や別経路を残しておくと切り分けやすくなります。

設定を置き換えるときは、直接上書きせず、一時ファイル、構文検証、バックアップ、原子的な置換の順で処理します。systemdのサービス再起動が必要な構成では、短い間隔で何度も再起動しないようクールダウンを設けます。Xrayの再起動で全接続が切れる環境では、動的APIによるアウトバウンド操作を検討できますが、変更後の状態確認とロールバック方法を先に作ってください。

#!/usr/bin/env bash
set -euo pipefail

CONFIG=/etc/xray/config.json
TEMP=/etc/xray/config.json.next
LOCK=/run/lock/xray-failover.lock

exec 9>"$LOCK"
flock -n 9 || exit 0

python3 /usr/local/libexec/render-xray-config.py \
  --state /var/lib/xray/failover-state.json \
  --output "$TEMP"

xray run -test -config "$TEMP"
install -o root -g root -m 0640 "$TEMP" "$CONFIG"
systemctl reload xray || systemctl restart xray

ここで重要なのは、systemctl reloadが使用中のXrayサービス定義で本当に設定を再読み込みするかを確認することです。サービスファイルによってはreloadが未定義で、コマンドが成功したように見えても実際には新設定が反映されない場合があります。反映後はjournalctlで起動結果を確認し、APIの統計カウンターや検査用リクエストで選択中のノードが期待どおりか確認してください。

障害ノードの隔離と復旧を安定させる

障害ノードを検出したら、すぐに削除するのではなく、状態を「active」「suspect」「quarantined」「recovering」のように段階化します。suspectは失敗が1回または2回発生した状態、quarantinedは新規接続への選択対象から外した状態、recoveringは一定時間後に検査を再開している状態です。こうすると、一時的なエラーで設定全体を書き換える回数を減らせます。

状態ファイルから有効ノードを反映したJSONを生成し、設定テスト後にXrayへ適用します。監査、再起動後の再現性、ロールバックを管理しやすい方式です。

適合:少数ノード、systemd管理、変更履歴を残したい環境

HandlerServiceやRoutingServiceを利用し、実行中のコアへ変更を送ります。再起動を減らせる可能性がありますが、APIメソッドとコアバージョンの確認が必要です。

適合:変更頻度が高く、API互換性を検証済みの環境

設定ファイルを変更してXrayを再起動するだけの方式です。実装は容易ですが、頻繁な切り替えでは既存接続を切断し、短時間の通信断が発生します。

適合:検証環境、切断を許容できる小規模サービス

復旧時は、障害ノードをいきなり最優先へ戻さないでください。まず隔離解除後に低い優先度の候補として検査し、例えば5回連続成功、TCP接続時間300ミリ秒未満、HTTPS応答時間500ミリ秒未満という条件を満たした場合だけ通常候補へ戻します。復旧直後に本番経路へ固定すると、障害が再発した際に再び全通信へ影響します。

API統計は、検査結果と組み合わせて使います。Xrayの統計値はアップリンク、ダウンリンク、インバウンド、アウトバウンドなどの通信量を示すため、値が増えていることはトラフィックが流れた証拠になります。しかし、通信量が増えているだけでノードの品質が良いとは限りません。大量の再送や遅いレスポンスでもカウンターは増加します。したがって、統計値は「経路が使われているか」の確認に使い、遅延と成功率は実リクエストで判定します。

APIに接続できるのに統計値が増えません

StatsServiceだけでなく、policyの統計設定、対象アウトバウンドのタグ、実際の通信経路を確認します。設定変更後にXrayを再起動していない場合や、タグ名が一致していない場合も値は期待どおりに表示されません。

1回のタイムアウトで切り替えてもよいですか

通常は避けてください。30秒間隔で3回連続失敗を隔離条件にし、復旧は5回連続成功とするなど、失敗と復旧に異なるしきい値を設定します。重要な業務通信では、さらにクールダウンを追加します。

APIで切り替えたら既存接続も移動しますか

多くの場合、新しい接続から新しいアウトバウンドが選ばれます。既存のTCPやTLSセッションが自動的に別ノードへ移るとは考えないでください。切り替え後も古い接続が残る場合は、アプリ側の再接続や接続タイムアウトを確認します。

APIポートを管理端末から操作したい場合は?

まずSSHトンネルや限定した管理ネットワークを使い、Xray APIを直接インターネットへ公開しないでください。アクセス元をファイアウォールで限定し、APIサービスを必要最小限にして、操作ログを保存します。

ログ、ロールバック、長期運用の確認項目

自動化は、切り替えに成功するだけでなく、なぜ切り替わったかを後から説明できなければなりません。少なくとも検査時刻、ノードタグ、検査先、HTTPステータス、接続時間、失敗理由、連続失敗回数、選択変更の前後を記録します。UUID、秘密鍵、サブスクリプションURLなどの認証情報はログへ出力しません。設定ファイルのバックアップを残す場合も、所有者とパーミッションを確認してください。

異常時には、まず自動化を止めて現在の状態を固定します。次にXrayのプロセス、API待受、設定テスト、アウトバウンドのタグ、外部ヘルスチェックの順に確認します。すべてのノードが同時に失敗しているなら、個別ノードの隔離を繰り返すのではなく、上流回線、DNS、時刻同期、ローカルファイアウォール、サーバー全体の負荷を調べます。逆に1つのノードだけが失敗しているなら、そのタグのJSON、リモートポート、SNI、UUID、REALITYパラメータ、サーバー側の変更を比較します。

Xray APIによる自動フェイルオーバーは、ノードを魔法のように高速化する機能ではありません。APIは状態を取得し、運用者が定義した基準に従って経路変更を実行する制御面です。監視対象、JSONの責任範囲、切り替え条件、復旧条件、権限、ロールバックを分離して設計すれば、ノード障害や高遅延が発生したときも、手動操作に頼らず影響時間を短縮できます。

v2rayN をダウンロード