megrxu

远程运行的 X Server

Dec 14, 2020  「DevOps」  

配置好能够使用 GPU 的容器之后,突然想到是否可以使用高性能 GPU 来进行 Steam Link 呢。

使用远程 GPU 来启用 Xorg Server

X11 Forward

Xorg 是 C/S 架构的,原生支持通过网络来进行转发。然而,X11 Forward 实际上是使用本地硬件资源进行渲染的。当在自己的电脑上运行 X11 转发的 Steam ,另一台电脑上登陆的 Steam 则会立即显示「远程畅玩可用」。这时候使用的仍然是本地资源,并不是我们期望的。因此,要想让使用远程 GPU 进行渲染,那么就必须在远程启用 Xserver。

Remote Xserver

首先需要配置合适的 xorg.conf。和通常的配置文件并无太多不同。

  1. 设备模块。
    1
    2
    3
    4
    5
    6
    
    Section "Device"
        Identifier     "Card0"
        Driver         "nvidia"
        BusID          "PCI:175:0:0"
        Option "AllowEmptyInitialConfiguration"
    EndSection
    
  2. 屏幕模块。自定义屏幕的 DPI ,否则就会使用默认的超小 DPI。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    Section "Screen"
        Identifier     "Screen0"
        Device         "Card0"
        Monitor        "Monitor0"
        DefaultDepth    24
        Option         "AllowEmptyInitialConfiguration"
        Option         "HardDPMS" "false"
        SubSection     "Display"
        Depth       24
        Virtual     2560 1440
        Option      "DPI" "144x144"
        EndSubSection
    EndSection
    

然而发现在非特权模式下,容器内部始终无法启动 Xserver,似乎必须拿到 /dev/tty0 的读写权限才行。于是为了保持非特权模式的容器,最终选择在宿主机启动 Xerver,然后将其转发到内部网络中,再将容器内部的 DISPLAY 变量设置为外部宿主机启动的 Xserver 处。

可以使用 socat 将 unix socket 转为 TCP(或者直接在 LXC 配置中挂载进容器):

1
socat -d -d TCP-LISTEN:6000,fork,bind=172.16.0.1 UNIX-CONNECT:/tmp/.X11-unix/X0

启动 Xserver 之后,可以看到 nvidia-smi 已经有了 Xorg 这个程序在跑了。

之后,就可以使用外部的 GPU 来渲染容器上跑的 GUI 程序了。简单的测试如 xclock

Indirect GLX 以及跑 32 位 的 Steam

然而,如果跑 GLX 程序,如 glxgears 时,则会报错。需要做的改动有两点:

  1. xorg.conf File 模块处添加 Nvidia 的 Xorg modules 的路径:
    1
    2
    3
    4
    
    Section "ServerFlags"
        Option "AllowIndirectGLX" "on"
        Option "IndirectGLX" "on" 
    EndSection
    
  2. xorg.conf ServerFlags 处启用 Indirect GLX:
    1
    2
    3
    4
    
    Section "Files"
        ModulePath      "/usr/lib/xorg/modules"
        ModulePath      "/usr/lib/x86_64-linux-gnu/nvidia/xorg"
    EndSection
    

这下就可以运行 glxgearsglxinfo 等程序了。那么是不是 Steam 也可以跑了呢?答案是否定的。Steam 一直以来都是 32 位的,而最新版的驱动都没有再支持 32 位的 GLX。Debian stable 上当时的 libglx-nvidia0:i386 包的版本是 418.152,与实际安装的驱动版本 455.23.05 差了很多。正当想放弃时,突然想显卡其实已经被驱动起来了,那么在容器内部也就没有必要执着于使用最新的驱动(无非只是一堆文件而已)。这样看来,容器化甚至成了一个好处,可以使得容器内选择性使用兼容的低版本库函数。

于是将容器内部的 libglx-nvidia0 包及其依赖统一替换为了有 i386 版本的低版本。终于可以将 32 位版本的 GLX 程序跑起来啦。

最后需要做的一步是组网。Steam Link 要求两台设备必须在一个局域网之内。熟悉起见使用了 Wireguard 。 然而最后虽然已经确定在一个网段内了,还是无法正常使用 Steam Link ,原因出在无法连接一个公网端口上面,感觉有可能是需要两台设备有同样的公网出口来和 Steam 建立连接。

最终是还是失败了 😢 。

(本文纯属技术实验性质)