EasyTier 异地组网实战:飞牛+阿里云+手机三端 P2P 打洞直连

2026-06-16

背景

我之前一直在用 WireGuard + socat 管理多台服务器的网络互通。这套方案虽然稳定,但维护成本高——每加一台设备就要手动配密钥、写路由规则、管理 iptables。而且 WireGuard 本身没有打洞能力,至少需要一端有公网 IP 才能建立直连。

我的家用宽带是 CGNAT(运营商级 NAT),没有公网 IPv4,手机在 5G 网络下也没有独立 IP。两端都躲在 NAT 后面,传统的端口映射或 WireGuard 直连都行不通。之前不得不依赖一台阿里云服务器做 socat 转发,所有流量都过中转,延迟高不说,还浪费云服务器带宽。

刚好最近想加一台设备进内网,就趁机试了几个组网方案。

为什么选 EasyTier

我评估了三个方案:

  • Tailscale:配置最极简,登录即用,打洞能力强。但控制面是闭源的,所有设备要经 Tailscale 的协调服务器握手,虽然打洞成功后流量不经过它,但协调服务器在国外,偶尔有延迟问题。免费版 100 台设备够用。
  • 节点小宝:主打扫码即用,配置门槛最低。但它是中心化转发架构,P2P 穿透能力弱,很多场景下流量走云端中转。免费版有功能限制,而且是闭源服务。
  • EasyTier:完全开源(GitHub 10k+ Stars),Rust 实现,去中心化设计——没有控制面服务器,节点之间直接通过内置的中继节点握手后建立 P2P 连接。支持 UDP/TCP 打洞,配置只需要一条 docker run 命令。

最终选了 EasyTier。核心原因:开源放心、去中心化架构不需要依赖第三方服务、Rust 写的性能和内存占用都不错。后续如果想改源码或者自己编译特定版本,也完全不受限制。

部署实战

整体架构

拓扑如图,三端的虚拟 IP 都在 10.144.144.0/24 子网内:

  • NAS:10.144.144.1(家中服务端)
  • 阿里云:10.144.144.2(初始连接中继)
  • 扩展节点:10.144.144.3(同局域网扩展)
  • 手机:10.144.144.x(移动端)

阿里云是唯一有公网 IP 的节点,充当初始连接的中继。节点之间握手打洞成功后,数据走 P2P 直连,不再经过阿里云。

准备:生成网络凭证

EasyTier 用网络名+密码标识一个虚拟网络,所有节点用相同的凭证加入同一个网络。

这个凭证只需要生成一次,后续所有节点共用。

第一步:飞牛 NAS 上部署(Docker)

我现有的设备用 Docker 管理,直接跑容器:

docker run -d \
  --name easytier \
  --restart unless-stopped \
  --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun:/dev/net/tun \
  -v /etc/machine-id:/etc/machine-id:ro \
  -e TZ=Asia/Shanghai \
  easytier/easytier:latest \
  -i 10.144.144.1 \
  --network-name YOUR_NETWORK_NAME \
  --network-secret YOUR_NETWORK_SECRET \
  --hostname home-nas \
  -p tcp://YOUR_CLOUD_IP:23000

几个关键点:

  • --network host 是必需的——EasyTier 需要接管网络栈来创建虚拟网卡,不能走 bridge 模式
  • --cap-add NET_ADMIN --cap-add NET_RAW 给予容器网络管理权限
  • /dev/net/tun 设备映射必须存在,否则虚拟网卡创建失败
  • -v /etc/machine-id:/etc/machine-id:ro 把宿主机 ID 映射进容器,保证重启后节点 IP 不变

如果你的环境支持 Docker Compose,配置会更清晰:

version: '3'
services:
  easytier:
    image: easytier/easytier:latest
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - /etc/machine-id:/etc/machine-id:ro
    environment:
      - TZ=Asia/Shanghai
    command: >
      -i 10.144.144.1
      --network-name YOUR_NETWORK_NAME
      --network-secret YOUR_NETWORK_SECRET
      --hostname home-nas
      -p tcp://YOUR_CLOUD_IP:23000
    restart: unless-stopped

第二步:阿里云部署(公网中继节点)

至少需要一个有公网 IP 的节点做初始连接中继。云服务器上的配置略有不同——它不需要主动连接别人,而是被动监听端口:

docker run -d \
  --name easytier \
  --restart unless-stopped \
  --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun:/dev/net/tun \
  -v /etc/machine-id:/etc/machine-id:ro \
  -e TZ=Asia/Shanghai \
  easytier/easytier:latest \
  -i 10.144.144.2 \
  --network-name YOUR_NETWORK_NAME \
  --network-secret YOUR_NETWORK_SECRET \
  --hostname cloud-relay \
  -l tcp://0.0.0.0:23000 -l udp://0.0.0.0:23000

注意这里用的是 -l(listener)而不是 -p(peer)。中继节点的作用是被动等待其他节点连接上来,帮它们完成初始握手。我选的端口是 23000,需要在云平台的安全组中同时开放 TCP 和 UDP 的这个端口。

第三步:手机 App(Android)

