一、引言
上一篇用 keynetworks/ztncui(ztncui-aio)搭了私有 Planet,建网、授权一直够用。等到要给不同成员分访问权限,ztncui 就不够用了,于是换成 ZTNet。这篇记录更换的过程,以及换完需要注意的地方。二、正文
(一)先上 Docker Compose
三个服务:postgres、zerotier、ztnet,和我的实际环境一样。建好目录,写一个 .env,密码和密钥别提交进仓库。NEXTAUTH_SECRET 用 openssl rand -hex 32 生成,漏了 Compose 直接不给 ztnet 启动。NEXTAUTH_URL 我填的 http://10.10.2.1:3000;走 HTTPS 反代就换成不带端口的 https:// 域名。
POSTGRES_USER=postgres
POSTGRES_PASSWORD=请替换为高强度数据库密码
POSTGRES_DB=ztnet
NEXTAUTH_URL=http://10.10.2.1:3000
NEXTAUTH_SECRET=<通过 openssl rand -hex 32 生成的随机值>NEXTAUTH_URL_INTERNAL 供容器在 Compose 网络内访问 ZTNet。ZT_OVERRIDE_LOCAL_CONF=true 会让 ZeroTier 容器重建 local.conf,使 ZT_ALLOW_MANAGEMENT_FROM 指定的管理网段配置生效;数据由绑定挂载目录持久化。br0 是我这边已有的外部 Docker 网络,创建方法不在这儿展开,你按自己的网络名和地址替换即可。
将下面内容保存为 compose.yml,并与 .env 放在同一目录。我的 PostgreSQL 数据目录是 /data/ztnet/postgresql/data;zerotier 与 ztnet 共享 /data/ztnet/zt1 中的 zerotier-one 数据。首次注册的用户会自动成为管理员,之后即可通过 ZTNet 管理既有控制器。
services:
postgres:
image: postgres:15.2-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- /data/ztnet/postgresql/data:/var/lib/postgresql/data
networks:
app-network:
ipv4_address: 10.10.2.3
zerotier:
image: zyclonite/zerotier:latest
restart: unless-stopped
ports:
- "9993:9993/udp"
cap_add:
- NET_ADMIN
- SYS_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
ZT_OVERRIDE_LOCAL_CONF: "true"
ZT_ALLOW_MANAGEMENT_FROM: 10.10.0.0/16
volumes:
- /data/ztnet/zt1:/var/lib/zerotier-one
networks:
app-network:
ipv4_address: 10.10.2.2
ztnet:
image: sinamics/ztnet:latest
restart: unless-stopped
depends_on:
- postgres
- zerotier
ports:
- "3000:3000"
environment:
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
NEXTAUTH_URL: ${NEXTAUTH_URL}
NEXTAUTH_URL_INTERNAL: http://ztnet:3000
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:?请先在 .env 中设置 NEXTAUTH_SECRET}
volumes:
- /data/ztnet/zt1:/var/lib/zerotier-one
networks:
app-network:
ipv4_address: 10.10.2.1
networks:
app-network:
external: true这套 Compose 中,ZT_ADDR 和 ZT_SECRET 保持默认值即可:前者为 http://zerotier:9993,后者使用共享目录中的 authtoken.secret。接入自定义或远程的 ZeroTier 控制器时,才需要另行配置它们。替换好变量后执行 docker compose up -d,再访问 NEXTAUTH_URL 指向的地址;未设置 NEXTAUTH_SECRET 时,上述变量展开会直接阻止启动。我用的 zerotier:latest 和 ztnet:latest 都是浮动标签,上生产可以钉一个验证过的版本。
(二)ztncui 和 ZTNet 对比
ztncui 和 ZTNet 是同一类东西:给自托管 ZeroTier 控制器套的 Web UI,都能建网络、管成员。差别在完整度和维护状态:
| 维度 | ztncui | ZTNet |
|---|---|---|
| 规则与标签 | README 明确写了 rules、tags 编辑未实现 | 可直接编辑 flow rules(rulesSource)、capabilities 和 tags |
| 成员协作 | 单一管理账号 | 多用户、组织、个人空间 |
| 数据存储 | 不需要独立数据库 | 需要 PostgreSQL |
| 控制器范围 | 面向本机 standalone 控制器 | 也可指向远程控制器,或用 ZeroTier Central API |
| 周边能力 | 官方资料未见对应功能 | REST API、Webhooks、备份恢复、多语言(含中文) |
| 维护状态 | 仓库提交 73 次,README 仍要求 Node v14 | 提交 2600 余次,仍在更新,官方标注 BETA |
表格里的提交数是抓取时的量级,会随时间变化。上一篇的私有 Planet 里,ztncui 建网络、批成员都没问题,真正卡住我的是规则和标签改不动——而这恰好是我要经常动的两个配置,只能绕开控制台手动改。ZTNet 把这两块补进了界面,顺带界面也更合我眼缘。官网和仓库分别在 ztnet.network 和 sinamics/ztnet。
两边界面放一起看更直观。先是 ztncui 的成员管理页(官方截图):

