0. 前言
本教程解决的问题:如何连接远程的无头服务器,并实现 GPU 加速渲染,解决远程服务器由于没有图形化界面导致渲染结果无法可视化或可视化卡顿的问题。
所谓“无头”即headless,指的是没有任何外接显示屏和鼠标键盘的计算机。
先叠个甲,因每个人的硬件设备和软件环境不同,本教程不一定对所有人都有效,也可能存在更加优雅的解决之道。本人目前也处于知其然而不知其所以然的状态,只是借助互联网的力量成功解决了问题,加以整理和记录,算是站在巨人们的肩膀上。
1. 问题描述
最近在复现一篇论文,使用到了Isaac Gym这样一个强化学习的仿真环境。Isaac Gym支持GPU同时运行多个仿真环境并使用GPU进行训练,可以大大提高训练的效率。由于需要使用GPU,所以自然要在远程服务器上部署了,所使用的设备和软件为一台8卡的A6000服务器,系统为Ubuntu20.04,平时使用ssh的方式连接服务器调试代码训练模型。然而仿真环境的调试不能只通过终端的输出,还需要查看渲染的结果,这对于没有连接显示器的远程服务器比较棘手。后来尝试了MobaXterm自带的X11 Forwarding实现画面的传输,虽然成功得到了Isaac Gym输出的图形化界面,但是异常卡顿,帧率只有5 FPS左右,并且服务器的GPU和CPU都是极低的占用(5%左右)。在经过长达半个多月的努力,最终使用TurboVNC+VirtualGL成功解决了问题。现在打算在另外一台3卡的3090远程服务器上进行复现,并记录过程。
2. X11 Forwarding
2.1 原理介绍
这里需要介绍一些重要的概念:X11,GLX,OpenGL
Linux系统的图像界面和Windows不一样,叫做 X Window,采用的是X11协议。X11 中的 X 指的就是 X 协议;11 指的是采用 X 协议的第 11 个版本。它规定了Xserver
和Xclient
通信的方式,给GUI程序提供了使用显示器,显卡,键盘,鼠标等设备的能力。
Xserver
是实现了X11服务端协议的进程,比如 Xorg
程序,它负责维护一个 Display
(=显示器+鼠标+键盘+显卡),允许 Xclient
程序使用这个 Display。它把它维护的 Display 包装成服务提供给 Xclient
程序使用,所以它是 server 端。既然是 server 端,它当然允许远程任意设备上的 Xclient
程序来连接它,使用它提供的显示服务。比如运行在远程服务器上的Isaac Gym进程就是一个 Xclient
,可以连接本地PC的 Xserver
,从而将渲染界面显示在本地PC上。
整个流程如下图所示,简而言之:X Server是运行在有显示器的设备上的,X client 是输出渲染画面的进程,两者可以通过网络通讯,也可以存在同一个物理设备中。
X11最重要的一个扩展是 GLX
扩展, GLX API 用于管理 OpenGL 渲染上下文和应用程序窗口之间的关系,OpenGL 负责生成 3D 图形,而 GLX 则负责将这些图形嵌入到 X Window 系统 中并进行显示。GLX有两种渲染方式,一种是“直接渲染”,如下图右FIGURE2,OpenGL指令直接发送到3D Driver进行硬件渲染。还有一种就是“间接渲染”,如下图左FIGURE1,OpenGL指令先发给X server,然后X server转发给X server所在机器的3D Driver。对于我的远程使用场景,X Server是在本地PC运行的。Application就是我的Isaac Gym进程,与X Server通过X11协议通讯,渲染任务是由本地的X server发送给本地的驱动,然后调用GPU进行渲染。
2.2 MobaXterm x11-forwarding
MobaXterm 是一个强大的远程终端应用程序,集成了多种功能,如 SSH 客户端、X11 服务器和网络工具。这里的X11服务是通过SSH进行的,称为x11-forwarding,本质也是X11协议,只是借助了SSH进行加密传输。首先在官网下载并安装MobaXterm :
https://mobaxterm.mobatek.net/download-home-edition.html
然后在远程服务器中安装X11相关的包:
sudo apt update
sudo apt install xauth xorg openbox
确保 SSH 服务器启用了 X11 转发。编辑 /etc/ssh/sshd_config
文件,确保以下配置项没有被注释,并且值为 yes
:
X11Forwarding yes
AllowTcpForwarding yes
然后记得重启SSH刷新配置文件。然后打开 MobaXterm,确保 X11 服务器已启用。可以在 MobaXterm 的主界面上看到 "X11 server" 按钮,点击它以确保 X11 服务器正在运行。
配置完成后,连接服务器,正常会显示下面的内容:
右上角可以对X server进行配置,自行探索,默认即可。
MobaXterm的终端中输入xclock
如果配置正确,就会显示一个时钟窗口。
2.3 问题
上述方式可以成功将Isaac Gym的渲染结果显示在本地的桌面,但是帧率非常低,只有3-6fps左右。这可能有两方面原因:
- GLX 间接渲染的方式,使得渲染的工作是在本地PC运行的,虽然我本地是一张Titan X,但是可能由于驱动的问题没有成功使用本地显卡渲染,而是用CPU。
- 同时如果渲染的工作量比较大,这种通过X11远程的方式传输GLX指令给本地,然后本地再渲染,容易出现延迟和带宽受限。
当然以上只是我在摸清整个流程和原理之后的猜想,并没有进行验证,但总之,上述这种X11间接渲染非常不优雅。我所想的是可以在服务器同时实现算法的运行和仿真环境的渲染,这就需要VirtualGL了。
3. TurboVNC+VirtualGL
3.1 原理介绍
如上图所示,可以看到X Server和X Client(Application)都在同一个设备上,VirtualGL的原理简单来说,就是在服务器端(Application Server)虚拟了一个X Proxy,这可以看做是远程Client在Application Server上的一个虚拟代理。X Proxy用于接受Client发送的各种指令(鼠标键盘),然后将这些指令转换成X11事件,并发送给X Client处理。X Client处理完成后,会返回渲染后的像素图片,然后X Proxy将像素图片通过网络传递到真正的Client端显示。
可以看到,此时进行的是直接渲染,libGL直接向本地的3D Driver发送OpenGL指令,并接受返回的渲染图片。再来看X Client内部的变化,多了一个GLX Interposer,可以简单把它看成一个OpenGL指令的拦截路由器。它会监听lib GL发出的OpenGL指令,并将3D渲染相关的OpenGL指令翻译成对应的GLX命令发送给3D X server,2D的就发送给2D X server(图中没画出)。这样就实现了在Application Server端的X11 Server显示3D 渲染画面。同时由于GLX指令是GlX Interposer发送的,它也就能知道什么时候应该更新画面,于是就能实时得调用 XputImage()
函数将X11 Server显示的3D画面传给X Proxy,最终传输给真正的远程客户端。
相比于原来的X11 Server,这种模式使得渲染的任务都在服务器上完成了,并且使用的还是直接渲染的方式,因此在服务器上的X11 Server几乎没有损耗和延迟。同时,X Proxy的存在使得 X11 Server上的画面可以同步到远程的某个客户端上,其实就类似于远程投屏。客户端只有一些网络上的带宽压力和解码压力,这点压力只能说轻轻松松了。
下面进入实操环节。
3.2 安装OpenGL图形库
由于是在服务器进行3D渲染,所以服务器上肯定需要OpenGL图形库,假定你的服务器也是Ubuntu20.04,逐步运行下面的指令进行安装:
-
安装基本编译环境
sudo apt-get install build-essential
-
安装OpenGL Library
sudo apt-get install libgl1-mesa-dev
-
安装OpenGL Utilities,这是一组建构于OpenGL Library 之上的工具组,提供许多很方便的函式,使OpenGL 更强大且更容易使用。
sudo apt-get install libglu1-mesa-dev
-
安装OpenGL Utility Toolkit 这是建立在 OpenGL Utilities 上面的工具箱,除了强化了 OpenGL Utilities 的不足之外,也增加了 OpenGL 对于视窗界面支援
sudo apt-get install freeglut3-dev
-
安装glew,这是一个跨平台的C++库,是一个OpenGL图形接口扩展库
sudo apt-get install libglew1.8 libglew-dev
-
安装glx,这是linux下OpenGL的X Window System接口扩展库,它允许通过x调用OpenGL库
sudo apt-get install libgl1-mesa-glx
-
补充有的也可能需要安装Xmu,即X11 miscellaneous utility library(X11实用工具库)
sudo apt-get install libxmu-dev
3.3 安装并配置VirtualGL
如果你英语不错,并且想客制化配置,并了解整个安装过程,或者在尝试过下面的安装步骤后无效,可以自行阅读官方安装教程:https://rawcdn.githack.com/VirtualGL/virtualgl/main/doc/index.html#hd004001
VirtualGL下载网站:https://sourceforge.net/projects/virtualgl/files/,选择最新版的安装包,ubuntu系统推荐使用deb安装包。下载后运行下面的命令安装:
dpkg -i virtualgl*.deb
apt install -f
然后在服务器上虚拟一个 X-window出来:
# https://virtualgl.org/Documentation/HeadlessNV
# 查看显卡的busid
nvidia-xconfig --query-gpu-info
# 在对应的显卡上创建X-window
sudo nvidia-xconfig -a --allow-empty-initial-configuration --use-display-device=None
--virtual=1920x1080 --busid {busid} # busid换成你自己显卡的
配置从VirtualGL启动3D X-server,这个过程中需要关闭桌面环境,注意如果你的显示器直接连着你的屏幕,此时就会黑屏,建议使用ssh连接服务器配置:
service gdm stop
配置VirtualGL:
/opt/VirtualGL/bin/vglserver_config
# 选择 1 configure server for use with VirtualGL in GLX mode
# 途中需要选择一些选项,全部选No即可,考虑安全也可以自己配置,查看官方文档
执行下面的指令进行测试:
xdpyinfo -display :0
/opt/VirtualGL/bin/glxinfo -display :0 -c
正常来说会输出类似下面的内容:
visual x bf lv rg d st colorbuffer ax dp st accumbuffer ms cav drw
id dep cl sp sz l ci b ro r g b a F bf th cl r g b a ns b eat typ
------------------------------------------------------------------------------
0x151 24 tc 0 32 0 r y . 8 8 8 0 . 4 24 8 16 16 16 16 0 0 None PXW
如果出现问题,可以删除当前配置,翻阅官方文档:https://rawcdn.githack.com/VirtualGL/virtualgl/main/doc/index.html#hd004001
/opt/VirtualGL/bin/vglserver_config
# 选择 2 Unconfigure server for use with VirtualGL in GLX mode
3.4 配置TurboVNC
同样从Source Forge下载对应版本的安装包
https://sourceforge.net/projects/turbovnc/files
参照官网文档,使用dpkg安装TurboVNC
# https://turbovnc.org/Documentation/Documentation
dpkg -i turbovnc*.deb
启动服务,首次启动时需要设置密码
/opt/TurboVNC/bin/vncserver
启动成功后,可以得到如下结果。注意记下最后一个数字。当开启多个Turbovnc服务时,使用最后的数字区分不同屏幕。
Desktop 'TurboVNC: XmechGPU:1 (my_user)' started on display XmechGPU:1
启动成功后,除非使用下方命令手动关闭,服务一直会运行。其中n为上面的数字。
/opt/TurboVNC/bin/vncserver -kill :n
3.5 从windows或mac远程访问服务器
然后就是在本地安装VNC客户端连接远程服务器的X Proxy,可以使用任意的VNC客户端,我用的TightVNC Viewer,因为之前就用过VNC,你也可以使用官方提供的TurboVNC Viewer。因为这里都是Viewer,只是一个显示的软件,所以无所谓,但是在服务器上需要安装TurboVNC而不是其他的VNC。因为TurboVNC是专门为VirtualGL定制的,稳定性和性能都会高一点。
TurboVNC Viewer的安装网站:https://sourceforge.net/projects/turbovnc/files
下面我展示的是TightVNC的登录界面,其实都大同小异,只要输入服务器的IP,然后加上端口号就能连接,端口号是5900+n,n指的是你开启TurboVNC输出的数字,代表几号屏幕,密码就是上面配置TurboVNC时的密码。
4. Reference
[1] 博客 X Window 的 OpenGL 扩展 —— GLX
[2] 博客:mobaxterm x11 转发Ubuntu mac
[3] VirtualGL Official Background
[4] TurboVNC+VirtualGL:实现服务器的多用户图形化访问与硬件加速