手机是最简单的——不需要 Docker,不需要命令行。直接从 GitHub Releases 下载 APK 安装:

  1. 下载 app-arm64-release.apk
  2. 安装后在 App 中新建网络,填入网络名、密码和初始节点地址(tcp://YOUR_CLOUD_IP:23000
  3. 点击 Run Network,同意 VPN 授权即可

手机 App 利用 Android 的 VPN 服务接口创建虚拟网卡,不需要 root,不需要安装 TUN 模块。这是 EasyTier 的一大优势——移动端集成做得比较成熟。

第四步:扩展节点(可选)

如果还有其他设备要加入网络,用同样的 docker run 命令配上不同的虚拟 IP 即可。跟 NAS 端的配置基本一样,只是 IP 换成 10.144.144.3,hostname 改成对应的设备名。

踩坑记录

坑 1:Docker 镜像拉取反复失败

第一次部署时,docker pull EasyTier 镜像在下载镜像层时不断重试后报 EOF 失败。换了几个国内镜像站也一样。

根因:Docker daemon 配置了 HTTP 代理指向本地代理工具,但代理工具对国外 registry 的请求不稳定。大镜像的某些层下载到一半就断了。

解决:有两个思路。一是直接下载 EasyTier 的二进制到宿主机运行,完全绕过 Docker。二是用 hub.rat.dev 等稳定的镜像加速站。最省事的方案是检查 Docker daemon 的 mirror 配置,让 registry 拉取走镜像加速而不是走 HTTP 代理。

如果你的网络环境也有类似问题,建议先 docker pull 确认镜像站是否稳定,再决定要不要改 Docker 配置。

坑 2:-p-l 参数混用

EasyTier 的启动参数中,-p(peer)和 -l(listener)的含义完全不同:

  • -p tcp://IP:PORT:主动连接某个对等节点
  • -l tcp://0.0.0.0:PORT:本机监听某个端口,被动等待连接

我一开始把阿里云的 -l 也写成了 -p,结果中继节点不停地尝试主动连接自己,日志里刷满了连接失败。正确做法:公网中继节点用 -l 被动监听,内网节点用 -p 主动连接中继。

坑 3:中继端口对外开放的安全权衡

把 23000 端口暴露在公网上,一开始是有点犹豫的。但 EasyTier 使用 Noise 协议做端到端加密,网络凭证是 16 位随机 hex 字符串,即使有人连上了 23000 端口,没有凭证也无法加入虚拟网络。

不过端口暴露始终增加了攻击面。如果追求更严格的安全,可以在云服务器上用 iptables 限制只允许已知节点的公网 IP 访问 23000 端口。考虑到我的场景中所有内网节点都会主动连接中继,而不是中继去找它们,这个限制是可行的。

最终我选择了保持开放,因为 Noise 加密 + 随机凭证的组合已经提供了足够的安全边界。这个决策因人而异,看你对暴露端口的容忍度。

坑 4:Docker 权限不足导致启动失败

EasyTier 需要创建 tun 设备来组建 VPN 网络。Docker 容器默认没有这个权限,必须显式授予。缺少以下任一参数都会导致容器启动后立即退出:

  • --network host:共享宿主机网络栈
  • --cap-add NET_ADMIN --cap-add NET_RAW:网络管理权限
  • --device /dev/net/tun:/dev/net/tun:tun 设备映射

如果启动后 docker logs easytier 看到权限相关的错误,优先检查这三个参数是否齐全。

验证结果

所有节点部署完成后,用 EasyTier 自带的 CLI 验证连接状态:

# 查看节点在线情况
docker exec easytier easytier-cli node

# 查看对等连接(重点关注 tunnel 类型)
docker exec easytier easytier-cli peer

# 查看路由表
docker exec easytier easytier-cli route

# 测试连通性
ping 10.144.144.2

关键观察easytier-cli peer 输出的 tunnel 列标识了连接类型。udpudp6 表示 P2P 打洞直连tcp 表示走了中继转发。

我实际测出来的结果:

连接 隧道类型 延迟
NAS ↔ 云服务器 TCP P2P 13ms
NAS ↔ 同局域网设备 TCP P2P <1ms
NAS ↔ 手机(5G) UDP P2P 18-35ms

最让我意外的是 NAS(PortRestricted Cone NAT)和手机(Symmetric NAT)之间成功建立了 UDP P2P 直连。Symmetric NAT 通常被认为是最难打穿的类型,但 EasyTier 成功在这两端之间建立了直连隧道,延迟 18-35ms,打洞后数据不经过云服务器中继。

两个 CGNAT 设备之间能打洞直连,意味着即使两端都没有公网 IP,也能建立低延迟的 P2P 通道。

日常运维

EasyTier 的运维很简单。几个常用命令:

# 查看节点在线状态
docker exec easytier easytier-cli node

# 重启容器
docker restart easytier

容器配置了 --restart unless-stopped,宿主机重启后会自动拉起,不需要额外操作。云服务器的安全组已经配好 23000 端口,其他节点不需要开放任何端口(全部走主动出站连接)。

对比总结

跟之前用的 WireGuard + socat 比,EasyTier 的维护成本低了一大截——不需要手动管理密钥分发、不需要写路由规则、不需要在云服务器上配 socat 转发。加新设备只需要一条 docker run 命令或手机 App 扫码。

跟 Tailscale 比,EasyTier 的配置门槛稍高(Tailscale 登录即用),但胜在完全去中心化——没有依赖第三方协调服务器,所有握手信息走自己的中继节点,网络自治性更强。

如果你的网络环境中至少有一台有公网 IP 的云服务器,又不需要 Tailscale 那种”在限制性网络下仍然几乎必定能打洞”的极致穿透能力,EasyTier 是一个值得尝试的开源替代方案。Rust 实现带来的性能和内存优势,加上简洁的配置模型,让它很适合在 NAS 和云服务器之间搭建私有虚拟网络。