跳转至

第 10 章 远程接入、传输与部署排障

机器人很少在开发机上跑——真机往往在无显示器的角落、另一栋楼、甚至另一个城市。这一章把"远程把机器维护下去"这条链立起来:SSH 是绝对主线(密钥、sshd_config、跳板、隧道、X11Forwarding、autossh 与 mosh),图形远程方案(xrdp / VNC / RustDesk / NoMachine)在第 5 章已讲服务端安装,此处做横向选型;跨网组网(Tailscale / ZeroTier)作为外场演示的地基;

文件传输(scp / sftp / rsync / sshfs / Samba / NFS)与远程开发(VS Code Remote-SSH)合并在 SSH 之上;系统维护(磁盘占用、journal 收缩、logrotate、Timeshift 快照、apport 与 kdump)与开机自启的 systemd 范式合并在部署侧;综合排障方法论与五条案例集收束全书。

1. SSH 全栈

远程链路的组合方式(隧道与文件传输都叠在 SSH 之上):

flowchart LR
    dev["开发机"] -->|"密钥登录"| jump["跳板机"]
    jump -->|"ssh"| robot["上位机"]
    robot --> svc["systemd 服务 / ROS 2 节点"]
    dev -.->|"本地端口转发:ssh -L"| svc
    dev -.->|"反连隧道:ssh -R"| jump
    dev -->|"scp / rsync / sshfs"| robot
    dev -->|"VS Code Remote-SSH"| robot

1.1 密钥与 authorized_keys

口令登录已不建议生产使用;密钥是唯一入口。

## 开发机生成密钥(-t ed25519 更短更安全,RSA 兼容旧设备)
ssh-keygen -t ed25519 -C "robot-uplink-2026" -f ~/.ssh/robot_ed25519
ssh-copy-id -i ~/.ssh/robot_ed25519.pub robot@192.168.10.20     # 把公钥推到远端

## 上位机侧:~/.ssh/authorized_keys 只保留允许的公钥
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

~/.ssh/config 里为每个远端起一个短别名,命令行与 VS Code Remote-SSH 都用它:

Host robot-arm
    HostName 192.168.10.20
    User robot
    IdentityFile ~/.ssh/robot_ed25519
    ServerAliveInterval 30
    ServerAliveCountMax 3

坑:默认 sshd 允许口令登录

生产机上口令入口是主要爆破面:密钥登录 + PasswordAuthentication no 应是默认配置,改完先用新会话验证再退出旧会话。

1.2 sshd_config、跳板与隧道

服务端配置在 /etc/ssh/sshd_config;Ubuntu 8.3+ 支持 Include,独立写在 sshd_config.d/ 更清晰:

## /etc/ssh/sshd_config.d/99-robot.conf
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers robot
X11Forwarding yes
X11UseLocalhost yes
MaxAuthTries 3
ClientAliveInterval 60
ClientAliveCountMax 3

改完 sudo sshd -t 静态校验语法、sudo systemctl restart ssh 生效(别断开当前会话,另开一个终端试新连接)。

跳板 ProxyJump 一步穿两跳:

ssh -J lab@jump.example.com robot@192.168.10.20

三种端口转发:

## 本地转发:把远端 3389 拉到本地 13389,然后 mstsc 连 localhost:13389
ssh -L 13389:localhost:3389 robot@192.168.10.20

## 远端反连:把开发机的 Grafana 3000 暴露到跳板
ssh -R 0.0.0.0:33000:localhost:3000 lab@jump

## SOCKS 代理:浏览器走 ssh 出口访问内网
ssh -D 1080 robot@192.168.10.20

判据:转发的是 TCP,UDP 需要 socat 或 SSH -w 隧道。ROS 2 的 DDS 组播走 UDP,跨 SSH 隧道跑不通——多机组网走第 6 章局域网或下节的 Tailscale。

1.3 远程 GUI 与保活

X11Forwarding:服务端配置在第 5 章,客户端 ssh -X host 后 $DISPLAY 会被 SSH 会话分配为 localhost:10.0,xauth 从 ~/.Xauthority 生成会话凭据;每次连接用一次性凭据避免断线重连后凭据泄漏。轻 UI(xeyes / gedit)延迟可接受;三维 RViz 与 Gazebo 不适合走 X11 转发。

