背景介绍

日常开发中,经常需要远程操作 Linux 服务器,并在其中的容器里运行 X11 图形应用程序(如 xclockmatplotlib 等)。典型的操作场景为:

从 Windows PC 上使用 SSH 登录到一台 Linux 服务器(服务器本身没有 X Server),而服务器又通过容器(使用桥接网络)运行了需要图形界面的程序。
Windows PC (X Server) <===> Linux Server <===> Container (X11 Apps)
                       SSH           Docker/Podman

理想情况下,我希望 X11 图形应用能够通过这两层:

  1. 从容器“跳”到服务器;
  2. 再从服务器通过 SSH 转发到 Windows PC 上的 X Server(例如 MobaXterm)。

然而,容器的独立网络环境、X11 的访问控制以及 DISPLAY 配置的复杂性,常常导致 X11 应用无法正常显示。经过一番探索,笔者找到了一套高效的解决方案。

注意:本文要求读者具备基本的 X11 相关知识,可移步笔者另一篇 博文 获取基本的操作知识。

配置方案

要实现这一需求,我们需要确保以下三点:

  1. 服务器的 SSH X11 转发正常工作,并能监听外部接口;
  2. 容器内能够访问服务器的 X11 转发端口
  3. 容器中配置与服务器一致的 Xauth Cookie,以完成 X11 的访问认证。

具体步骤如下:

1. 在服务器上配置 SSH X11 转发

首先,我们需要确保服务器的 SSH 服务可以正确转发 X11 图形。编辑服务器上的 sshd_config 文件,添加以下内容:

X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost no

其中,X11UseLocalhost no 是关键配置,它使得 X11 转发端口监听在所有网卡上(0.0.0.0),而不仅仅是 127.0.0.1

保存配置后,重启 SSH 服务:

sudo systemctl restart sshd

接下来,从 Windows 使用 ssh -X(或 ssh -Y)登录服务器,获取 $DISPLAY 变量的值:

ssh -X youruser@server_ip
echo $DISPLAY

如果 $DISPLAY 不为空,说明环境变量自动配置正常,例如:192.168.1.33:10.0。基于该值验证 X11 转发是否正常。

首先通过 netstatss 命令确认端口监听状态:

netstat -antp | grep 6010
注意,6010 = 6000 + 10。如果 $DISPLAY中的 offset 不为 10,请自行调整命令。

然后运行一个简单的 X11 应用进行测试:

xclock  # 应该能在 Windows X Server 上显示时钟窗口

2. 在容器中配置 DISPLAY 和网络

容器和服务器之间使用桥接网络模式,因此容器和服务器的 IP 并不相同。下文假设容器可以访问到服务器的(某个)IP:192.168.1.33。

进入容器后:

  1. 设置 DISPLAY 环境变量,指向服务器的 IP 和 DISPLAY 号:

    export DISPLAY=192.168.1.33:10.0

    其中 10.0 是 SSH 分配的 DISPLAY 号,需要与服务器上的一致。

  2. 如果需要测试网络连通性,可使用 telnetnc 测试:

    telnet 192.168.1.33 6010

3. 在容器中设置 Xauth Cookie

X11 的认证使用 MIT-MAGIC-COOKIE-1 机制,因此容器内需要与服务器相同的 Xauth Cookie。以下是两种方法:

方法 1:直接复制服务器上的 .Xauthority 文件

服务器(宿主机) 上当前用户的 .Xauthority 文件复制到容器内:

podman cp ~/.Xauthority your_container:/root/.Xauthority

然后,在 容器 中设置 XAUTHORITY 环境变量:

export XAUTHORITY=/root/.Xauthority
该方案中,由于你直接复制了 Cookie,容器内的 DISPLAY 变量必须和服务器上的完全一致

方法 2:手动添加 Cookie

服务器(宿主机) 上运行 xauth list 查看当前会话的 Cookie:

xauth list

例如,输出如下:

localhost/unix:10  MIT-MAGIC-COOKIE-1  abcdef1234567890abcdef1234567890
localhost:10  MIT-MAGIC-COOKIE-1  abcdef1234567890abcdef1234567890

容器 中使用 xauth add 命令手动添加:

xauth add 10.88.0.1:10  MIT-MAGIC-COOKIE-1  abcdef1234567890abcdef1234567890
该方案中,你可以将 10.88.0.1 替换为任何可以访问到宿主机的 IP/域名,例如 host.containers.internal

4. 测试 X11 应用程序

配置完成后,在容器中运行 X11 图形应用程序:

xclock

如果所有设置正确,xclock 的窗口应该能显示在 Windows 的 X Server 上。

后记

上次折腾 X11 可能还是 2022 年,三年时光弹指一瞬……

总之,通过以上方案,笔者成功在远程服务器的容器中运行 X11 应用,并将图形无缝地显示在 Windows 上。希望这篇文章能为遇到类似需求的读者提供一些帮助!

拓展阅读