☰
Ubuntu 22.04离线部署TigerVNC无头远程桌面实战
2026/10/17 8:40:03 网站建设 项目流程

项目一拿到手,我脑子里浮现的第一句话就是:又得跑机房一趟了。Ubuntu 22.04 服务器装离线远程桌面,这活儿看起来简单,实际坑比想象的多。关键词是“离线”、“TigerVNC”、“无头”。很多生产环境没有显示器、没有公网出口,甚至 USB 口都被管控,这时候想给用户开个图形桌面,不是装个包就能跑起来的,得提前把依赖链、配置文件、自启动服务、防火墙策略一股脑全理顺。这篇文章我就把自己跑通的完整过程、踩过的坑、以及最终的部署脚本都摊开讲。

先交代一下适合谁看:负责内网服务器运维、要给无头 Linux 机器配远程图形界面的同学,尤其是那些被“离线安装”四个字卡过脖子的朋友。看完你至少能搞清楚三件事:离线条件下怎么把安装包和依赖一次备齐,TigerVNC 到底怎么配置成独立会话而不是共享已有桌面,以及无头模式下开机自启怎么做到位。

1. 离线下部署远程桌面的需求与方案选型

1.1 为什么需要“独立远程桌面”而不是共享会话

很多第一次接触无头服务器的朋友会有一个惯性想法:服务器上都装了完整桌面环境,我远程连上去直接用不就行了?这话在办公室环境说得通,在机房和边缘节点里完全行不通。无头服务器(Headless Server)天生没有接显示器、没有独立显卡输出,甚至 BIOS 都不一定开启了图形输出,而远程桌面方案里“共享现有会话”那一类,比如 x11vnc,需要目标机器上已经有一个正在运行的 X 会话。问题是,很多 Linux 服务器默认就是跑在文本模式,你根本没机会在物理机上先启动一个图形登录界面,那 x11vnc 就无从谈起。

TigerVNC 走的是另一条路。它自带的 Xvnc 是一个完整的 X Server 实现,不需要真实显卡,也不需要物理显示器,直接在内存里创建虚拟帧缓冲(framebuffer)来承载桌面会话。换句话说,你通过 VNC 连进去看到的是一个完全独立、后台运行的图形会话,就跟你在物理机上插了显示器然后点登录一样,只是这台“虚拟显示器”长在内存里。正好匹配无头服务器“没有显示器也要能出桌面”的需求,而且这个会话不依赖任何正在运行的本地图形进程,重启也好、切用户也好,都不会互相干扰。

1.2 主流方案对比:TigerVNC 为什么更适合这个场景

市面上的远程桌面方案不少:x11vnc、VNC 家族、XRDP、NoMachine,甚至还有一堆商业软件。我自己的选型逻辑很简单:离线环境、无头服务器、要稳定、要能开机自启、要尽量不引入额外依赖。逐个分析下来:

  • x11vnc:必须依附于已有的 X 会话,无头场景直接出局。而且很多新机器用的是 Wayland,x11vnc 压根不认。
  • XRDP:本身设计目标就是做 RDP 协议服务端,和 Xorg 配合能新建会话,但配置相对繁琐,对非标准桌面环境兼容性反而一般,离线下要下载一堆编译依赖更是灾难。
  • NoMachine:体验确实好,但商业授权条款限制多,离线安装包体积也很大,不适合企业内网批量下发。
  • TigerVNC:作为 VNC 正统分支,功能完整,支持独立 Xvnc 会话、支持 UTF-8 键盘、支持通过 systemd 模板以服务形式托管,安装包体积适中,依赖链清晰。在离线下属于“最省心的选项”。

综合下来,TigerVNC 是无头服务器场景里最务实的选择。它附带的 vncpasswd、vncserver 配置体系本身就很“服务端友好”,加上 systemd 模板机制,基本就是为无人值守环境准备的。

2. 离线安装包准备:一次搞定依赖收集

2.1 出网机器上的下载准备