autossh 监视 ssh 进程、断线自动重建,适合外场笔记本换网络:

sudo apt install autossh
autossh -M 0 -f -N -L 13389:localhost:3389 robot@192.168.10.20

mosh(mobile shell)UDP + 客户端状态机,弱网与漫游体验更好;需要服务端 mosh-server + UDP 60000–61000 端口段放行。Ubuntu 22.04 主仓库直接提供 mosh 1.3.2(apt-cache policy mosh 可查候选版本),装通即得。上游 README(取用 2026-11)划定了两条硬边界:mosh 不支持 X 转发,也不支持 SSH 的非交互用途(含端口转发)——转发 GUI 与 -L 隧道仍走 SSH 本体;建连本身由 SSH 完成(密钥登录),之后 mosh-server 在高编号 UDP 端口监听、经 SSH 下发端口号与 AES-128 会话密钥,客户端换 IP 地址后数秒内自动跟随(漫游)。mosh --terminal=tmux-256color 与 terminfo 的对应关系、跨网延迟表现仍属待测量项。

2. 图形远程与跨网接入

2.1 方案横向选型

方案 协议 适用面 延迟 服务端要求
SSH X11Forwarding TCP / X11 单窗口轻 UI 中 sshd + xauth(见上节与第 5 章)
xrdp RDP / TCP 3389 Windows 客户端连入 Linux 中 xrdp + 桌面会话(MATE / XFCE 更稳;服务端安装在第 5 章)
VNC(TigerVNC / x11vnc) RFB / TCP 5900+ 跨平台、脚本友好 中 桌面会话 + VNC server;传输未加密,配 SSH 隧道
RustDesk 类 RDP 自研 开源、可自建中继 中–低 客户端 + 可选自建 hbbs/hbbr(机制见下节)
NoMachine NX 协议 高帧率、GPU 桌面 低 专有客户端;授权限制

判据:三条硬约束决定选型——客户端是什么平台(Windows 自带 mstsc → 优先 xrdp)、桌面会话类型(MATE / XFCE → 全支持;Wayland → 各家兼容不同)、带宽与延迟(现场笔记本 4G → mosh + SSH;有线 LAN → NoMachine / RustDesk)。xrdp 的服务端侧是 xorgxrdp 的 Xorg 栈,会话要固定为 X11,Wayland 会话没有对应实现(第 5 章「xrdp:RDP 协议的服务端」)。

RustDesk 自建服务的上游文档形态(RustDesk 文档,取用 2026-11):后端技术上为两个可执行文件——hbbs(ID / 会合信令服务器,TCP 21115/21116/21118 + UDP 21116)与 hbbr(中继服务器,TCP 21117/21119);最小必需端口段 21115–21117。客户端常驻 ping hbbs 上报地址,A 连 B 时由 hbbs 协调 NAT 打洞,打洞失败才回落 hbbr 中继——无人值守机器人在 cellular / 严格 NAT 后应预留中继路径。另有仅暴露 TCP 443 的 WSS 部署模式,但该模式下只剩中继通信、无点对点。

2.2 跨网组网

外场演示的常见需求:"机器人、控制笔记本、旁观者三台设备各自 4G / 校园 WiFi / 家里宽带,但要看同一个 DDS 域"。Tailscale(WireGuard mesh 网络,免费额度足够)与 ZeroTier(自建 controller 更灵活)都提供"跨 NAT 的虚拟 LAN":

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale ip -4                    # 分到 100.x.x.x 的虚拟内网 IP

判据与限制:组播帧跨 Tailscale 隧道不可靠——WireGuard 是单播 UDP 隧道,而 Tailscale 文档(取用 2026-11)的可检索口径只覆盖子网路由、单播互联与 MagicDNS,未提供组播转发的承诺页(其多播专页已迁址、本网络不可达);

