从零搭建无障碍远程服务:NVDA Remote、RustDesk 与争渡远程

记录在腾讯云上使用 Docker Compose 搭建 NVDA Remote、RustDesk 和争渡远程服务的过程,以及排查 UDP、SQLite 和客户端兼容性问题的经验。

本文是一份脱敏后的部署记录。为避免把可直接用于连接或登录的信息公开,文中不包含服务密码、服务器码、连接 ID、公网 IP、Docker Hub Token、SSH 私钥或完整运行日志。

一、目标

在一台腾讯云 Ubuntu 服务器上,用 Docker Compose 统一管理三类远程服务:

  • NVDA Remote:为 NVDA 屏幕阅读器提供远程协助;
  • RustDesk:提供开源远程桌面服务;
  • 争渡远程:使用争渡官方提供的 Linux x64 服务端。

统一使用子域名 r.example.com 的思路,实际部署时按自己的域名替换。网站说明页单独由 Caddy 提供 HTTPS,方便用户查看地址、端口和客户端设置方法。

二、目录与账号

服务数据集中放在:

1
/home/ubuntu/remote

其中按服务分目录:

1
2
3
/home/ubuntu/remote/nvda
/home/ubuntu/remote/rustdesk
/home/ubuntu/remote/zdsr

服务器上创建了专用的 remote 用户组和受限系统用户。服务容器不需要把该用户加入 sudo 或 Docker 组;Docker Compose 由已有的运维账号执行,服务数据目录则使用独立权限保存。

三、Compose 统一管理

NVDA Remote、RustDesk 的 hbbs/hbbr 和争渡远程都放在同一个 Compose 项目中。这样可以统一完成:

  • 启动、停止和查看状态;
  • 数据目录挂载;
  • 容器自动重启;
  • 端口映射;
  • 版本更新。

争渡官方服务端要求同一个端口同时提供 TCP 和 UDP,因此 Compose 中需要同时发布:

1
2
3
ports:
  - "23188:23188/tcp"
  - "23188:23188/udp"

实际运行争渡服务时还使用了官方安装脚本采用的目录参数:

1
ZDRemoteServer /data

这一步很重要。只运行二进制而不传入数据目录时,服务端不能正确完成 ID 数据初始化。

四、最初遇到的问题

1. 云安全组没有放行

服务器本机监听和 UFW 放行,并不代表公网一定可用。腾讯云安全组还需要放行对应入站端口。

2. Docker 桥接网络对 UDP 注册不理想

争渡远程客户端可以发出 UDP 请求,但在 Docker bridge 模式下,服务端注册流程表现不稳定。后来将争渡容器改为:

1
network_mode: host

使官方服务端直接使用宿主机网络,客户端注册过程更加符合官方安装方式。

3. SQLite 原生库名称与权限

争渡 Linux 服务端依赖随包提供的 SQLite.Interop.dll。容器中先后遇到了:

  • 基础镜像缺少运行时依赖;
  • 原生库搜索路径不包含 /app;
  • 文件权限导致非 root 用户无法读取;
  • 官方程序寻找的库名称与压缩包文件名不完全一致。

最终在镜像中完成了:

  • 设置 LD_LIBRARY_PATH=/app;
  • 修正二进制与 SQLite 原生库权限;
  • 为兼容名称建立符号链接;
  • 以非 root 用户运行服务。

修复后,持久化目录出现了有效的 data.db,客户端可以正常获取 ID 并建立远程连接。

五、排查方法

遇到客户端拿不到 ID 时,按下面顺序排查比盲目重装更有效:

  1. 查看 Compose 状态:
    1
    
    docker compose ps
    
  2. 查看本机 TCP/UDP 监听:
    1
    
    ss -lntup | grep 23188
    
  3. 检查云安全组和 UFW;
  4. 从公网测试 TCP 端口;
  5. 用临时 tcpdump 确认客户端 UDP 请求是否到达;
  6. 查看争渡服务端 log.txt;
  7. 确认客户端与服务端版本、密码和服务器设置一致;
  8. 清理旧连接状态后重新生成 ID。

抓包时只查看协议元数据即可,不要公开完整数据包,因为其中可能包含客户端哈希、连接信息或其他敏感内容。

六、网页说明与无障碍

远程服务说明页使用原生 HTML 控件,重点做了几项简化:

  • 每项信息都有单独的复制按钮;
  • 每个服务都有“复制此服务信息”;
  • 按钮可通过 Tab 聚焦;
  • Enter 或空格才执行复制;
  • 复制结果使用 role="status" 和 aria-live 提示屏幕阅读器;
  • 网页不公开服务密码、服务器码或连接 ID,敏感信息改为通过可信渠道提供;
  • 页面会定期检查内容是否更新,检测到新版本后再刷新,避免无意义地频繁刷新。

七、自动更新的取舍

Watchtower 与当前 Docker API 版本存在兼容性问题,因此改用 systemd timer 每天执行 Compose 更新。更新脚本只拉取公开的 NVDA Remote 与 RustDesk 镜像;争渡镜像由自己的构建流程发布后,再按版本明确更新,避免自动任务误拉取不存在的本地标签。

八、镜像构建与分发

争渡镜像使用官方 Linux x64 服务端文件构建,并在 GitHub Actions 中通过 Docker Hub Access Token 发布。仓库保持私有,Actions Secrets 只保存:

1
2
DOCKERHUB_USERNAME
DOCKERHUB_TOKEN

不要把密码、Token 或私钥写入仓库、网页、日志或文章。Docker Hub 的镜像说明与 GitHub README 可以分别维护;镜像版本通过 Git tag 触发发布。

九、最后的连接信息

实际服务器地址、端口和密码请以自己的受保护配置为准。不要从博客文章复制密码,也不要把密码与服务器码放在同一篇公开文章中。

镜像项目仓库:

https://github.com/dpy013/zdsr-remote-image

Docker Hub 镜像搜索地址:

https://hub.docker.com/search?q=zdsr-remote&type=image

如果要直接使用 Docker Hub 仓库,请在 Docker Hub 中搜索 zdsr-remote,并核对发布者、版本标签和 README 后再拉取。

updatedupdated2026-10-052026-10-05