Files
dht/docs/xray-transparent-proxy.md
2026-08-10 19:34:30 +08:00

4.9 KiB

Xray 透明代理与 DHT 直连处理

本文只适用于宿主机启用了 Xray TProxy 或其他全局透明代理的特殊部署环境

普通 Docker 主机直接使用根目录 compose.yaml 不需要这里的专用网络或 nftables 规则

为什么会发生

DHT 会向大量公网节点发送 UDP 查询 并尝试连接大量 Peer 的随机 TCP 端口

全局 TProxy 如果在 prerouting 中接管所有公网 TCP 和 UDP Docker 容器流量也会进入 Xray

Xray 即使最终选择直连出站仍然需要维护这些 UDP 会话 因此可能积累大量 socket 并让 Metadata TCP 连接失败

典型现象

  • DHT 节点 Peer 和采样 infohash 持续增长
  • Metadata 成功长期为零或远低于正常水平
  • Metadata 失败几乎全部是连接失败
  • Xray 文件描述符和宿主机 UDP socket 持续增长
  • SSH 或其他新连接开始偶发断开
  • dht-search 容器自身文件描述符并不高

仅看到 Metadata 超时不能直接判断为 Xray 问题 公网 DHT 本身就包含大量不可达 Peer

如何确认

查看服务统计

docker run --rm --network web-network curlimages/curl:8.16.0 -fsS http://dht-search:8080/stats

重点比较以下字段

metadata_ok
metadata_failed
metadata_failure_connect
metadata_failure_timeout
metadata_in_flight

查看宿主机和 Xray socket

cat /proc/net/sockstat

xray_pid="$(pidof xray)"
find "/proc/${xray_pid}/fd" -maxdepth 1 -type l | wc -l
grep "Max open files" "/proc/${xray_pid}/limits"

查看 DHT 容器自身 socket

docker exec dht-search cat /proc/net/sockstat
docker exec dht-search sh -c 'ls /proc/1/fd | wc -l'

查看透明代理规则

nft list ruleset
ip rule show

如果存在类似规则 Docker 公网流量会进入 Xray

meta l4proto { tcp, udp } tproxy to :12345 meta mark set 1 accept

推荐解决方案

让 DHT 同时连接反向代理网络和独立直连网络

networks:
  web-network:
    external: true
    name: web-network
  dht-direct:
    name: dht-direct
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/24

services:
  dht-search:
    networks:
      web-network:
      dht-direct:
        gw_priority: 1

web-network 只负责 Caddy 到 dht-search:8080

dht-direct 具有更高网关优先级并负责 DHT 和 Peer 公网流量

部署前必须确认 172.30.0.0/24 没有与宿主机路由或其他 Docker 网络冲突

ip route show 172.30.0.0/24
docker network inspect dht-direct

配置 Xray 来源旁路

在 Xray 持久化 nftables 文件中增加来源集合

set bypass_source_ipv4 {
    type ipv4_addr
    flags interval
    elements = {
        172.30.0.0/24
    }
}

prerouting 链进入 TProxy 之前增加

ip saddr @bypass_source_ipv4 return

必须修改 Xray 服务启动时实际加载的持久化文件 只向当前运行规则临时执行 nft add rule 会在重启后失效

安全实施顺序

  1. 使用 docker compose stop 优雅停止 DHT
  2. 备份 Compose 和 Xray nftables 文件
  3. 增加 dht-direct 网络和来源旁路
  4. 使用 docker compose config --quiet 校验 Compose
  5. 使用 nft -c -f <规则文件> 校验 nftables
  6. 重启 Xray 释放已经积累的旧会话
  7. 使用 docker compose up -d 重新创建 DHT 容器
  8. 验证默认网关 Metadata 成功率和 socket 数量

重启 Xray 会短暂中断现有代理连接 应选择允许短暂中断的时间执行

验收

确认容器同时加入两个网络且没有宿主机端口绑定

docker compose ps
docker inspect dht-search --format '{{json .HostConfig.PortBindings}}'
docker inspect dht-search --format '{{json .NetworkSettings.Networks}}'
docker exec dht-search cat /proc/net/route

默认网关应该属于 dht-direct 然后持续观察

  • metadata_okpersistence_inserted 开始增长
  • Metadata 失败不再全部是连接失败
  • Xray 文件描述符不再随 DHT 流量快速增长
  • 宿主机 UDP socket 恢复到正常范围
  • Caddy 仍能通过 web-network 访问服务

实际案例

一次远端 Debian 部署中修复前 Metadata 尝试超过一百万次但成功为零 Xray 文件描述符增长到约三十九万 主机 UDP socket 超过三十三万

增加独立直连网络并重启 Xray 后两分钟内 Metadata 成功 1222 条并入库 1218 条 Xray 文件描述符稳定在 12 主机 UDP socket 稳定在 6

不建议的处理

  • 只提高 Xray LimitNOFILE
  • 只增加机器内存
  • 只降低 Metadata 并发
  • 让 DHT 流量进入 Xray 后再选择 direct outbound

这些方法只能延迟资源耗尽或仍然让 Xray 维护海量 UDP 会话 不能消除错误流量边界

回滚

如果专用网络启动失败

  1. 停止 DHT 容器
  2. 恢复原 Compose 和 nftables 备份
  3. 校验并重启 Xray
  4. 保持 DHT 停止直到重新确认路由方案

不要在恢复了全局 TProxy 且没有来源旁路的情况下重新启动高并发 DHT