因此 DDS 多机发现跨网不能指望组播复制,实际部署三条路:改 rmw_zenoh(ROS2-note 第 2 章)、开上游支持的 subnet router(tailscale up --advertise-routes=<网段> + 控制台批准 + 对端 --accept-routes,把局域网整段接入,局域网内组播仍由物理网段完成)、或多台设备同一物理 WiFi。自建 DERP 中继用于两端都落在对称 NAT 后的场景,ZeroTier 的自建 controller 则把控制面收回自己手里——两者都是托管方案的补充而非替代。

判据:远程图形按带宽与延迟选方案

纯看画面选传输效率高的方案,要交互低延迟选协议更轻的;跨公网先组网再谈图形,别让 RDP 直接暴露在公网。

3. 文件传输与远程开发

3.1 传输工具选型

工具 语义 用于
scp SSH 上拷文件 单文件、脚本嵌入
sftp 交互式浏览 手工上传下载
rsync 增量、断点续传 默认最优:rosbag / 模型权重回传
sshfs 挂载远端 现场临时浏览远端目录
Samba(smbd) 局域网共享 Windows 同事看数据
NFS 局域网挂载 Linux 集群共享盘;权限坑多

rsync 最小用法(Ubuntu 22.04 的 rsync 3.2.7,protocol 31):

## 上传:仅传变化、保留权限、进度条、断点续传
rsync -avh --progress --partial \
    --exclude='build/' --exclude='.git/' \
    ~/ws/ros2_bag/ robot@192.168.10.20:/data/bag/

## 从远端拉回一次全量、后续增量
rsync -avh robot@192.168.10.20:/data/bag/ ./local_cache/

判据:-a = -rlptgoD、--partial 保留断点、--exclude 排除不该同步的目录;大目录同步不宜用 scp -r,断一次全重来。

sshfs 一行挂载:

sudo apt install sshfs
mkdir -p ~/mnt/robot
sshfs robot@192.168.10.20:/data ~/mnt/robot -o reconnect,ServerAliveInterval=15
fusermount -u ~/mnt/robot

Samba 与 NFS 的部署属服务配置(/etc/samba/smb.conf、/etc/exports + exportfs -r);机器人上位机一般优先 SSH 家族,Samba 只在需要 Windows 端直接看到时开。

3.2 远程开发组合

承接第 3 章「编辑器、包管理与开发环境」中的 VS Code 侧,此处从 SSH 视角补一层:Remote-SSH 是在远端下载一个 vscode-server 二进制、通过 SSH 与本地 GUI 通信。判据:

  • 远端磁盘要留 200 MB+、内存要留 500 MB+,否则 vscode-server 起不来。
  • 首次连接较慢(下载 server),后续走缓存;升级 VS Code 主版本会触发远端重新下载。
  • 与本章 SSH 密钥、~/.ssh/config 完全复用;不需要额外开放端口。
  • clangd / python 扩展装在远端环境里,本地只画 UI;板上代码补全走的是远端符号数据库。

4. 系统维护与开机自启

4.1 磁盘与 inode 占用

df -hT /                                    # 文件系统剩余
df -i /                                     # inode 剩余(大量小文件会先耗尽 inode)
sudo du -xh --max-depth=1 / | sort -h | tail
sudo apt install ncdu
ncdu -x /                                    # 交互式磁盘分析

判据:机器人上位机磁盘占用大户依次是 /var/log/journal(常能占到 GB 级)、/var/lib/snapd/snaps、/home/<user>/.ros/log、/data 下的 bag;先清 journal 与 snap,再谈业务数据。

4.2 journal 收缩与 logrotate

journalctl --disk-usage                       # 输出:Archived and active journals take up 4.0G
sudo journalctl --vacuum-size=200M            # 缩到 200 MB
sudo journalctl --vacuum-time=7d              # 或只保留 7 天

永久限制在 /etc/systemd/journald.conf:SystemMaxUse=200M 一行;改完 sudo systemctl restart systemd-journald。logrotate 给非 journal 的日志(syslog、nginx、自写服务)配轮转:

/var/log/robot/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate                  # 让正在写的进程不用重启也能安全轮转
}

判据:journal 走 journald.conf、非 journald 走 logrotate,两套互不重叠。

4.3 apt 清理、快照与崩溃报告

