你能在Linux Docker容器中运行GUI应用程序吗?

505

如何在Linux Docker容器中运行GUI应用程序?

是否有任何镜像可以设置vncserver或其他内容,以便您可以在例如Firefox周围添加额外的保护层?


请查看HPC可视化容器用户指南获取一些想法。 - kenorb
24个回答

13

这并不是轻量级的解决方案,但是它可以让docker与完整桌面虚拟化实现功能平衡。Ubuntu和CentOS都适用于Xfce4或IceWM。而noVNC选项可以通过浏览器轻松访问。

https://github.com/ConSol/docker-headless-vnc-container

它运行noVNCtigerVNC的vncserver。然后调用给定的窗口管理器的startx。此外,libnss_wrapper.so用于模拟用户的密码管理。


有人测试过这个吗? - guilhermecgs
3
@guilhermecgs 是的,而且工作得很好。从那时起,我还尝试在Docker中使用xpra,它是无需root的X。在我看来,xpra最合适,比VNC更高效。 - dashesy
只是为了明确一点… 我可以在这个镜像上拥有完整的桌面体验(GNOME, KDE)吗? - guilhermecgs
我只尝试过Xfce4和IceWM(在那个仓库里)。当然,体验会受到限制,例如挂载设备将不会显示在桌面上(gvfs),除非您将--device /dev/...传递给docker并设置必要的--cap权限。这违背了容器化的目的,但您可以通过设备进行传递。通过一些调整,我相信在VNC下运行GNOME/KDE是可能的。我在带有nvidia卡的docker中运行了多个X(没有VNC或Xpra),所以这肯定是可行的。 - dashesy
到目前为止,我们还没有尝试过。其中最大的挑战将是启动一个可工作的D-Bus守护进程。大多数GNOME或KDE桌面都需要它们。也许ubuntu-desktop-lxde-vnc项目可以帮助你解决这个问题。 - toschneck
在浏览器中直接使用noVNC客户端完美运行。经过测试:consol/ubuntu-xfce-vnc - vladkras

9
其他解决方案可能有效,但这里提供了一种使用docker-compose的解决方案。
为了解决该错误,您需要将$DISPLAY和.X11-unix传递给docker,并授权启动docker的用户访问xhost。
docker-compose.yml文件中:
version: '2'
services:
    node:
        build: .
        container_name: node
        environment:
            - DISPLAY
        volumes:
            - /tmp/.X11-unix:/tmp/.X11-unix

在终端或脚本中:

  • xhost +si:localuser:$USER (允许当前用户访问X服务器)
  • xhost +local:docker(允许Docker容器访问X服务器)
  • export DISPLAY=$DISPLAY(设置显示环境变量为当前X服务器)
  • docker-compose up(启动Docker Compose服务)

7
如果您想无头运行GUI应用程序,请阅读这里。您需要使用xvfb或其他类似软件创建虚拟监视器。例如,如果您想在浏览器中运行Selenium测试,这非常有帮助。
并未提到的是,一些软件实际上会自己使用Linux容器进行沙盒化。因此,例如Chrome将永远无法正常运行,除非在运行容器时使用适当的标志--privileged

6

有一种无需使用 VNC、SSH 和 X11 转发即可在容器中运行 GUI 应用程序的解决方案,是由 lord.garbage 提出的 另一个解决方案。这也提到了这里


1
如果安全不是一个问题,这样做非常好。如果使用Docker来隔离某个东西的目的,最好避免在容器内部使用X11输入输出。 - Will

6

虽然我来晚了,但是对于不想使用XQuartz的Mac用户,这里提供一个可以构建Fedora Image且带有桌面环境(xfce)的工作示例,它使用了XvfbVNC,非常简单易用:

在Mac上,您只需要使用默认的屏幕共享应用程序,连接到localhost: 5901即可。

Dockerfile:

FROM fedora

USER root

# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd

# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false  \
 && dnf install -y --setopt=deltarpm=false \
                openssl.x86_64             \
                java-1.8.0-openjdk.x86_64  \
                xorg-x11-server-Xvfb       \
                x11vnc                     \
                firefox                    \
                @xfce-desktop-environment  \
 && dnf clean all

# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer

# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh

# Expose VNC, SSH
EXPOSE 5901 22

# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV  DISPLAY :1.0
RUN  mkdir ~/.x11vnc
RUN  x11vnc -storepasswd letmein ~/.x11vnc/passwd

WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]

start-vnc.sh

#!/bin/sh

Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &

bash
# while true; do sleep 1000; done

如果您需要,可以查看链接的自述文件获取构建和运行命令。


5

根据Jürgen Weigert的回答,我有一些改进:

docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes

唯一的区别是它创建一个名为 $XAUTH_DIR 的目录,用于放置 $XAUTH 文件,并将 $XAUTH_DIR 目录挂载到 docker 容器中,而非挂载 $XAUTH 文件。

这种方法的好处是,您可以在 /etc/rc.local 中编写一个命令,在 /tmp 中创建一个名为 $XAUTH_DIR 的空文件夹,并将其模式更改为 777。

tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local

当系统重新启动,在用户登录之前,如果容器的重启策略是"always",那么docker将自动挂载$XAUTH_DIR目录。在用户登录后,您可以在~/.profile中编写一个命令来创建$XAUTH文件,然后容器将自动使用此$XAUTH文件。

tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile

总之,每次系统重启和用户登录后,容器都会自动获取Xauthority文件。


5

我按照以下步骤成功地在 docker 中使用 opencv 运行 USB 摄像头的视频流:

  1. Let docker access the X server

    xhost +local:docker
    
  2. Create the X11 Unix socket and the X authentication file

    XSOCK=/tmp/.X11-unix
    XAUTH=/tmp/.docker.xauth
    
  3. Add proper permissions

    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
    
  4. Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine

    export QT_GRAPHICSSYSTEM=native
    
  5. Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise

    export QT_X11_NO_MITSHM=1
    
  6. Update the docker run command

    docker run -it \
               -e DISPLAY=$DISPLAY \
               -e XAUTHORITY=$XAUTH \
               -v $XSOCK:$XSOCK \
               -v $XAUTH:$XAUTH \
               --runtime=nvidia \
               --device=/dev/video0:/dev/video0 \
               nvcr.io/nvidia/pytorch:19.10-py3
    

注意:完成项目后,请将访问控制返回到默认值 - xhost -local:docker

更多细节请参见:使用Docker的GUI

来源:使用Tensorflow、OpenCV和Docker进行实时视频处理和目标检测


创建X11 Unix套接字和X身份验证文件并不会创建任何文件,它只是定义变量吗? - MrR

4
您可以允许Docker用户(此处为root)访问X11显示屏:
XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root 
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image 
xhost -SI:localuser:root

4

3
如果您已经构建了镜像,以下是另一个答案:
  1. 在不使用sudo的情况下调用docker(如何解决docker:权限被拒绝问题

  2. 在主机和容器之间共享相同的用户、主目录和密码(提示:使用用户ID而不是用户名)

  3. 为驱动程序依赖库工作良好设置dev文件夹

  4. 加上X11转发。

    docker run --name=CONTAINER_NAME --network=host --privileged \
      -v /dev:/dev \
      -v `echo ~`:/home/${USER} \
      -p 8080:80 \
      --user=`id -u ${USER}` \
      --env="DISPLAY" \
      --volume="/etc/group:/etc/group:ro" \
      --volume="/etc/passwd:/etc/passwd:ro" \
      --volume="/etc/shadow:/etc/shadow:ro" \
      --volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
      --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
      -it REPO:TAG /bin/bash

你可能会问,如果很多东西都一样,使用Docker有什么意义呢?嗯,我能想到的一个原因是克服软件包依赖地狱(https://en.wikipedia.org/wiki/Dependency_hell)。所以我认为这种使用方式更适合开发人员。

1
这是唯一适用于我的方法。就我的需求而言,我能将它最小化为:docker run --network=host --volume=echo ~:/home/${USER} --user=id -u ${USER} --env="DISPLAY" --volume="/etc/passwd:/etc/passwd:ro" -it REPO:TAG /bin/bash - user1145922

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接