在上一篇文章 https://blog.glumi.cn/systemd-nspawn-guide 中我聊到了如何使用 systemd-nspawn 来实现 Linux 容器并支持了 GUI,不过后来发现方案并不是很满意,bwrap 也不错,但制作镜像和管理起来又比较麻烦,于是乎又找上了比较有名的 Docker。一开始是翻阅了很久官方文档,但他们的东西写的太多了我找不到核心关键。然后 bing 搜了其他很多帖子发现都是基于 VNC 实现,但这样会存在更明显的性能损失,我想能不能像 bwrap 那样直接呢?然后我翻到 Archwiki 的 Docker 图形相关 还是给找到了。(🙏太棒了)
Docker 可以说是非常容易上手了,基础系统咱们直接这样就可以下载。也不需要什么 debootstrap 和 pacstrap 或自己搞什么的,Docker hub 直接提供各种系统的基础镜像
# Fedora 42:
sudo docker pull fedora:42
# Ubuntu 25.04:
sudo docker pull ubuntu:25.04
# Debian 13
sudo docker pull debian:trixie
有了基础系统镜像,接下来就可以定制适合自己的镜像,可以写一个 dockerfile ,并写入相关规则:(这里我将以 Fedora 为例子)
# 指定基础系统镜像
FROM fedora:42
# 先更新系统
RUN dnf update -y
# 安装一些工具和测试 GUI 程序
RUN dnf install -y vim firefox
# 容器启动时运行 bash
CMD ["/bin/bash"]
其中 RUN 就是相当于跑脚本,可以提前把一些事情做好,这里我只测试 firefox 就不写那么多了。而 CMD 就是容器跑起来时启动的命令,这里我指定为 bash
接下来,在写好 dockerfile 的路径下输入:
sudo docker build --network host -t gui-test:fedora .
这样就构建出了一个叫 gui-test:fedora 的镜像了。其中我使用了 --network host
参数主要是为了方便连通宿主网络,避免因为网络问题卡住不动。而 -t
的意思就是指定一个镜像名字和标签,格式为 x:x
此时输入 sudo docker images
可以看到我们已经构建好的镜像
接下来,我们可以运行这个容器了。这里为了书写方便,我写成了 createBox.sh
来运行
sudo docker run -it --name "fedora42-Gui-test" \
--network host \
--hostname Fedora \
-e DISPLAY=$DISPLAY \
--device /dev/dri:/dev/dri \
--device /dev/snd:/dev/snd \
-e AUDIO_ID=`getent group audio | cut -d: -f3` \
--privileged \
-e XMODIFIERS="@im=fcitx" \
-e QT_IM_MODULE="fcitx" \
-e GTK_IM_MODULE="fcitx" \
--mount type=bind,src=/run/user/$UID/pulse,dst=/run/user/$UID/pulse \
--mount type=bind,src=/tmp/.X11-unix,dst=/tmp/.X11-unix \
--mount type=bind,src=/tmp/container_xauth,dst=/tmp/container_xauth \
--mount type=bind,src=/dev/shm,dst=/dev/shm \
--mount type=bind,src=/run,dst=/run \
--mount type=bind,src=/sys,dst=/sys \
--mount type=bind,src=/tmp,dst=/tmp \
gui-test:fedora
由于在 dockerfile 构建阶段已经安装好 firefox,此时在命令行中可以直接输入 firefox 来启动
如果提示 Error: cannot open display:0 什么的,可能需要 xhost +local:
授权一下,一般情况下 x 有个机制默认不允许非本机与 x 进行通信(避免导致 X 服务器被未授权访问),而容器其实就相当于另一个主机。为了让它们建立连接,需要使用 xhost 去开放权限
当 exit 离开后,该容器会停止运行,需要这样来启动容器:
sudo docker start 容器id
并且这样来再次进入交互:
sudo docker exec -it 容器id /bin/bash
而容器id可以通过这样来得知:
sudo docker ps -a
如果你想要删除某个容器可以:
sudo docker rm 容器id
如果你想要删除某个镜像可以:
sudo docker rmi 镜像id
有关 docker 和其他容器的图形性能测试:https://blog.glumi.cn/docker-bwrap-nspawn-gpu-test