sudo apt autoremove --purge                   # 卸依赖与残留配置
sudo apt clean                                 # 清 /var/cache/apt/archives
snap list --all | awk '/disabled/{print $1, $3}' | \
    xargs -r -n2 sudo snap remove --revision   # 清 snap 旧版本(默认留 3 份)

systemctl status apport                        # Ubuntu 桌面崩溃报告服务;服务器上可 disable
sudo apt install kdump-tools                   # 内核崩溃现场落盘;生产慎用(占内存)

Timeshift:桌面侧的 rsync / BTRFS 快照工具,能一键回到上一次启动前的 / 与 /home。两种后端分工明确:BTRFS 模式走 subvolume 快照(秒级、几乎不占额外空间,快照必须与源在同一 BTRFS 文件系统内),rsync 模式复制文件(支持 ext4,可按排除表裁剪)。机器人上位机常直接靠 apt 快照 + 手工回退,或走只读根分区(板端)。

4.4 开机自启的 systemd 范式

承接第 4 章与第 6 章,把"机器人上位机上电即跑"的服务组织成三层:

  • 业务服务:my-node.service(第 4 章已给模板),After=network-online.target + Wants=。
  • 锚点 target:自建 robot-baseline.target,把感知 / 控制 / 数据落盘服务串成一条依赖链,方便 systemctl isolate robot-baseline.target 一键切换。
  • 就绪探测:.service 里 ExecStartPost= 或健康检查 .timer,服务真就绪了再启动下一层;systemd 的 Type=notify + sd_notify 更精细。
## /etc/systemd/system/robot-baseline.target
[Unit]
Description=Robot baseline services
Requires=basic.target
After=basic.target

[Install]
WantedBy=multi-user.target
sudo systemctl enable robot-baseline.target
sudo systemctl set-default robot-baseline.target       # 开机进这一层

4.5 板端系统烧写的厂商途径

无人值守机器的系统镜像从厂商工具链落盘,两条路径的形态不同(均为厂商文档口径,取用 2026-11):

  • 树莓派:Raspberry Pi Imager(Windows / macOS / Ubuntu 均有安装包,sudo apt install rpi-imager 可装)——选型号→选系统→选存储→写入;写入前的 OS 定制一步即可预配置用户名与密码、Wi-Fi、主机名、时区、键盘与远程开关,免掉首启动向导——无显示器部署时在这一步把 SSH 与主机名定好,接电后直接走本节以上的 SSH 主线。
  • Jetson:SDK Manager 与 L4T 手动流两条——SDK Manager(Ubuntu 主机上的图形工具,登录 NVIDIA 账号、设备进 Recovery 模式后被识别、先刷 Jetson Linux 再选装 SDK 组件)与 L4T 手动流(解压 release 包与 rootfs、apply_binaries.sh 后在主机侧 sudo ./flash.sh ${BOARD} mmcblk0p1,$(BOARD) 取模块+载板对应的 .conf 名)。Recovery 识别可在主机侧用 lsusb | grep -i nvidia 确认;烧写与管理形态随 JetPack 版本变化,跨品牌差异不展开。

装完系统的闭环判据与上位机一致:开机自启走「开机自启的 systemd 范式」,设备接入与固定设备名归第 9 章,远程接入回到本章主线。

5. 排障方法论与案例集

总纲:journal / dmesg 双线、由近及远、每次只改一项。

  • 用户态双线:journalctl -u <unit> -b 看服务;systemctl status <unit> 看退出码与主进程。
  • 内核态双线:dmesg -T 看硬件与内核日志;/proc/<pid>/ 与 perf 看现场。
  • 由近及远:目标主机自身进程 → 内核 → 二层网络(ip link)→ 三层网络(ip route / ping)→ 应用协议(DDS / MQTT / RDP)→ 服务端配置(sshd_config / xrdp.ini)→ 上游依赖(chrony / DNS / DHCP)。

