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 建立連線。

最終是還是失敗了 😢 。

(本文純屬技術實驗性質)