再是我现在用的 ZTNet 网络详情页(本机 v0.7.18 界面):

个人认为相比ZTNet,ztncui更复古。
(三)能不能沿用已有Zerotier控制器
ZTNet 的迁移说明里说:两个 ZeroTier 实例不能合并,它们本质上是各自独立的实体;但 ZTNet 可以配置成管理另一个 ZeroTier 实例,不必用它自带的那个。
所以我直接使用了之前创建的 ZeroTier 控制器实例,zerotier-one 数据目录、identity 和 Planet 都不动;ztnet 与 zerotier 两个容器共用同一份 /data/ztnet/zt1,等于 ZTNet 接在老控制器上做前端,全程只有一个实例,不涉及合并。
按官方文档,接进去之后还有个收尾动作:ZTNet 会把数据库里没有归属的网络标成 unlinked,需要在 Admin => Controller 页面把它们指派给用户,网络才算纳入管理。
动手前我把整个挂载目录备份了一份。换完之后网络、成员、控制器身份、Planet 原样都在,客户端什么都不用改,直接就上来了;
(四)异地灾备考量
我考虑过为这套控制器做异地灾备:在主中心之外维护一套备中心,两边运行心跳检测;主中心故障时,备中心自动启动并接管,主中心恢复后再自动切回。
控制器目录的同步交给 Syncthing 即可覆盖,把 /data/ztnet/zt1 同步到备中心。真正卡住的是 PostgreSQL 数据的实时同步:没有找到简单好用的自托管开源方案,现有的要么过于复杂,要么开源版本限制严格;而且选型还要考虑后续可能引入其他数据库的实时备份,不能只为 pg 挑一个。
切换这一层也没走通。官方客户端不支持运行时更换 planet:zerotier-cli 没有相关子命令,替换 planet 文件后必须重启服务才能生效。开源侧也没有现成方案,ZerotierFix 和官方移动端都只支持静态导入 planet,docker-zerotier-planet 的「多 planet 支持」还停留在路线图上。
也试过把主备两个中心写进同一个 planet:按官方设计,一个 planet 最多可以写 4 个根、每个根最多 32 个端点,客户端理应在主根不可达时自动改用备根,切换和切回都是协议原生行为。实际效果不好,经常连不上,这条路也堵住了。几个条件凑不齐,方案最终搁置,控制器目录和数据库仍以保证可恢复备份为底线。
(五)把 flow rules 当作配置来维护
ZTNet 可以直接编辑 ZeroTier 的 flow rules(rulesSource),并管理 capabilities 与 tags。我的需求是让某些成员只能访问指定 IP 的指定端口,且不能 ping 机器,其余流量默认拒绝——这类规则按成员拆 capability 就能表达,以前在 ztncui 上没法维护,只能绕开控制台手动改,现在直接在界面里完成。规则的语法和逐段拆解见《ZeroTier 03:用实例理解 flow rules》。
(六)选择建议
单控制器下只需建网和成员授权时,ztncui 的轻量和较少依赖仍有吸引力。需要在一个自托管界面中管理多人、多组织空间,并持续维护 rulesSource、capabilities 和 tags 时,ZTNet 更贴合这类工作流。ztncui 少一个数据库;ZTNet 要养一套 PostgreSQL,还得接受 BETA 的运维和升级风险。
三、总结
换到 ZTNet,主要是为了能在界面上直接改 flow rules,顺便审美也是对上了。还是上一篇那套控制器,备份完直接沿用数据目录,网络、成员、身份、Planet 一个没丢,客户端也没重新 join。用完记得把数据库、控制器目录和 rulesSource 一起备份。