五条案例串起跨章索引:

  • 设备名漂移:ls /dev/ttyUSB* 号变了、脚本连不上。一线排查 dmesg -T | grep -E "ttyUSB|ch34|ftdi" 看枚举顺序、readlink /sys/class/tty/ttyUSB0/device 追物理路径;解按 udev 规则以 ATTRS{serial} 或 KERNELS 固定符号链接(第 9 章)。
  • QoS 通了但多机不通:单机 ros2 topic hz /scan 正常、两台机器静默收不到。一线排查 ip route get <peer> 判断走哪张网卡、sudo tcpdump -i any udp port 7400 看组播包是否出网、sudo ufw status verbose 看是否被过滤;解走 ROS_DOMAIN_ID + 网卡匹配 + 第 6 章的防火墙规则放行 DDS 组播段。深入 QoS 与 DDS 归 ROS2-note 第 2 章。
  • RT 配置后仍抖:装了 RT 内核,cyclictest -p 80 Max 仍上百 μs。一线排查 uname -v 确认 PREEMPT_RT 生效、cat /proc/cmdline 看 isolcpus / nohz_full / rcu_nocbs 是否配套、systemctl status irqbalance 是否还在跑、cat /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor 是否 performance;解 mask irqbalance、启动参数三件套齐上、禁 C6 与 intel_pstate=disable(第 7 章全套)。
  • GUI 起不来查图形依赖:ros2 run rviz2 rviz2 报无法打开显示。一线排查 echo $DISPLAY、echo $XDG_SESSION_TYPE(Wayland 还是 X11)、xdpyinfo | head(X server 可达性)、glxinfo | grep -E "vendor|renderer|OpenGL version"(GL 驱动是否 OK);解 SSH 无 -X 需带 X 转发、Wayland 会话下走 GNOME on Xorg、驱动未加载或 Secure Boot 拒载(第 5 章)。
  • 磁盘满导致服务起不来:某服务突然反复重启、journalctl 也拉不出新日志。一线排查 df -h / 与 df -i / 看空间和 inode;du -xh --max-depth=1 / | sort -h | tail 与 ncdu -x /var/log 定位;解 journalctl --vacuum-size=200M、清 .ros/log 与 snap 旧版本、必要时 apt clean。判据:inode 满比空间满更难发现,df -h 看着没事就漏掉了 df -i。
  • mDNS 发现失败:脚本里写 robot-arm.local,直连 IP 可以、主机名不通。一线排查 avahi-resolve -n robot-arm.local、systemctl status avahi-daemon、ss -unlp | grep 5353;解起 avahi-daemon、防火墙放 5353/udp、跨路由器场景要么同二层要么上 avahi-reflector(第 6 章)。

6. 要点

  • SSH 是绝对主线:密钥 + sshd_config.d/ 收紧 + ~/.ssh/config 别名 + -L / -R / -J 三件套;DDS 组播跨 SSH 隧道不通是硬边界。
  • 图形远程按客户端与桌面会话选:Windows 客户端优先 xrdp、MATE/XFCE 稳、Wayland 各家兼容不同;NoMachine 高帧率、RustDesk 自建走 hbbs/hbbr(打洞失败回落中继,默认端口 21115–21117);mosh 不承载 X 转发与端口转发,GUI 与隧道仍走 SSH。
  • 文件传输默认 rsync -avh --partial,不要 scp -r;Samba / NFS 只在跨平台或长期挂载时用;VS Code Remote-SSH 是 SSH 之上的应用组合,与第 3 章联动。
  • 系统维护三件套:journal 走 SystemMaxUse= 与 --vacuum-size;非 journald 走 logrotate;ncdu -x + df -i 双尺子查磁盘。开机自启走 systemd 单元 + target 锚点 + 就绪探测;板端烧写走厂商工具(树莓派 Imager、Jetson SDK Manager / L4T flash.sh)。
  • 排障双线由近及远:journalctl -u <unit> -b 与 dmesg -T;主机自身进程 → 内核 → 二层 → 三层 → 应用协议 → 服务端 → 上游依赖;每次只改一项、留下读数。
  • 五条案例是跨章索引:udev 规则(第 9 章)、DDS 组播(第 6 章 + ROS2-note)、cyclictest 抖动(第 7 章)、X11Forwarding 与 GL(第 5 章)、mDNS 与 avahi(第 6 章)——每章各承担一条主线,此处只作组合视图。

参考文献与延伸阅读

  • OpenSSH. 发行说明. 文档
  • OpenBSD. sshd_config 手册页. 文档
  • systemd-resolved. 手册页. 文档