一台不带显示器的 Ubuntu 服务器,放在内网机房里,平时想上去操作图形界面工具,比如看报表、配置监控大屏、调一调运维平台的 Web 页面,最直接的做法是装个远程桌面。但麻烦的是,这类服务器往往和外网隔离,apt 源连不上,装什么都要先下载一堆 deb 包再拷进去。这篇文章就围绕 Ubuntu 22.04 离线环境下部署 TigerVNC 展开,目标很明确:在无显示器、纯命令行的服务器上,跑起一个独立的远程桌面会话,用 VNC 客户端从局域网任意机器连上去就能看到完整的图形环境。整个过程不依赖外网,不依赖物理屏幕,适合机房服务器、虚拟机和各种内网环境。
TigerVNC 对做运维的人来说并不陌生,它是 VNC 的一个活跃分支,性能和兼容性都比较成熟,尤其适合 Linux 远程桌面场景。我第一次在无头服务器上搞远程桌面也踩了不少坑,最典型的两个问题:一是离线环境下依赖关系理不清,dpkg 装到一半报一堆依赖缺失;二是好不容易装上了,连接后却是黑屏或者灰屏,根本没法操作。这篇文章会把这两条线完整捋一遍,从方案选型、离线包准备、系统配置,到 systemd 托管、客户端连接、故障排查,全部用可复现的步骤写清楚。无论你是第一次接触 VNC 的新手,还是被离线依赖折腾过几次的老手,看了这篇应该能少走不少冤枉路。
1. 方案选型:为什么是 TigerVNC,而不是 xrdp 或 X11 转发
1.1 三种远程方案的差异对比
在做内网图形远程访问时,常见的选择有三类:xrdp、X11 转发(SSH 端口转发)、还有 VNC 系(TigerVNC、TightVNC 等)。我直接把它们的核心差别列出来,这样更容易判断什么场景该选谁。
| 方案 | 工作原理 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| xrdp | 自己实现 RDP 服务端,连接后创建新的 X 会话 | Windows 自带客户端可连,体验接近 Windows 远程桌面 | 默认需要 Xvfb 配合,会话管理偶尔抽风;离线依赖数量并不少 | 客户端以 Windows 为主、不想装 VNC 软件的环境 |
| X11 转发 | 通过 SSH 隧道把单个应用的显示转发到本地 | 安全,不额外开端口,重量轻 | 只转发单应用,不是完整桌面;网络差时延迟明显 | 临时用一下图形小工具 |
| TigerVNC | Xvnc 服务端创建独立虚拟显示,VNC 客户端直接连 | 独立桌面完整,性能好,压缩算法成熟,离线依赖相对可控 | 默认不加密,需自行控制网络暴露面 | 无头服务器完整桌面访问,本文的场景 |
如果你的服务器本身就是 Ubuntu 桌面版,那么系统里已经装了 GNOME。TigerVNC 可以直接基于它开一个独立会话,不需要再装额外的桌面环境。如果是最小化安装(没有桌面),那就在准备离线包时把 XFCE 或 LXDE 这类轻量桌面一起带上,别用 GNOME 全套,依赖太重,离线安装时容易把自己坑哭。
1.2 独立桌面模式的原理:虚拟显示器与无头能力
TigerVNC 的"独立桌面"模式,核心在于 Xvnc 这个进程。它本质上是一个 X Server,但它不依赖物理显卡和显示器,而是在内存里创建一个虚拟显示设备。你给它分配一个显示号(display number),比如:1,它就监听5900 + 显示号的端口(所以:1对应5901)。用户连上 VNC 客户端之后,看到的就是这个虚拟显示器上渲染的画面。
这种模式的"无头可用"体现在哪里?物理机上没有插显示器,Xorg 起不来,但 Xvnc 照样能工作。它不需要检测 EDID、不需要 GPU 驱动,只要 CPU 和内存足够,就能跑起完整的图形会话。这也是它被大量用于云服务器、虚拟机和机房无头节点的重要原因。
还有一个细节值得注意:TigerVNC 的独立会话和系统的默认桌面会话是隔离的。你在 VNC 里做的操作不会直接出现在物理屏幕(如果服务器真的接了屏幕)上;多个 VNC 用户也可以各开各的会话互不干扰。这在多人共用服务器、需要互不影响的图形环境时非常实用。
2. 离线安装包准备全流程
2.1 在联网机器上下载 deb 包
离线安装的第一步,是在一台同样跑 Ubuntu 22.04、可以访问外网的机器上,把需要的 deb 包全部下载下来。这里强烈建议使用apt-get install --download-only,而不是手动一个个apt download,因为前者会解析完整的依赖关系,一次性把所有需要的包下载到/var/cache/apt/archives目录里。
我实际操作时这样做的:
sudo apt-get update sudo apt-get install --download-only tigervnc-standalone-server tigervnc-common tigervnc-xorg-extension dbus-x11 xauth如果你的服务器是最小化安装、也没有桌面环境,那么还需要把桌面环境的依赖一起带上。以 XFCE 为例:
sudo apt-get install --download-only xfce4 xfce4-goodies dbus-x112.2 架构一致性与依赖版本匹配
这一步有个非常关键的检查项:下载机的系统版本和 CPU 架构必须和目标服务器一致。Ubuntu 22.04 的 deb 包在 apt 源里区分 amd64、arm64 等架构,如果下载机是 x86_64、目标服务器是 arm64,拷贝过去的包基本上没法用。检查架构用:
dpkg --print-architecture当前下载机返回amd64,目标服务器也要是这个值。如果两台机器都是 Ubuntu 22.04、都是 amd64,那apt-get install --download-only解析出来的依赖就是完全匹配的。这里还有一个实用技巧:下载完成后,把/var/cache/apt/archives目录里的所有 deb 文件拷贝到 U 盘或内网共享目录,然后在目标服务器上直接对所有 deb 执行dpkg -i ./*.deb。如果依赖有顺序问题,dpkg -i会在同一批 deb 文件里自动反复尝试,只要文件全,最终都能装上。
为了确保下载到最新且兼容的版本,我还会查看一下下载下来的包版本:
ls -lh /var/cache/apt/archives/ | grep tigervnc一般会看到tigervnc-standalone-server_1.12.0+dfsg-1_amd64.deb这样的文件。版本号和实际系统仓库有关,不用强求一致,但建议下载机和目标服务器的更新源尽量接近,避免出现一方包版本过高、依赖不满足的情况。
2.3 传输与本地仓库安装
传输方式不限,scp、U 盘、内网 HTTP 共享都可以。到了目标服务器上,我习惯先把所有 deb 放到同一目录,然后执行:
cd /path/to/debs sudo dpkg -i ./*.deb如果提示依赖缺失,先不要慌。dpkg -i失败只是中断安装,不会污染系统。此时检查是否漏掉了某些依赖包,尤其是libx11-6、libxext6、libxfixes3、libxrandr2这类基础图形库。它们很可能在目标服务器上已经存在(因为系统本身就带了图形库),也可能确实缺失。如果缺失,回到下载机上用apt-get install --download-only补下载,或者在目标服务器上先用apt-cache depends tigervnc-standalone-server查看依赖关系——不过离线环境下这个命令不一定有缓存信息,所以最保险的还是提前备好全套包。
为了方便后续维护,我还建议在目标服务器上做一个最简单的本地 apt 源,把 deb 目录挂到/var/lib/apt/lists之外的一个位置,用sudo apt install ./xxx.deb方式补充安装。这条命令会读取当前目录下的 deb 并尝试解析依赖,比dpkg -i更智能,能识别已经安装的依赖。
3. 服务端配置与 systemd 托管
3.1 用户与密码准备
TigerVNC 的会话是跟用户绑定的,服务运行在哪个用户下,桌面环境就是那个用户的。不建议用 root 跑 VNC 服务,安全性和桌面环境的兼容性都不好。我一般建一个专门用户,或者直接使用已有的普通用户。
sudo adduser vncuser su - vncuser然后是设置 VNC 密码,VNC 的密码不是系统密码,而是服务端为每个用户单独维护的访问密码:
vncpasswd这个命令会要求输入两次密码,然后生成~/.vnc/passwd文件。如果要在自动化脚本里无条件设置密码,也可以这样用:
mkdir -p ~/.vnc echo "你的密码" | vncpasswd -f > ~/.vnc/passwd chmod 600 ~/.vnc/passwd注意:vncpasswd -f只读取输入流的第一个行作为密码。密码长度最长 8 个字符,多余部分会被截断,这也是 VNC 协议本身的限制,不是配置问题。
3.2 xstartup 脚本编写
TigerVNC 启动会话时,会去执行用户家目录下的~/.vnc/xstartup文件。这个文件决定了虚拟显示器上启动什么桌面环境。如果这个文件不存在或者没有执行权限,你会得到一个灰屏或黑屏的会话,什么都不显示。
最基础的做法,在 Ubuntu 22.04 桌面版上启动 GNOME:
#!/bin/sh unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS exec dbus-launch --sh-syntax --exit-with-session gnome-session如果是最小化系统搭配 XFCE:
#!/bin/sh unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS startxfce4写完以后必须赋予执行权限:
chmod +x ~/.vnc/xstartup这里要特别提醒:xstartup 的第一行#!/bin/sh不能省。很多人在这里填了 Python 解释器或者别的路径,结果脚本直接执行失败,VNC 会话起不来。另外,.vnc目录的权限建议保持 700,passwd文件权限保持 600。TigerVNC 对权限检查虽然不像 SSH 那么严格,但保持最小权限是良好习惯。
3.3 手工启动与 systemd 服务文件
配置完 xstartup 之后,先手动启动一次验证,这样日志输出直接能看到:
vncserver :1 -localhost no-localhost no表示允许非本机连接。如果只在内网使用,可以不加这个参数,默认情况下 TigerVNC 在 1.12 版本以上只监听 127.0.0.1,需要显式开放才能监听所有网卡。用vncserver -list可以查看当前已启动的会话:
vncserver -list TigerVNC server sessions: X DISPLAY # RFB PORT # RFB UNIX PATH :1 5901确认正常后,把它交给 systemd 管理,这样开机自启、崩溃自拉起。在/etc/systemd/system/vnc@.service下创建服务文件:
[Unit] Description=TigerVNC Server for %i After=syslog.target network.target [Service] Type=forking User=vncuser Group=vncuser WorkingDirectory=/home/vncuser ExecStart=/usr/bin/vncserver %i -localhost no -geometry 1920x1080 -depth 24 ExecStop=/usr/bin/vncserver -kill %i Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target注意这里的%i是一个模板占位符,通过systemctl start vnc@:1启动时,会被替换成:1。Type 使用forking,因为vncserver启动后会把自身 daemon 化。启动命令:
sudo systemctl daemon-reload sudo systemctl enable vnc@:1 sudo systemctl start vnc@:1查看状态和日志:
sudo systemctl status vnc@:1 journalctl -u vnc@:1 -f如果看到New Xtigervnc server '...' is now running,说明服务端已经就绪。
4. 客户端连接与无头模式验证
4.1 用 TigerVNC Viewer 连接
服务端跑起来以后,客户端就简单了。Windows、Linux、macOS 上都有 TigerVNC Viewer,下载对应版本打开,地址栏输入服务器IP:5901或服务器IP:1,回车后输入 VNC 密码就能看到桌面。Linux 上也可以直接用命令:
vncviewer 192.168.1.100:5901这里推荐使用 IP 加端口5901的方式,不容易产生混淆。如果客户端连不上,先用telnet 192.168.1.100 5901检查端口通不通,然后再排查服务端状态。
4.2 验证"独立桌面"可以同时存在多个会话
TigerVNC 的"独立"体现在每个显示号都是一个独立会话。比如服务器上已经跑了:1,你还可以再开一个:2给另一个用户,互不影响:
vncserver :2 -localhost no -geometry 1280x800 -depth 16这种多会话能力在做开发测试、Demo 展示、多人共用服务器时特别有用。我甚至见过有人在一个 32 核的服务器上给每个项目组各开一个 VNC 桌面,各自装自己的图形工具互不干扰,比起物理机一人一台省了不少资源。
4.3 网络暴露与控制
默认的 VNC 协议是不加密的,密码校验也存在被嗅探的风险。在内网使用问题不大,但如果服务器暴露在不可信网络,强烈建议做两层防护:一是防火墙只放行内网网段访问5900-5910端口;二是用 SSH 隧道包装 VNC 流量。
SSH 隧道的用法很简单,客户端机器上执行:
ssh -L 5901:localhost:5901 vncuser@服务器IP然后本地 VNC 客户端连接localhost:5901,流量就完全走 SSH 加密隧道了,服务器上的 VNC 服务端甚至不需要开-localhost no,只要监听本地即可。这种方式既安全,又不用额外配置 TLS,是我个人最推荐的远程访问方式。
5. 实战踩坑实录与常见问题速查表
5.1 典型的启动失败与日志定位
TigerVNC 的问题,大多能在日志里直接看到原因。日志位置在用户的~/.vnc/目录,文件名一般是机器名:1.log。启动失败时,先别慌着重启,打开日志文件看尾部:
tail -n 50 ~/.vnc/*:1.log我遇到最多的三种启动失败情况,这里整理成一张排查表:
| 错误现象 | 根因 | 解决办法 |
|---|---|---|
A VNC server is already running as :1 | 显示号被占用,或某个残留锁文件存在 | 用vncserver -list查看;若残留锁文件,删除/tmp/.X11-unix对应的文件 |
xauth: file ... does not exist | 家目录的.Xauthority不存在或权限不对 | 确保以 vncuser 身份运行 vncserver,或在 xstartup 中额外生成 xauth |
dbus-launch相关报错导致桌面起不来 | 缺少 dbus-x11 包或系统总线未初始化 | 安装dbus-x11,xstartup 中显式调用dbus-launch |
5.2 黑屏、灰屏的排查路线
连接后如果是灰屏或者黑屏,VNC 服务端本身是正常的,问题基本出在 xstartup 和桌面环境上。排查路线按优先级是这样:
第一,确认.vnc/xstartup是否有执行权限。没有x权限,脚本根本不会被执行,会话就是个空壳。
第二,确认 xstartup 里的桌面环境启动命令存在。比如用了startxfce4,但系统根本没装 XFCE,命令找不到,桌面自然起不来。可以用which startxfce4或者在#里直接写绝对路径/usr/bin/startxfce4。
第三,检查日志里是否有Could not create directory之类的权限错误。这类问题通常是因为用 root 启动了 vncserver,但 xstartup 又是普通用户配置的,目录归属不一致。
第四,注意会话变量清理。很多桌面环境在启动时需要干净的SESSION_MANAGER和DBUS_SESSION_BUS_ADDRESS,不清空可能导致会话启动一半就挂掉。这也是为什么 xstartup 开头先unset这两个变量的原因。
5.3 性能调优参数
无头服务器的 VNC 体验,很大程度上取决于分辨率和色彩深度的设置。默认vncserver的分辨率可能是 1024x768,客户端窗口看着小,操作体验也不好。我常用的启动参数组合是这样:
vncserver :1 -localhost no -geometry 1920x1080 -depth 24 -compressionlevel 6 -quality 8分辨率1920x1080配合客户端全屏模式,视觉上没有物理屏的差距。-depth 24提供真彩色,适合要看图形报表、图表、视频画面的场景。如果服务器性能紧张,改用-depth 16可以显著减少网络传输量。连接卡顿严重时还可以压低-quality或调低 JPEG 压缩质量,这个是在客户端加载缓慢时最有效的几个手段之一。
还有一个实践经验:如果桌面里开了很多 3D 加速或合成器效果,比如 GNOME 的默认动画、透明效果,VNC 的传输压力会增大。轻量桌面如 XFCE 在这种场景下明显更顺滑。如果必须用 GNOME,可以在设置里关闭动画,或者用dconf关闭 window manager 的合成效果。这个细节在实际使用中经常被人忽略,却是提升流畅度最直接的方法。
5.4 会话断开后重连与资源清理
VNC 连接突然断开是常有的事,比如客户端电脑休眠、网络闪断。服务端会话一般不会立刻终止,只是客户端掉了,重新连接同一端口就能回到原桌面会话,正在运行的程序都还在。这一点比远程重启服务要友好很多。
但如果手动vncserver -kill :1之后,发现端口还占着,或者再启动时报身份不对,可以用以下命令清理:
sudo pkill Xvnc rm -rf /tmp/.X11-unix/X1 /tmp/.X1-lock然后再启动 VNC。这里要提醒一句:pkill会把该用户下所有 VNC 会话全杀掉,如果有多个会话在跑,先确认再执行,别误伤别的用户桌面。
5.5 离线环境顺手补装的常用包
最后分享一个让我省心的经验:既然都要离线装 VNC 了,我通常会顺手把其他可能用到的图形工具依赖一起下载好,避免以后每次要装个小工具都重新走一遍下载拷贝流程。适合提前准备的有:
x11-apps:包括xclock、xeyes等基础图形测试工具,调试 VNC 是否正常很快xterm:VNC 里默认终端,故障排查时很有用firefox或chromium-browser:如果远程桌面是为了操作 Web 页面,浏览器少不了fonts-noto-cjk:中文显示需要的字体,不装的话远程桌面上所有中文全是方块system-config-printer、gnome-system-monitor这类图形管理工具,按需带
这些包在下载机上同样用apt-get install --download-only准备好,拷贝到目标服务器后统一安装。别看每个包都不大,关键时候能省掉一次来回跑的麻烦。
我在实际项目里做过一次完整的离线部署,当时目标服务器不但没有外网,连 DNS 都不通,所有操作只能靠 U 盘传递 deb 包。第一次因为下载机用的架构和服务器不一致,装到一半发现依赖版本对不上,全部重来;第二次又因为是 root 用户的 xstartup 配置在普通用户下执行,黑屏了半小时。后来把 systemd 服务、用户隔离、xstartup 模板都整理固定之后,复制到任何一台同类服务器上,基本五分钟内就能把远程桌面拉起来。这个套路现在已经成了我处理内网图形环境需求的首选,也希望能帮你少踩几个坑。