Xray APIでノードを自動切替|StatsService実践設定ガイド

Xray-coreのJSONを編集できる開発者向けに、gRPC APIのStatsServiceとHandlerServiceを使ったノード監視・出力先変更の方法を解説します。通信量やエラーをスクリプトで判定し、障害時の自動フェイルオーバーまで実装できる設定例と運用上の注意点を紹介します。

Xrayには、稼働中のノードやアウトバウンドの通信量をAPIから取得し、条件に応じて利用する出力先を切り替える仕組みがあります。単純に「応答が速いノード」へ毎回変更するのではなく、一定時間ごとの統計値、接続テスト、失敗回数、切り替え間隔を組み合わせることで、頻繁な往復切り替えを防ぎながら運用できます。

この記事では、Xray 1.8.x以降を想定し、ローカルのAPI待受、StatsServiceによる通信量の取得、RoutingServiceのバランサー上書き、障害時の切り戻しを説明します。APIは強力ですが、認証なしで外部公開すると設定情報や通信統計を操作されるおそれがあります。待受アドレスを127.0.0.1に限定し、ファイアウォールでも外部から到達できない状態にしてください。

この記事の要点

複数のVLESSまたはVMessアウトバウンドをあらかじめ登録し、Xray APIのStatsServiceで通信統計を読み取り、接続テストと連続失敗回数を加味してバランサーの選択先を変更する方法を扱います。API設定、スクリプトの判定ロジック、手動切り戻し、ログ確認、アクセス制御までを実運用向けに整理します。

Xray APIで自動切替を行う構成

自動切替は、Xrayコアがすべてを自動判断する機能ではありません。XrayはAPIを通じて統計情報やルーティング状態を提供し、外部スクリプトがその情報を読んで、次の出力先を指定します。基本構成は、複数のノードを持つアウトバウンド、対象通信を受けるルーティングルール、選択対象をまとめるバランサー、そしてAPIを呼び出す監視スクリプトの4要素です。

ノードを事前登録 統計値を取得 疎通を確認 候補を評価 出力先を上書き

StatsServiceは、ユーザーまたはアウトバウンド単位のアップロード量とダウンロード量を取得するために使います。ただし、通信量が少ないことは、必ずしもノードの遅延が小さいことを意味しません。通信量は負荷の参考値として扱い、HTTPまたはHTTPSの接続テスト、TCP接続時間、連続失敗回数と組み合わせる必要があります。

  • StatsService:アップリンク、ダウンリンク、ユーザー統計などを取得します。
  • RoutingService:テストルートやバランサー情報の確認、バランサー選択先の上書きに利用します。
  • HandlerService:必要に応じてアウトバウンドやインバウンドを追加、変更、削除します。通常は安全性のため、起動時にノードを固定登録する構成を推奨します。
  • 監視スクリプト:一定間隔で測定し、しきい値、クールダウン、切り戻し条件を適用します。
127.0.0.1
APIの推奨待受アドレス
10085
APIでよく使われるローカルポート
30秒
監視間隔の初期値
3回
切替判定に使う連続失敗回数

結論:統計値だけでノードを選ばない

通信量、遅延、接続成功率は意味が異なります。まず疎通失敗を除外し、その後に直近の遅延と負荷を比較する二段階判定にすると、空いているだけで不安定なノードへ切り替える事故を減らせます。

API待受とStatsServiceを設定する

API設定では、apiタグを持つローカルインバウンドを作成し、提供するサービスを明示します。StatsServiceだけでなく、バランサーを切り替える場合はRoutingServiceも必要です。統計を取得する対象は、アウトバウンドタグ、またはユーザー名を含む統計名で指定します。タグは後からスクリプトで利用するため、英数字とハイフンを使い、重複させないでください。