离线安装的核心痛点不是 TigerVNC 本身,而是它的依赖树。TigerVNC 服务端会依赖 X 相关的库、字体、认证组件,如果你还要装一个轻量桌面环境(比如 Xfce4),那依赖数量直接奔着几十个甚至上百个去。我见过很多同事在离线机上跑dpkg -i跑出一长串“依赖不足”的报错,然后逐個人肉去联网机器上找包,找到一个传一个,传一个又缺一个,心态直接崩掉。

正确的打开方式是在一台“同架构、同版本”的出网机器上,用 apt 的缓存机制一次把依赖全部拉下来。注意,我说的是同架构同版本。Ubuntu 22.04 的 amd64 包和 arm64 包不通用,20.04 的依赖版本也可能和 22.04 对不上,最稳妥的办法是在出网机器上开一个相同版本的容器或者虚拟机,在里面模拟一次纯净安装。

具体做法并不复杂。先在出网机器上创建或进入一个 Ubuntu 22.04 的干净环境,然后执行更新:

# 确保软件源可用 sudo apt-get update sudo apt-get install -y apt-utils debconf-utils

接着就是关键一步:用apt-get install --download-only把目标包和全部依赖下载到本地缓存,但不实际安装。

# 先建一个标记文件,便于之后区分新下载的包 touch /tmp/download_marker # 下载 TigerVNC 服务端、Xfce4 轻量桌面、中文字体、dbus 工具 sudo apt-get install --download-only \ tigervnc-standalone-server \ tigervnc-common \ tigervnc-xorg-extension \ xfce4 \ dbus-x11 \ fonts-noto-cjk \ ttf-mscorefonts-installer

这个命令执行完,所有需要的 .deb 文件都躺在/var/cache/apt/archives/目录里了。因为我们在下载前先创建了标记文件/tmp/download_marker,接下来可以非常干净地把本次新增的包全部挑出来,一个不漏也不多复制:

mkdir -p ~/vnc-offline/packages cd /var/cache/apt/archives find . -name '*.deb' -newer /tmp/download_marker -exec cp {} ~/vnc-offline/packages/ \;

注意我特意没有把xfce4替换成xfce4-session,因为如果只装会话管理,桌面里很多基础功能(文件管理器、终端、面板)会缺失,用户体验不好。但整个 xfce4 桌面组的依赖确实多,如果你的磁盘空间紧张,也可以只装xfce4-session xfce4-terminal xfce4-panel thunar这几个核心组件,依赖能少一小半。

2.2 依赖收集的坑:别迷信apt-cache depends

有些朋友会问我:为什么不用apt-cache depends逐个递归查依赖?这个思路本身没问题,但坑在依赖关系有三种:Depends、Recommends、Suggests。apt-cache depends会把这些混在一起列出来,而且不会告诉你哪些包系统里已经装过了。你跟着输出一个个找,不仅慢,还容易把 Recommends 里那些“可选依赖”也当成必需,白传一堆没用的包。更致命的是,有些依赖本身还有弱依赖关系,靠人肉递归几乎不可能保证完整性。

所以我的经验是:apt-get install --download-only是最可靠的,因为它使用和正常安装完全相同的依赖解析算法,apt 会替你算清楚需要哪几个包,连版本都锁好。下载完可以在出网机器上跑一把dpkg-deb -I抽查几个包的版本信息,确认 Source 版本和系统一致。

如果你的出网机器不是 Ubuntu 22.04,而是 Debian 12 或者其他发行版,千万别盲目套这个命令,Debian 和 Ubuntu 的依赖命名有差异。最保险的验证手段:在有网机器上跑apt-get install -s tigervnc-standalone-server xfce4,-s参数是 simulate(模拟安装),它会先打印“将会安装哪些包”而不实际执行,你拿这个输出和本地缓存目录里的包做个比对,就能确认完整性。

2.3 离线机上安装:dpkg 批处理和 apt 本地缓存两手都行

