1. 项目概述与部署思路
1.1 这个任务要解决什么问题
前段时间接了个需求,要给一台无显示器的 openEuler 20.03 服务器配远程图形桌面。很多刚入行的朋友可能觉得这不就是个 VNC 嘛,装一下不就行了。但实际上,服务器没插显示器、没有图形环境、只有一个最小化安装的系统,VNC 服务装上去之后能不能把你想要的桌面显示到客户端,这里面每一步都有不少讲究。
先说场景。openEuler 20.03 是华为开源的一款服务器操作系统,RPM 系,默认安装基本不带图形界面。平时大家操作它基本都是纯 SSH 命令行,但有些场景确实需要图形桌面:比如跑一些只提供 GUI 的配置管理工具、做自动化测试的桌面环境模拟、给非技术人员提供远程图形操作入口,或者单纯想在一个独立的 Linux 桌面上跑企业级应用。这种时候,给服务器装一个可远程访问的图形桌面就很有必要。
我用的是 TigerVNC,这个方案在 RHEL/CentOS 体系里算是标配了。整套流程走下来,可以拆成四步:先给系统装一个可用的桌面环境,再安装并配置 TigerVNC 服务端,然后通过 systemd 把 VNC 服务托管成开机自启的后台服务,最后放行防火墙并让客户端连上来。
1.2 为什么选 TigerVNC 而不是其它 VNC 实现
Linux 下的 VNC 服务端有好几个,常见的除了 TigerVNC,还有 vnc4server、x11vnc、TightVNC。我在 openEuler 上首选 TigerVNC,主要原因有三个。
第一是软件源兼容性。TigerVNC 在这类 RPM 系系统的默认源里就有,直接dnf install就能装,不用额外折腾第三方源,这一点在离线内网服务器上尤其重要。第二是它和 systemd 的配合成熟,RPM 包装完会自带vncserver@.service模板,你只需要改几个参数就能把 VNC 托管成系统服务,这在生产环境里比手动起进程要可靠得多。第三是性能和加密机制都够用,TigerVNC 在做远程桌面协议时对带宽占用控制还算合理,也支持一定程度的加密,局域网内使用完全没有问题。
相比之下,x11vnc是共享当前物理屏幕的方案,适合有真实显示器但想远程操作的场景,对无头服务器并不友好;vnc4server已经多年不怎么更新了,在较新的系统上经常遇到依赖问题;TightVNC更偏向 Windows 生态,在 Linux 服务端的维护活跃度也不如 TigerVNC。
1.3 整体部署链路
整个部署流程我用一张简单的逻辑链说明:系统安装桌面环境 → 安装 TigerVNC 服务端 → 配置 VNC 用户和密码 → 编写 xstartup 启动脚本 → 通过 systemd 托管服务 → 防火墙放行端口 → 客户端连接。
这里有一条最容易被忽略的原则:VNC 本身只是一个远程显示协议,它只负责把你的键盘鼠标操作传过去、把服务器的屏幕内容传回来,但它自己不会生成桌面。如果系统里连桌面环境都没有,VNC 连上之后只会看到一块黑屏。所以桌面环境必须装,而且要装在 VNC 服务启动之前。
至于桌面环境选哪种,我在 openEuler 上测试过 GNOME 和 Xfce 两条路线。GNOME 是系统的默认图形环境,功能完整,但安装体积大、资源占用高;Xfce 比较轻量,远程桌面卡顿感更小。对于远程桌面这种使用场景,我的建议是如果服务器配置一般或者内存不大,优先选 Xfce,后面我会给出两种环境的安装命令。
2. 环境准备与依赖安装
2.1 确认系统版本与基础网络配置
在动手之前,先确认一下系统版本和状态。登录服务器之后,执行下面几条命令:
cat /etc/openEuler-release uname -a ip addr | grep inet ping -c 3 www.baidu.com第一条命令确认是 openEuler 20.03 以及具体的 LTS 小版本,第二条看内核架构,第三条确认 IP 地址,第四条测一下外网连通性。如果服务器在内网无法访问外网,后面用dnf install安装软件时会需要用本地镜像源或者离线 RPM 包,这个先了解清楚,免得中途抓瞎。
确认网络没问题后,顺便把系统软件包索引更新一下:
dnf update -y更新这一步建议做,但也要注意,如果你的服务器承载着生产业务,更新内核或者系统核心库有风险,可以选择只更新索引而不升级具体软件包,或者干脆跳过。我在实际操作中一般只执行dnf makecache,把缓存更新一下,然后直接装软件。
2.2 安装桌面环境(GNOME 与 Xfce 两条路线)
桌面环境是 VNC 能显示内容的前提。openEuler 20.03 的默认源里带了 GNOME 相关的软件组,你可以用dnf group list看一下有哪些组可用。
安装 GNOME 的命令:
dnf groupinstall "GNOME" -y如果 group 名称不匹配,可以试试dnf groupinstall "GNOME Desktop"或dnf install gnome-session gnome-terminal nautilus等核心包。不同小版本对软件组名称的定义会有些差异,以dnf group list实际显示为准。
如果你决定走轻量路线,安装 Xfce 的命令是:
dnf install epel-release -y dnf groupinstall "Xfce" -y注意:在 openEuler 上,Xfce 相关软件包有一部分不在默认的 BaseOS 源里,需要先安装epol-release或者开启 openEuler 的 EPOL 源。EPOL 是 openEuler 官方维护的扩展软件仓库,类似 CentOS 里的 EPEL,里面有很多默认源没有的软件包。如果执行dnf groupinstall "Xfce"时报“No group data available”,大概率就是扩展源没有配好。
这里有个经验点:对于只用来跑 VNC 远程桌面的服务器,我不建议装完整的 GNOME。GNOME 全家桶装完要占好几个 GB 的磁盘空间,运行起来内存占用也不低。Xfce 加基础工具链差不多几百 MB 就搞定,远程操作时的响应速度会明显更快。如果你拿不准自己的服务器该选哪个,先看看机器配置:内存小于 4GB 或者磁盘小于 40GB 的,闭眼选 Xfce。
2.3 安装 TigerVNC 服务端
桌面环境装好后,终于可以安装主角 TigerVNC 了。在 openEuler 20.03 上执行:
dnf install tigervnc-server tigervnc -y其中tigervnc-server是服务端,tigervnc这个包里包含 vncviewer 客户端和一些配套工具。如果只想跑服务端,只装tigervnc-server也够,但把客户端一起装上,方便在服务器本机测试服务是否正常,避免一上来就被“到底是服务端问题还是客户端问题”给绕进去。
确认安装结果:
rpm -qa | grep tigervnc vncserver --version看到版本号输出就说明安装成功。在 openEuler 20.03 上,TigerVNC 的版本通常在 1.9 以上,1.9 之后的 TigerVNC 在配置文件的组织方式上有了一些变化,支持~/.vnc/config这种纯文本配置方式,用起来比之前一堆命令行参数清晰很多。
到这里,前置环境已经准备完毕。接下来是配置环节,也是这个任务里最容易出错的部分。
3. 用户配置与服务初始化
3.1 创建专用用户还是复用现有用户
VNC 服务运行的时候,会以某个系统用户的身份启动桌面会话。这里有两个选择:用现有的日常登录用户,或者专门创建一个用户用于 VNC 登录。
我的建议是:单独创建一个专用用户,例如vncuser。这样做的原因很现实:VNC 会话会在用户家目录下生成一堆配置文件、日志和缓存,如果混在真实工作账号里,日积月累会很乱;另外,VNC 会话相当于给客户端开放了一个图形操作入口,如果直接使用 root 或者高权限用户,万一密码泄露,风险面会大很多。专用低权限用户即使被攻破,影响范围也可控。
创建用户的命令:
useradd vncuser passwd vncuser这里设置的系统登录密码不用和后面 VNC 的访问密码一致,两者是独立的两套认证。如果确实想让 root 也能通过 VNC 登录,后面我会单独说怎么处理,但生产环境不推荐。
3.2 设置 VNC 访问密码
切换到 vncuser 身份,设置 VNC 的独立访问密码:
su - vncuser vncpasswd执行后会提示输入两次密码。这个密码就是客户端连 VNC 时需要输入的密码,跟系统登录密码没有任何关系。vncpasswd默认会把密码文件写到~/.vnc/passwd,并且自动设置成仅当前用户可读的权限。
这里有个细节:vncpasswd 执行完,家目录下会多一个.vnc目录。如果客户端连接时提示密码认证失败,先检查这个密码文件的属主和权限:
ls -la ~/.vnc/ chmod 600 ~/.vnc/passwdpasswd文件权限过松(比如 group 或 others 有读权限),TigerVNC 会拒绝读取,这算是一个常见的“冷门坑”。另外别忘了,VNC 密码最长只有 8 位,超过 8 位的输入会被截断,这是 VNC 协议的一个历史遗留限制。
3.3 手动启动一次 VNC 会话,生成默认配置
配置密码后,先手动启动一次 VNC 会话,目的是生成默认的配置文件,并顺手验证安装成果。
su - vncuser vncserver :1这里的:1是显示编号,对应端口号 5901。VNC 的端口规律是 5900 + 显示编号,所以:1就是 TCP 5901 端口,:2就是 5902,依此类推。之所以从 5900 开始算,是因为显示编号 0 通常保留给本地物理显示器。
如果启动成功,输出里会显示类似这样的信息:
New 'localhost.localdomain:1 (vncuser)' desktop is localhost.localdomain:1 Starting applications specified in /home/vncuser/.vnc/xstartup Log file is /home/vncuser/.vnc/localhost.localdomain:1.log此时~/.vnc/目录下会自动生成xstartup文件吗?不一定,有些版本的 TigerVNC 不会自动创建模板,只创建日志文件和密码文件。如果没有 xstartup,VNC 服务虽然能起来,但客户端连上后是黑屏或只有一个空窗口。这个问题我放到第 6 节常见问题里细说。
先停掉这个手动启动的会话,因为后面我们要改用 systemd 方式托管:
vncserver -kill :1看到输出Killing Xvnc process ID XXXXX就说明已经正常停掉了。
4. 编写配置与服务托管
4.1 重点来聊 ~/.vnc/config 和 xstartup
正式让 VNC 跑起来之前,先把两个关键配置文件说透。
第一个是~/.vnc/config。在 TigerVNC 1.9 之后的版本里,VNC 服务端启动时会自动读取这个文件,把里面的每一行当成一个启动参数。比如我想固定会话分辨率和关闭一些不必要的安全限制,可以这样写:
geometry=1920x1080 localhost=no securitytypes=vncauth,tlsvncgeometry:会话分辨率,按需调整。localhost=no:允许非本机地址远程连接。如果保持默认的localhost=yes,外部客户端是连不上的,只能在本机测试。securitytypes:允许的加密和认证方式组合,实际连接时客户端会自动协商。
第二个是~/.vnc/xstartup,这是 VNC 会话启动后要执行的脚本,相当于告诉 Xvnc “你启动显示服务之后,给我把哪个桌面环境跑起来”。如果你的系统装的是 GNOME,xstartup 可以写成:
#!/bin/sh unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS export XDG_RUNTIME_DIR=/run/user/$(id -u) exec /usr/bin/gnome-session如果装的是 Xfce,写成:
#!/bin/sh unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS export XDG_RUNTIME_DIR=/run/user/$(id -u) exec /usr/bin/xfce4-session这里注意几个细节。XDG_RUNTIME_DIR是很多现代桌面应用必须的环境变量,不设置的话启动桌面时经常报 warning,严重的情况会导致桌面直接起不来。unset SESSION_MANAGER和unset DBUS_SESSION_BUS_ADDRESS是为了断开和系统级会话管理器的关联,避免 VNC 桌面和本地桌面互相干扰。
写完后一定要赋予执行权限:
chmod +x ~/.vnc/xstartup这一步漏掉的话,VNC 会话启动时 xstartup 不会被执行,客户端连上就是黑屏,这是远程桌面场景里最常见的问题之一。
4.2 定制 systemd 服务模板
RPM 包装好后,系统里会自带一个 systemd 模板文件。先把它复制一份到/etc/systemd/system/下,再进行修改:
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@.service vim /etc/systemd/system/vncserver@.service打开之后,典型的模板内容是这样:
[Unit] Description=Remote desktop VNC service After=syslog.target network.target [Service] Type=forking User=vncuser Group=vncuser WorkingDirectory=/home/vncuser ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :' ExecStart=/usr/bin/vncserver %i -geometry 1280x1024 -localhost no ExecStop=/usr/bin/vncserver -kill %i [Install] WantedBy=multi-user.target实际操作中最常忘记改的就是User=和Group=,默认模板里是<USER>占位符,如果你只复制不修改,systemd 一旦启动服务会因为没有<USER>对应的用户而直接失败。另外,%i这个变量在 systemd 模板里会被替换成@符号后面的内容,也就是说当执行systemctl start vncserver@:1时,%i就是:1。
上面的ExecStart里保留了一个-geometry 1280x1024,你可以把它改成自己偏好的分辨率。但如果你已经在~/.vnc/config里写了geometry=1920x1080,命令行里的参数也可以去掉,避免两处都设置时造成混乱。
4.3 加载服务并设置为开机自启
配置改完之后,让 systemd 重新加载配置,并启动服务:
systemctl daemon-reload systemctl enable --now vncserver@:1enable --now的含义是“设置开机自启并立即启动服务”,两条命令一次搞定。如果执行完没有报错,用下面的命令检查服务状态:
systemctl status vncserver@:1 ss -tlnp | grep 5901看到Active: active (running)以及端口正常监听的输出,就说明服务端已经起来了。如果状态是failed,别急着改参数,先看日志:
journalctl -u vncserver@:1 -n 50 cat /home/vncuser/.vnc/*.log日志里通常会把失败原因写得很清楚,比如“No such file or directory”多半是 xstartup 路径或桌面环境命令不对,“A VNC server is already running as :1”表示端口被占用。排错我放到第 6 节统一说。
到这里,服务端其实已经可以正常用了。接下来处理防火墙和客户端连接。
5. 防火墙放行与客户端访问
5.1 开放 VNC 端口
openEuler 20.03 默认可能没有启动 firewalld,但为了安全起见,最好确认一下:
systemctl status firewalld如果 firewalld 没在运行,你可以选择直接不用防火墙(内网临时环境常见),也可以启动防火墙后按规则放行。我的建议是无论内网外网,防火墙开了总比不开好,哪怕只是心理安慰。放行 VNC 端口有两种方式。
方式一,按端口放行:
firewall-cmd --permanent --add-port=5901-5905/tcp firewall-cmd --reload方式二,按服务放行(前提是系统里定义了 vnc-server 这个服务):
firewall-cmd --permanent --add-service=vnc-server firewall-cmd --reload查看最终生效的规则:
firewall-cmd --list-all如果你后续还想用 HTTP 或其它服务,记得一起放行。这里有一个要注意的事项:VNC 服务端除了监听 5901,还会在用户目录下生成 X11 相关的临时 socket 文件,路径通常在/tmp/.X11-unix/。有时候即使端口放行了,客户端依然连不上,不妨看看是不是 SELinux 拦截了,可以用getenforce查看 SELinux 状态。如果排错排查到最后确认是 SELinux 拦截,临时关闭可以执行setenforce 0,但生产环境我不建议直接关,除非你能接受安全策略降级带来的风险。
5.2 客户端连接(Windows / Linux / 手机)
服务端就绪后,客户端连接就简单了。我这里讲三种比较常见的。
Windows 客户端,推荐下载 TigerVNC Viewer 或 RealVNC Viewer,打开后输入192.168.1.100:1,回车,弹窗里输入刚才用vncpasswd设置的密码就能进桌面。
Linux 客户端,用同一条命令:
dnf install tigervnc -y vncviewer 192.168.1.100:1手机的 VNC Viewer 客户端也有,App Store 或各大应用市场搜 VNC Viewer 就能找到,内网测试时用起来很方便。
如果客户端连上之后字体模糊或者分辨率不对,一般是 VNC 会话的 geometry 和客户端屏幕比例不一致造成的,可以在~/.vnc/config里把你的真实屏幕分辨率写好,重新启动服务即可。
5.3 安全加固的简单建议
VNC 协议本身的一些安全设计是比较老的,密码只有 8 位有效,传输过程如果不走 TLS 加密,理论上数据包被截获后存在被破解的风险。所以在实际部署时,我有几个建议:
- 不要把 VNC 端口直接暴露到公网,尽量只在可信内网使用。
- 如果必须跨公网访问,优先用 SSH 隧道方式,把 5901 端口包装到 SSH 里。
- VNC 密码定期更换。
- 使用低权限专用用户运行 VNC 服务。
这些规则虽然简单,但能挡住绝大部分不负责任的顺手扫描和恶意尝试。
6. 常见问题与排查实录
6.1 客户端连接成功但黑屏
这是所有 VNC 问题里出现频率最高的一个。连接没有报错,窗口也弹出来了,但里面只有一块灰黑色屏幕,没有任何桌面内容。
排查思路按顺序来:
第一步,确认~/.vnc/xstartup存在且有执行权限:
su - vncuser ls -la ~/.vnc/xstartup chmod +x ~/.vnc/xstartup第二步,确认 xstartup 里写的桌面环境命令确实存在。比如我写的是exec /usr/bin/gnome-session,那先执行which gnome-session看路径是否正确。不同发行版安装 GNOME 后,gnome-session 的路径可能不同,如果包没装全,路径根本不存在。
第三步,重启 VNC 服务再连:
systemctl restart vncserver@:1如果还是黑屏,看一下 VNC 的日志文件:
cat /home/vncuser/.vnc/*.log日志里如果出现类似dbus[1234]: spawn of dbus-daemon failed之类的错误,基本就是 DBus 会话没起来。解决方案就是我在第 4.1 节里写的,给 xstartup 里加上:
export XDG_RUNTIME_DIR=/run/user/$(id -u)还有一个低概率但真实存在的情况:/tmp/.X11-unix目录下有残留的 X socket,导致新的 Xvnc 会话无法正常初始化。处理方式是把所有 VNC 进程杀掉,删掉残留文件再启动:
pkill -u vncuser -f vnc rm -rf /tmp/.X11-unix/X1 /tmp/.X11-lock systemctl start vncserver@:16.2 端口占用与会话残留
VNC 端口被占用是个老问题。在调试过程中,经常会出现手动启动了vncserver :1,然后又用 systemd 启动vncserver@:1,结果其中一个起来,另一个报错。
遇到这个问题,要能分清是哪个进程占用了 5901:
ss -tlnp | grep 5901 ps aux | grep Xvnc如果是手动起的进程占着,用vncserver -kill :1停掉。如果vncserver -kill也杀不掉,直接按 PID kill:
kill -9 PID然后重新启动服务。另外注意,每次systemctl restart vncserver@:1的时候,systemd 模板里的ExecStartPre会先尝试 kill 掉旧的:1会话,但这个逻辑依赖vncserver -kill正常工作。如果 vncserver 命令和实际 Xvnc 进程不匹配,也会出现杀不掉的情况,这时候手动清一次是最快的。
6.3 VNC 密码认证失败与权限问题
客户端连接时提示密码认证失败,但密码明明是刚设置过的。这种情况大概率不是密码本身错了,而是密码文件权限或属主不对。
检查办法:
su - vncuser ls -la ~/.vnc/passwd正常情况下应该是:
-rw------- 1 vncuser vncuser 8 ... passwd如果属主不是 vncuser,或者权限太松,用下面的命令纠正:
chown vncuser:vncuser ~/.vnc/passwd chmod 600 ~/.vnc/passwd还有另一个坑:如果服务器上同时安装了多个 VNC 相关软件,或者你曾经用 root 用户跑过vncpasswd,可能会在/root/.vnc/passwd和/home/vncuser/.vnc/passwd里分别生成密码文件,而 systemd 服务里运行的 User 是 vncuser,它读的是后者。所以设置密码时一定要用对的用户执行。
6.4 分辨率设置
VNC 会话的分辨率有两种方式设置:一是启动时加-geometry 1920x1080参数,二是在~/.vnc/config里写geometry=1920x1080。
如果客户端是全屏连接,希望分辨率自动适配客户端窗口,TigerVNC 也支持,需要在启动参数里加上-geometry对应的动态调整选项。不过不同版本的 TigerVNC 对这个特性的支持程度不一样,我的建议反而是:固定一个你自己屏幕上看着舒服的分辨率,比如笔记本 1366x768,显示器 1920x1080。因为远程桌面场景下,鼠标精确度和键盘输入有网络延迟,如果分辨率自动变化,经常会出现窗口布局错乱、图标位置漂移的问题。
6.5 服务和进程状态速查表
这里把常用运维命令整理成一张速查表,方便实际操作时直接抄:
| 操作 | 命令 |
|---|---|
| 查看服务状态 | systemctl status vncserver@:1 |
| 重启服务 | systemctl restart vncserver@:1 |
| 停止并取消自启 | systemctl disable --now vncserver@:1 |
| 查看 VNC 日志 | cat /home/用户名/.vnc/*.log |
| 查看端口监听 | ss -tlnp | grep 59 |
| 手动启动会话 | vncserver :1 |
| 手动停止会话 | vncserver -kill :1 |
| 清理残留 socket | rm -rf /tmp/.X11-unix/X1 |
| 查看防火墙规则 | firewall-cmd --list-all |
7. 写在最后的一点使用建议
整套 TigerVNC 部署完成后,它在实际使用中给我的感受是:够用,但不完美。远程桌面协议本身属于比较传统的方案,流畅度远不如 RDP 那么顺滑,尤其是在跨公网、高延迟的网络环境下,能明显感受到鼠标移动和键盘回显都有延迟。所以在服务器日常管理这件事上,我个人的倾向是:如果只是执行命令、编辑配置、查日志,SSH 永远是最优解;只有当你确实需要图形界面时,VNC 这种远程桌面方案才派得上用场。
另外再分享一个小技巧:如果你需要同时给多台 openEuler 服务器批量配置 VNC 环境,手动一台台敲命令效率太低了。可以先把整个过程写成 Shell 脚本,脚本里动态创建用户、设置密码、写入 xstartup、配置 systemd 服务文件,然后用 Ansible 之类工具批量推送到目标机器执行。这样一次性搞定整个机房的远程桌面环境,后续再遇到同样需求,脚本一跑,十分钟内全部绿。
在 openEuler 20.03 上安装配置 tigervnc-server 这件事,本质上就是一个“操作系统环境准备 + 远程服务管理”的综合实践。踩过几次坑之后,你会发现它背后的思路和 CentOS/RHEL 系服务器配置其它服务一样:先理解组件之间的依赖关系,再按逻辑链路一步步落地,最后用日志和端口去验证结果。希望这份记录能帮你少走一些弯路。