{
  "api": {
    "services": [
      "StatsService",
      "RoutingService"
    ],
    "tag": "api"
  },
  "inbounds": [
    {
      "tag": "api",
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    }
  ],
  "stats": {},
  "routing": {
    "balancers": [
      {
        "tag": "auto-node",
        "selector": [
          "node-"
        ]
      }
    ],
    "rules": [
      {
        "type": "field",
        "network": "tcp,udp",
        "balancerTag": "auto-node"
      }
    ]
  }
}

上の例では、node-で始まるアウトバウンドをauto-nodeというバランサーに登録しています。実際の設定では、outbounds配列にnode-anode-bnode-cなどの完全なノード定義を追加します。各ノードのUUID、サーバーアドレス、TLS、Reality、WebSocketパスなどは、利用中のサブスクリプション情報と一致させてください。

自動切替に向くノード設計を選ぶ

ノードを自動切替する前に、どの範囲を同じバランサーに入れるかを決めます。地域、用途、プロトコルが大きく異なるノードを一つの候補群に混在させると、テスト結果の比較が不公平になります。たとえば通常のWeb通信向けと、UDPを必要とするアプリ向けでは、別のバランサーと判定基準を用意した方が安定します。

起動時に複数のアウトバウンドを読み込み、APIで選択中のタグだけを変更します。構成が予測しやすく、障害時の復元も簡単です。

適合:常用環境、少数ノード、安定運用

HandlerServiceでアウトバウンドを追加、変更、削除する方式です。ノード数を柔軟に管理できますが、設定不整合や削除対象の誤指定に注意が必要です。

適合:短時間の検証、外部管理システムとの連携

v2rayNやv2rayNGの画面からノードを選ぶ方法です。API障害の影響を受けませんが、無人運用や夜間の障害復旧には向きません。

適合:少数端末、原因調査、手動確認

候補ノードには、安定したタグ命名規則を設定します。たとえば、node-tokyo-anode-tokyo-bnode-singapore-aのように地域と識別子を含めると、ログから切替先を追跡しやすくなります。スクリプトでは表示名ではなく、Xray設定のアウトバウンドタグを比較してください。

StatsServiceを読み取り切替する手順

ここでは、APIを有効にしたXrayがすでに起動しており、複数ノードがauto-nodeバランサーに登録されている前提で進めます。最初から自動切替を有効にせず、スクリプトをドライランで動かし、候補順位だけをログへ出力する段階を設けてください。設定ミスで短時間に何度もノードが切り替わると、既存のTCP接続が切断され、原因調査も難しくなります。

  1. APIをローカル起動

    Xrayを再起動し、ログにAPIインバウンドの初期化成功が出ることを確認します。127.0.0.1:10085がLISTEN状態であり、別プロセスが同じポートを使っていないことを確認してください。

  2. 統計名を確認

    アウトバウンドタグに対応する統計を照会します。タグにハイフンや地域名を含める場合、APIに渡す統計名の形式が実際のXrayバージョンと一致するか、まず1ノードだけで確認します。

  3. 疎通を測定

    各候補へ同じHTTPSエンドポイントから接続テストを行います。DNSキャッシュやブラウザーキャッシュの影響を避け、HTTPステータス、接続時間、TLSエラー、タイムアウトを別々に記録します。

  4. 候補をスコア化

    連続失敗が3回に達したノードを一時除外し、成功した候補の中央値遅延と直近通信量を比較します。最速との差が10%未満なら、現在のノードを維持して不要な切替を抑えます。

  5. 選択先を変更

    十分なクールダウン時間を置いてからRoutingServiceのバランサー上書きを実行します。変更後は、現在の選択タグ、Xrayログ、実際のHTTPS接続を確認し、成功した場合だけ状態ファイルを更新します。

# 概念的な監視ループ
while true:
    stats = query_stats("127.0.0.1:10085")
    results = test_each_node(["node-tokyo-a", "node-tokyo-b"])
    candidates = remove_nodes_with_three_failures(results)
    target = choose_by_latency_and_load(candidates)

    if target != current and cooldown_elapsed():
        override_balancer("auto-node", target)
        verify_active_route(target)

    sleep(30)

