# 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 ## 如何确认 查看服务统计 ```shell docker run --rm --network web-network curlimages/curl:8.16.0 -fsS http://dht-search:8080/stats ``` 重点比较以下字段 ```text metadata_ok metadata_failed metadata_failure_connect metadata_failure_timeout metadata_in_flight ``` 查看宿主机和 Xray socket ```shell 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 ```shell docker exec dht-search cat /proc/net/sockstat docker exec dht-search sh -c 'ls /proc/1/fd | wc -l' ``` 查看透明代理规则 ```shell nft list ruleset ip rule show ``` 如果存在类似规则 Docker 公网流量会进入 Xray ```nft meta l4proto { tcp, udp } tproxy to :12345 meta mark set 1 accept ``` ## 推荐解决方案 让 DHT 同时连接反向代理网络和独立直连网络 ```yaml 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 网络冲突 ```shell ip route show 172.30.0.0/24 docker network inspect dht-direct ``` ## 配置 Xray 来源旁路 在 Xray 持久化 nftables 文件中增加来源集合 ```nft set bypass_source_ipv4 { type ipv4_addr flags interval elements = { 172.30.0.0/24 } } ``` 在 `prerouting` 链进入 TProxy 之前增加 ```nft 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 会短暂中断现有代理连接 应选择允许短暂中断的时间执行 ## 验收 确认容器同时加入两个网络且没有宿主机端口绑定 ```shell 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_ok` 和 `persistence_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