把~/vnc-offline/packages/目录用 U 盘、内网共享或者 scp 复制到离线机的任意位置。如果设备允许插 U 盘,直接拷贝最省事;如果不允许,就在内网起一个临时的 HTTP 服务(python3 -m http.server 8000),在离线机上用wget -r -np -nH --cut-dirs=3把目录拉下来。大文件走 HTTP 比走 scp 快,而且不容易断。

包拿到离线机上之后,有两种安装姿势:

第一种是纯粹的dpkg -i一锅端:

cd ~/vnc-offline/packages sudo dpkg -i *.deb

执行以后如果看到“dependency problems”报错,说明漏了依赖。此时不要慌,先看一下报错里缺的包名。只要 apt 的软件源里确实没有这些包,我们可以直接在出网机器上再补一次下载。补过一次之后,把新增的 deb 文件同步到离线机,再执行一次sudo dpkg -i *.deb。反复几次后一般就能装干净。注意 dpkg -i 一次传入多个包时,它会自动对这批包做依赖关系重组,顺序问题不大;但如果分多次传,顺序就要靠你掌握了,所以我更推荐第二种方案。

第二种是企业内网批量部署更稳的方案:把 deb 文件放进 apt 的本地缓存,借助 apt 的依赖解析来安装。

sudo cp ~/vnc-offline/packages/*.deb /var/cache/apt/archives/ sudo apt-get install --no-download tigervnc-standalone-server xfce4 dbus-x11

这里的关键参数是--no-download,它约束 apt 只能使用本地缓存里已有的包,不能联网下载。这样一个命令就能让 apt 自己算好顺序,自动处理依赖,一跑就通。有了这一手,完全不需要人肉记录依赖顺序,非常省心。

另外提醒一句:安装过程中如果弹出图形化的“配置文件是否需要覆盖”之类的提示,说明缺了debconf-utils或者DEBIAN_FRONTEND环境变量没设置。在批处理环境里,建议在命令前加上export DEBIAN_FRONTEND=noninteractive,以免安装进程被卡住等待交互输入。这是自动化离线部署特别容易被忽略的点。

3. 基础配置:创建用户、设置 VNC 密码与桌面会话

3.1 创建专用用户与 VNC 密码

安装完 TigerVNC,第一步不是急着启动,而是先想清楚用什么用户跑远程桌面。很多新手喜欢直接用 root。这里必须拦一下:TigerVNC 默认配置就限制 root 直连,就算你强行改配置跑起来了,安全风险也太高,而且 root 桌面里跑的文件管理器、浏览器一旦权限泄露,等于把整台机器交出去了。正确做法是建一个普通用户:

sudo adduser remoteuser

按照提示设置密码,其他信息一路回车即可。这里建议给这个用户配一个足够复杂的密码:长度不低于 10 位,包含字母大小写和数字。因为 VNC 的认证机制比较朴素,暴力破解是它最大的软肋。

接下来生成 VNC 访问密码。这里要特别区分两个概念:用户登录密码(Linux 系统密码)和 VNC 会话密码(进入远程桌面的密码)。两者可以不同,也不建议相同:

sudo -u remoteuser vncpasswd

输入两次确认密码即可。这个密码文件会存到/home/remoteuser/.vnc/passwd,权限是 0600。不要用sudo vncpasswd直接设置到 /root 下面,否则后续以 remoteuser 身份启动服务时读不到密码文件,结果就是“一直认证失败”。这是一个特别容易犯的低级错误,我调试的时候看过太多人栽在这。

3.2 配置 xstartup:让会话知道该启动什么桌面

启动会话之前,还需要确保~/.vnc/xstartup脚本存在并且准确。xstartup 就是 VNC 会话启动时执行的脚本,它决定你在远程桌面上看到的是完整桌面环境,还是只有一个静态的灰色窗口。

如果用户目录下还没有.vnc目录,先手动创建并设置好权限:

sudo mkdir -p /home/remoteuser/.vnc sudo touch /home/remoteuser/.vnc/xstartup sudo chmod -R 755 /home/remoteuser/.vnc sudo chown -R remoteuser:remoteuser /home/remoteuser/.vnc

然后把下面这个脚本内容写进xstartup:

#!/bin/sh unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS export LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 exec startxfce4

需要解释一下这两行unset。TigerVNC 的 Xvnc 会继承系统当前的环境变量,如果系统里残留着物理控制台的SESSION_MANAGER,那么在 VNC 会话里启动桌面时,程序可能会试图去连物理会话的 DBus,结果就是启动失败或者黑屏。把这两个变量清掉,等于告诉桌面:这是一个全新的图形会话,自己重新拉起一套 DBus 和会话管理。这两行对黑屏问题有奇效。

LANG和LC_ALL设置成中文 UTF-8 主要是为了让桌面和终端显示中文干净,同时确保键盘输入法相关的组件不出现乱码或者缺字。如果离线机上已经装了fonts-noto-cjk,中文字体显示就没问题;如果没装,界面会显示方块,所以前面下载时我特意把中文字体放进依赖里。

写完之后必须确认执行权限:

sudo chmod +x /home/remoteuser/.vnc/xstartup

没有执行权限,Xvnc 在启动时虽然会去读这个文件,但不会执行它,后果就是一个纯黑屏幕或者一个没有办法启动桌面的空 X 会话。这个检查点极其重要,基本可以决定部署是否成功。

3.3 手动启动验证第一遍跑通

在把服务交给 systemd 之前,先手动启动一次,便于直接观察日志和错误信息。用sudo -u remoteuser切换到 remoteuser 身份执行:

sudo -u remoteuser vncserver :1 -geometry 1366x768 -depth 24

这里的:1表示显示编号(display number)。编号:1对应的默认端口是5901,:2对应5902,以此类推。第一次启动时会生成一份~/.vnc/<主机名>:1.log的日志文件。启动完立刻查看日志:

sudo -u remoteuser tail -50 /home/remoteuser/.vnc/*:1.log

如果看到Xvnc TigerVNC 1.x以及类似Listening on port 5901的输出,说明服务端正常起来了。接着在另一台机器上用任意 VNC 客户端去连:

vncviewer <离线机IP>:1

能弹出桌面就说明基础链路全部通了。如果连不上,先看看系统防火墙是否放行了 5901 端口,不要一上来就修改 VNC 配置,很多问题其实是防火墙的锅。确认能连上之后再往下走。

3.4 手动启动的教训:日志是唯一的真相

我在本地测试时踩过一个很典型的坑:连接后黑屏,桌面完全无响应,日志里却什么都没有。后来仔细看才发现xstartup里的startxfce4路径不对。因为 Xfce4 安装后二进制文件在/usr/bin/startxfce4,但部分精简安装只装了xfce4-session,没有装完整的xfce4-session入口。这是为什么我在下载阶段特意装了整套xfce4而不是只装最小会话的原因。

排查黑屏问题,最忌讳的就是“猜”。VNC 会话的日志目录是/home/remoteuser/.vnc/,里面每一个文件都记录了这个会话启动时的输出和报错。先看日志、再改配置、最后重启会话,这个顺序别搞反了。手动启动的方式验证通过后,再进入下一步服务化,可以避免把大量时间浪费在 systemd 状态和故障排查的夹缝之间。

4. 无头服务器场景的服务化与自启动

4.1 理解 TigerVNC 的 systemd 模板机制

手动可以启动不代表生产环境就合格了。一台无头服务器常常要考虑断电重启、系统崩溃后恢复,所以远程桌面服务必须交给 systemd 托管,做到开机自启、异常自动拉起、崩溃后不再手动登录。

TigerVNC 安装完以后,系统里会自带一个 systemd 模板单元文件/lib/systemd/system/tigervncserver@.service。你也能在/lib/systemd/system/tigervncserver@.service里看到它的实际内容,格式上是标准的 systemd template。这个模板文件里预留了两个配置文件:/etc/tigervnc/vncserver.users用来定义“哪个显示编号对应哪个系统用户”,/etc/tigervnc/vncserver-config-mandatory用来放所有用户都必须继承的强制配置。

先看vncserver.users。默认文件里被注释的例子长这样:

# :1=test # :2=otheruser

我们把示例取消注释,或者直接添加如下行:

:1=remoteuser

这行的语义是:当 systemd 以:1作为实例参数运行时,实际服务以remoteuser这个系统用户身份去启动 VNC 会话。所以 /etc/tigervnc/vncserver.users 本质上就是“显示号到系统用户”的映射表。有了它,systemd 就知道该用哪个身份、哪个家目录来寻找 xstartup 和 passwd 文件。

4.2 固定显示号与端口映射

systemd 模板的服务名写起来有一点讲究。启用服务时要用:

sudo systemctl enable tigervncserver@:1.service sudo systemctl start tigervncserver@:1.service

注意这里的服务名里:1必须带着@和冒号。systemctl start tigervncserver@1.service和服务名不匹配会直接报错,这个细节看起来不起眼,却是新手最容易犯的错。带上了:1之后,systemd 会读取刚才配置的vncserver.users文件,自动把实例参数:1和用户remoteuser关联起来。

默认端口就固定在5901,一般不需要改。但如果这台机器上已经跑了 5901 端口的其他服务,比如监控 Agent,那就要手动指定端口了。可以通过在/etc/tigervnc/vncserver-config-mandatory中加一行指定:

rfbport=5903

然后启动服务时使用:3作为显示号。注意这里的显示号:3会自动映射到端口 5903,当然你也可以用强制配置覆盖。我一般建议显示号连续且从 1 开始,端口映射直观好排查,不要搞得跳来跳去。

另外还有一个无头场景常遇到的问题:默认会话分辨率是1024x768,很多运维同事远程进去觉得窗口太小。我们可以在强制配置里写死几何大小:

geometry=1920x1080

这个值也可以放到用户级配置文件/home/remoteuser/.vnc/config里,只对 remoteuser 生效。如果想让所有用户都用统一分辨率,放到/etc/tigervnc/vncserver-config-mandatory更合适。

4.3 开机自启与 systemd 管理细节

配置好以上内容后,执行启动:

sudo systemctl daemon-reload sudo systemctl enable --now tigervncserver@:1.service

enable --now一条命令完成“开机自启”和“立即启动”。如果之前手动启动过 vncserver,需要先手动停掉:

sudo -u remoteuser vncserver -kill :1

不然 systemd 启动时会发现 5901 端口已经被占用,报错“address already in use”。这个坑我至少踩过三四次,每次都是因为图方便手动调试完忘了关,然后 systemd 起不来。一定要记住:手动调试和服务托管只能二选一,千万别同时开。

启动以后,用systemctl status tigervncserver@:1.service查看状态,看到active (running)即成功。为了保险,把离线机的网络断开或者干脆重启一次测试开机自启是否生效。开机自启类服务最怕的就是“当时能起,重启后起不来”,所以重启验证这步不能省。

4.4 端口与监听地址:无头场景下的第一个安全分歧点

TigerVNC 默认监听所有网络接口(0.0.0.0)。如果这台机器本身在网络边界,或者所在网络环境里有人做扫描,那么直接把 5901 端口裸奔到网络上是非常危险的行为。VNC 密码虽然做了加密传输,但密码爆破工具成熟,数据库里大量弱口令被撞库,仅仅依赖 VNC 密码并不安全。

所以在 systemd 托管之后的下一步,就要认真思考“这个服务到底给谁用”。如果只是内网运维人员通过跳板机访问,那完全可以让 VNC 只监听自己:

# /etc/tigervnc/vncserver-config-mandatory localhost=yes

设置成localhost=yes后,VNC 服务只会监听127.0.0.1:5901,外部机器根本连不上。此时运维人员需要先通过 SSH 登录到这台服务器,再在 SSH 会话里建立本地端口转发访问 VNC:

ssh -L 5901:127.0.0.1:5901 remoteuser@<离线机IP>

然后在自己的电脑上连接127.0.0.1:5901,实际上流量会通过 SSH 隧道送到远端服务器的127.0.0.1:5901。这样既免去了 VNC 流量明文传输的担忧,又大幅降低了被外部扫描爆破的概率。如果客户端图形界面不方便操作端口转发,用 PuTTY 的 Tunnels 面板配置也是一样的效果,填上源端口 5901、目标127.0.0.1:5901即可。

如果你的使用场景就是“内网直连,且所在网络有防火墙白名单”,那可以选择不启用 localhost 限制,但至少要在防火墙层把 5901 端口的来源 IP 限制到运维网段。顺序上一定先限制端口,再考虑暴露。

5. 远程使用技巧与安全加固

5.1 客户端连接与全屏等参数

TigerVNC 官方客户端(TigerVNC Viewer)的参数非常实用。连接时最直接的写法:

vncviewer -geometry 1366x768 192.168.x.x:1

但如果你希望全屏显示并且不让会话在断连后退出,客户端和服务端要配合好。服务端在配置里加上:

AlwaysShared=1

这个参数让一个显示号可以同时被多个客户端连接。默认情况下 VNC 会话是独占的,第二个客户端连接会把第一个挤掉,这在团队协作或者远程演示的时候非常难受。开了共享之后,多个同事可以同时看同一个画面,但要注意只有一个人能操作鼠标键盘,实际上没有“同时操作”的能力。

客户端本身想在全屏和窗口之间切换,按F8调出菜单即可,这里面有一个常见误区:很多人以为全屏后没法退出,按F8再选“Quit”才回到窗口模式。实际上F8菜单随时可以唤出,不用把视线从屏幕上挪开去关客户端窗口。

5.2 仅本机监听加 SSH 隧道的实践模板

在我个人运维习惯里,远端客厅(办公室)直连服务器 VNC 的场景我只开放 SSH 端口,VNC 一律按localhost=yes配置。这样整个链路的加密、认证都收敛到 SSH 一层,减少了漏洞面。

具体操作时,我通常先在本地机器上建一个 SSH 隧道:

ssh -N -L 5901:127.0.0.1:5901 remoteuser@<服务器IP>

-N表示不执行远程命令,只做隧道,-L做本地转发。隧道建好后,再去连127.0.0.1:5901。实测下来这个方案的体验非常稳,尤其在跨网络环境,VNC 本身暴露的端口越少,运维越省心。如果只是临时开一个隧道,用-f后台执行,关闭时找到 ssh 进程杀掉即可。

当然,如果你的使用环境属于完全可信任的内网,采用白名单限制端口来源 IP 的方式也能接受。但无论如何,裸奔配置localhost=no且不做任何来源限制,我是强烈不建议的。

5.3 对公网环境补充的安全加固

如果服务器有公网地址,或者 VNC 必须要被外部客户端直接访问,那配置上要比内网严格得多。首先密码必须足够复杂,而且建议在 VNC 配置里启用 TLS 加密:

SecurityTypes=TLSVnc

TigerVNC 支持 TLS/VeNCrypt 加密,前提是客户端也支持。配置完成重启服务后,看日志确认是否成功协商加密,如果客户端连不上,通常说明版本兼容性问题,可以临时回退到SecurityTypes=Vnc验证,但不要长期裸奔。

其次,强烈建议在系统层做进程级的访问控制,比如用ufw或firewalld只放行指定来源 IP。Ubuntu 22.04 默认没有开 ufw,离线环境下你需要提前下载安装ufw包。操作如下:

sudo ufw allow from 192.168.10.0/24 to any port 5901 proto tcp sudo ufw enable

这里的来源 IP 网段可以根据实际运维入口来定。开了这个以后,外部机器即使拿到了密码,也没法从其他来源连入,相当于多了一把锁。

5.4 分辨率与多显示器问题的处理

无头服务器分辨率是一个容易被忽视的痛点。默认的 1024x768 在现代大屏显示器上拉得很模糊,而且很多用户希望远程桌面和自己的本地屏幕分辨率保持一致。TigerVNC 的配置里,可以让服务端动态适应客户端窗口:

# 不写死 geometry,交给客户端窗口大小 geometry=1366x768

如果客户端连上后分辨率不对,可以在 VNC 客户端菜单里选择“Scale”或“Resize window to session size”。但更本质的问题在于:VNC 服务端的 framebuffer 大小在会话创建时就固定了,即使客户端窗口变化,服务端分辨率也不一定随之调整,需要重新启动会话才能改。

遇到这种情况,我比较常用的做法是给服务端配一个较大的固定分辨率,比如 1920x1080,然后在客户端选择“Fit Window to Session”,让画布自适应窗口缩放。这样既保证了桌面元素不至于过小,也免去频繁重启会话的麻烦。

6. 常见故障与排查实录

6.1 连接成功却黑屏

我遇到过好几次这样的现象:VNC 客户端能正常连上,但屏幕一片黑,或者只有一个灰色背景没有面板和图标。这个问题几乎 100% 都出在xstartup上。

排查顺序先看日志,执行下面这条命令检查最近一次会话的输出:

sudo -u remoteuser tail -50 /home/remoteuser/.vnc/<hostname>:1.log

如果日志里有xauth: file /home/remoteuser/.Xauthority does not exist,多半是/home/remoteuser/.Xauthority的权限或属主不对,执行chown remoteuser:remoteuser /home/remoteuser/.Xauthority修复。如果日志里直接报了startxfce4: command not found,说明 xfce4 的核心组件没装全,回头检查离线包里有没有xfce4-session。如果日志里有一堆 dbus 报错,通常就是xstartup里没写那两行unset,把前面给的脚本原封不动放上去就好了。

黑屏问题的核心教训就一句话:别盲猜,先看日志。VNC 会话日志记录得非常详细,从服务启动到桌面拉起的所有输出都在里面,排查一轮基本能定位到 90% 的问题。

6.2 认证一直失败

客户端输入密码后提示Authentication failed,这不是你输错那么简单。先说最常见的情况:vncpasswd生成密码文件时用错了用户。比如你以 root 身份执行了sudo -u remoteuser vncpasswd,那/home/remoteuser/.vnc/passwd就正常;但如果直接执行sudo vncpasswd,密码文件会写到/root/.vnc/passwd,remoteuser 的会话里根本读不到,自然认证失败。

还有一种是多个 VNC 实现共存导致的冲突。有些机器上预先装了 RealVNC 或者 TightVNC,它们的密码文件格式虽然都是 VNC 格式,但路径和配置优先级可能不一样。离线部署前,务必检查/etc/tigervnc/和系统里是否有其他 vncpasswd 的残留配置。如果确实有冲突,可以显式在 xstartup 或者 systemd 服务环境里指定密码文件路径。

另外提醒一下,修改密码之后建议重启 VNC 服务。VNC 密码文件是启动时加载的,改了不重启不会立即生效,见过有人改了密码然后来问为什么还提示密码错误,实际上服务还缓存着旧密码。

6.3 端口不通与防火墙拦截

客户端一直转圈然后超时,通常不是 VNC 服务本身的问题,而是网络路径上端口被关了。排查顺序:

先确认服务端是不是真的在监听:

ss -tlnp | grep 5901

如果输出里有监听记录,说明服务端没问题。接下来检查系统防火墙:

sudo ufw status sudo iptables -L -n | grep 5901

很多 Ubuntu 服务器默认没开 ufw,但有可能在系统初始化时被安全基线加固脚本加过 iptables 规则。如果 iptables 里有 DROP 规则,需要单独放行 5901。

再往上就是网络层了:如果是云主机,要在安全组里放行 5901 端口;如果是物理机,需要找网络管理员确认交换机、边界防火墙的放行策略。对于无头服务器,很多同事的机器其实不在公有云上,自己又没有权限看机房防火墙,那就只能用前面提到的 SSH 隧道方案绕过去。

6.4 掉线与会话抢占问题

VNC 会话莫名其妙的掉线,最常见的原因是本地网络波动,客户端和服务端之间的 TCP 链路断了。默认情况下 VNC 会话会继续保留,重新连接即可恢复。但如果配置了AlwaysShared=1且第二个用户连接进来,第一个用户可能被挤掉,这也是“掉线”的一种。

还有一种情况是服务端所在的机器休眠了。对于无头服务器,一般都会在 BIOS 层面关掉休眠,但有些系统预装的电源管理策略还是会让机器进入睡眠。检查logind.conf:

sudo grep -E '^IdleAction' /etc/systemd/logind.conf

如果看到IdleAction=suspend或IdleAction=hibernate,改成ignore并重启 logind。

另外,长时间不操作导致会话“冻结”也是一个常见现象,多半和屏幕节电策略有关,VNC 会话里跑到 xset 相关的命令:

sudo -u remoteuser xset s off -dpms

这一条可以写进xstartup,让会话不进入屏保和自动关屏状态。加上这一行之后,VNC 会话长期挂着不操作也不容易断。

6.5 离线补装中文字体

如果用户在远程桌面里看到大量方块字,多半是中文字体缺失。前面下载时我特意加了fonts-noto-cjk。但如果当时没装,后面也可以补:

# 在出网机器上 apt-get download fonts-noto-cjk fonts-noto-cjk-extra fonts-droid-fallback

把下载好的 deb 文件拷贝到离线机,执行sudo dpkg -i fonts-noto-cjk*.deb,然后重开 VNC 会话即可。装完中文字体后,随便开一个终端运行fc-list :lang=zh检查输出,能看到 Noto Sans CJK 相关字体就说明 OK 了。另外要确认/etc/locale.gen里有没有生成zh_CN.UTF-8。Ubuntu 22.04 默认可能只生成了英文 locale,执行:

sudo locale-gen zh_CN.UTF-8 sudo update-locale LANG=zh_CN.UTF-8

这两步做完,桌面程序的中文界面大概率能正常显示。

6.6 其他细节与避坑经验

最后把我在实际部署中容易犯的“小毛病”汇总一下,这些都是去一次机房成本极高、所以值得提前规避的问题:

  • Xauthority 文件权限:~/.vnc/目录属主必须是 remoteuser,否则 VNC 会话会因为无法写入 Xauthority 而直接崩溃。
  • 手动启动和服务托管互斥:调试时用了vncserver :1,上线前必须vncserver -kill :1再启动 systemd 服务,否则端口冲突。
  • 防火墙的白名单:每次部署前先确认目标网段是不是真的能访问服务器,不要在远端改了一通配置,最后发现是机房防火墙根本没放行。
  • 日志保留策略:建议在/etc/logrotate.d/tigervnc里为/home/remoteuser/.vnc/*.log配置轮转,不然长期运行的服务器日志文件可能膨胀到几个 GB。
  • 公网环境不要长期关闭 VNC 加密:在公网中,VNC 明文的密码和画面抓包就能看到。开了 SSH 隧道或者至少 TLSVnc 再暴露端口。

离线部署 TigerVNC 这套流程,我前后跑过不下十次,从一开始依赖漏成筛子到现在能一条命令从准备到上线,核心就是抓住了两个关键动作:用apt-get install --download-only一次性解决依赖,用 systemd 模板加vncserver.users把服务托管交给系统。配置上别贪多,xstartup保持精简,强制配置保持收敛,遇到问题就先看日志再动手,基本不会有大坑。

最后再分享一个小技巧:如果你部署的目标机数量比较多,建议在出网机器上把整个~/vnc-offline/packages目录做成一个 tar 包并记录校验值,内网下发时用 sha256sum 校验后再安装,避免传输过程中 deb 损坏导致莫名其妙的安装失败。这个看似不起眼的动作,能省掉许多不必要的远程排障。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询