実装方法は、Xray APIのgRPC定義を利用するPythonやGoのクライアント、または環境に導入したgRPC操作ツールから選べます。重要なのは、API呼び出しが成功したことだけで切替完了と判断しないことです。OverrideBalancerの応答が正常でも、対象タグがバランサーのセレクターに含まれていない、ノード自体が起動していない、ルーティングルールが別のアウトバウンドを選んでいる、といった理由で実通信が変わらない場合があります。

障害時の切り戻しと安定化

自動切替では、障害が起きたときの切り替え条件よりも、元へ戻す条件の方が重要です。ネットワークが一時的に遅いだけなのにノードを除外し続けると、すべての候補が短時間で不健康と判定されます。各ノードに「失敗回数」と「最後に成功した時刻」を保存し、少なくとも5分程度は再評価期間を設けてください。

  • 即時切替:TCP接続拒否やTLSハンドシェイク失敗が3回連続し、別ノードのテストが成功した場合。
  • 維持:遅延差が10%未満、または測定回数が3回未満の場合。データ不足で切り替えません。
  • 切り戻し:新しいノードで2回連続失敗し、直前のノードが再び2回成功した場合。
  • 全滅:候補がすべて失敗した場合は、最後に成功したタグを保持し、15分間は自動切替を停止してログを通知します。

切り戻しの基準を別に持つ

切替条件を「3回失敗」、復帰条件を「2回成功」と分けると、回線が回復した直後に無期限で待機する状態を避けられます。ただし、復帰時も最低5分のクールダウンを置き、短時間の揺らぎを無視してください。

切り替え後に既存接続がそのまま維持されるかどうかは、通信方式と接続状態に依存します。バランサーの選択先を変更しても、すでに確立したTCPセッションが別のノードへ移動するわけではありません。新しい接続から切替先が利用されるため、測定には新規接続を使い、長時間接続中のアプリだけで成功・失敗を判定しないでください。

ログ、権限、運用時の確認項目

監視スクリプトは、少なくとも測定時刻、候補タグ、接続時間、失敗理由、現在の選択先、変更前後のタグ、API応答結果を記録します。パスワード、UUID、Realityの秘密鍵、サブスクリプションURLをログへ書き出してはいけません。Xrayのログレベルを長期間debugにすると、通信量や処理負荷が増える場合があるため、通常運用はwarningまたはinfoを基本にし、調査時だけ一時的に詳細化します。

rpc error: code = Unavailable desc = connection refused

原因と対処:APIインバウンドが起動していない、ポート番号が違う、またはXrayが再起動中です。Xray設定のAPIタグと127.0.0.1:10085の待受状態を確認してください。

failed to find stats channel for outbound

原因と対処:指定した統計名またはアウトバウンドタグが実際の設定と一致していません。タグの大文字小文字、接頭辞、統計名の形式を確認し、1つのノードから照会を試します。

balancer has no available outbounds

原因と対処:セレクターに一致するアウトバウンドがない、または動的削除で候補を失っています。固定登録構成へ戻し、node-で始まるタグとbalancerのselectorを照合してください。

APIポートがローカル限定でも、監視スクリプトの実行権限は最小限にしてください。設定ファイルの読み取り権限とAPIへの接続権限を分離できる環境では、専用ユーザーでスクリプトを実行します。外部の監視サーバーから直接APIへ接続するのではなく、端末側のエージェントが結果だけを送る構成にすると、API操作面をネットワークへ公開せずに済みます。

動作確認は、正常時、1ノードだけのタイムアウト、API停止、全ノード失敗、Xray再起動後の5パターンで行います。各テストで「現在の選択タグ」「新規接続の出口」「ログの切替記録」「切り戻しの発生」を照合してください。自動化は便利ですが、ノード設定そのものの誤り、期限切れの認証情報、サブスクリプション更新失敗を修正する機能ではありません。まず手動選択で各ノードが利用できることを確認し、その後にStatsServiceと自動判定を追加するのが安全です。

クライアントをダウンロード