用于编译任务的非特权 LXC 容器(三):在 LXC 中运行完整桌面环境

Tips:这是我关于非特权 LXC 的第三篇文章。如果对如何配置 LXC 还有疑问,可以移步至 这里

我发现很多窗口合成器都支持嵌套模式,即合成器本身作为一个桌面应用,在父合成器中运行。那么,我们能否像在虚拟机中那样,在 LXC 容器中运行一个全新的桌面环境呢?答案是肯定的。

Wayland Socket 的配置

首先需要了解的是,Wayland 使用 socket 进行通信。要获取宿主机上的 Wayland socket 路径,可以执行以下命令:

ls $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY

为了让容器能够访问宿主机的 Wayland socket,我们需要在 LXC 容器的配置文件中添加挂载项。例如:

lxc.mount.entry = /run/user/1000/wayland-1 dev/wayland-1 none bind,optional,create=file 0 0

其中 /run/user/1000/wayland-1 是宿主机上的 Wayland socket 路径。这里我将 Wayland socket 映射到了容器的 /dev 目录下,而不是直接映射到容器内的 $XDG_RUNTIME_DIR 路径。原因在于,$XDG_RUNTIME_DIR 实际上是一个挂载为 tmpfs 的文件系统,如果直接挂载到该路径,用户登录后映射的文件会被覆盖。

为了将 Wayland socket 正确暴露在容器内的 $XDG_RUNTIME_DIR 中,我们可以创建一个软链接。虽然可以手动操作,但更推荐使用 systemd 用户服务 实现自动化。

创建文件 ~/.config/systemd/user/wayland-mount.service,内容如下:

[Unit]
Description=Link Wayland socket to user runtime directory
After=systemd-user-sessions.service

[Service]
Type=oneshot
ExecStartPre=/usr/bin/mkdir -p /run/user/1000
ExecStartPre=/usr/bin/chmod 700 /run/user/1000
ExecStart=/usr/bin/ln -sf /dev/wayland-1 /run/user/1000/wayland-1
RemainAfterExit=yes

[Install]
WantedBy=default.target

完成上述配置并重启容器后,就可以在容器内体验图形环境了。

单一图形应用的效果

记得启动应用前要设置 WAYLAND_DISPLAY 环境变量。我们的容器的 Wayland socket 来源于宿主机,而启动容器时不会继承宿主机的环境变量,因此需要手动设置。

图形加速支持

目前启动完整桌面环境的效果可能并不理想,因为缺少 GPU 加速,许多动画效果无法正常显示。我们可以将 GPU 设备映射到容器中。

以下是我使用的配置示例:

 DRM render 节点
lxc.mount.entry = /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file 0 0

 DMA-BUF Heaps(用于零拷贝缓冲区共享)
lxc.mount.entry = /dev/dma_heap/system dev/dma_heap/system none bind,optional,create=file 0 0

配置完成后,即可在容器中启动各种窗口合成器甚至完整的桌面环境。我测试了 cageswaynirilxqt(使用 labwc)、xfce(使用 labwc)以及 KDE Plasma 等,均表现良好。

下面的图片是 KDE Plasma 的示例:

我使用的是 WhiteSur 主题

启动命令为:

WAYLAND_DISPLAY=wayland-1 startplasma-wayland

剪切板同步(可选)

如果你使用过 Waydroid ,可能会对其剪切板同步功能印象深刻。Waydroid 使用 Binder IPC 实现容器内外的剪切板同步。

而在我们的设置中,LXC 容器的 rootfs 对宿主机可见,且文件所有者一致,因此实现剪切板同步更为简单,甚至无需编写复杂的 Python 程序,使用简单的 Shell 脚本即可完成。

以下是我的配置示例:

我的宿主机使用 wl-clipboardcliphist 管理剪切板历史,历史文件位于 ~/.cache/cliphist/db。我将整个 ~/.cache/cliphist/ 目录映射到容器中:

lxc.mount.entry = /home/acd407/.cache/cliphist home/acd407/.cache/cliphist none bind,optional,create=dir 0 0

在容器中,我使用 KDE 桌面环境。首先禁用 KDE 默认的剪切板管理器 Klipper,然后在 KDE 设置的自动启动项中添加 wl-clipboard 启动脚本:

wl-paste --type text --watch cliphist store
wl-paste --type image --watch cliphist store

剪切板选择同步(可选)

除了基本同步外,我的宿主机还支持通过 rofi 选择下一次粘贴的内容。要想让宿主机 rofi 选择器的结果作用到容器中,还是需要一些额外的设置。

以下是我的实现方案:

while true ; do
    result=$(rofi -dmenu \
        -kb-custom-1 "Control-Delete" \
        -kb-custom-2 "Alt-Delete" \
        -config ~/.config/rofi/wide.rasi < <(cliphist list))
    case "$?" in
        1)
            exit ;;
        0)
            case "$result" in
                "")
                    continue ;;
                *)
                    cliphist decode <<<"$result" | tee ~/.cache/cliphist/current | wl-copy
                    exit ;;
            esac ;;
        10)
            cliphist delete <<<"$result" ;;
        11)
            cliphist wipe ;;
    esac
done

在选择内容后,我通过 tee 命令将其写入 ~/.cache/cliphist/current 文件。由于该目录已映射到容器中,我们可以在容器中监听该文件的变化,并在内容更新时同步到容器的剪切板:

FILE_TO_WATCH="$HOME/.cache/cliphist/current"
inotifywait -m -e close_write --format "%w%f" "$FILE_TO_WATCH" | while read file; do
    cat "$file" | wl-copy
done

其中 inotifywait 来自 inotify-tools 包,用于监听文件变化。

总结

现在,我们就有了支持运行桌面框架的 LXC 容器了。下一次再想测试某一个桌面环境好不好用、某一个主题好不好看,就不用再装虚拟机了。

其实,映射 Wayland socket 来让容器支持图形界面的方法也是通用的,大家也可以试一试 Docker 等容器,操作流程应该是差不多的。

posted on 2025-12-14 23:01  acd407  阅读(3)  评论(0)    收藏  举报

导航