如何在Docker容器内查看GUI应用程序

4
当我尝试运行GUI(例如xclock),我会收到以下错误消息:
Error: Can't open display: 

我正在尝试使用Docker运行ROS容器,并且需要查看其中运行的GUI应用程序。
我曾经使用Vagrant VM成功地使用X11实现过这一点。
到目前为止,我已经尝试根据此处的信息将方法#1和#2放入基于docker文件中: http://wiki.ros.org/docker/Tutorials/GUI 然后我尝试复制这里大部分的dockerfile: https://hub.docker.com/r/mjenz/ros-indigo-gui/~/dockerfile/ 这是我的当前docker文件:
# Set the base image to use to ros:kinetic
FROM ros:kinetic

# Set the file maintainer (your name - the file's author)
MAINTAINER me

# Set ENV for x11 display
ENV DISPLAY $DISPLAY
ENV QT_X11_NO_MITSHM 1

# Install an x11 app like xclock to test this
run apt-get update 
run apt-get install x11-apps --assume-yes

# Stuff I copied to make a ros user
ARG uid=1000
ARG gid=1000

RUN export uid=${uid} gid=${gid} && \
    groupadd -g ${gid} ros && \
    useradd -m -u ${uid} -g ros -s /bin/bash ros && \
    passwd -d ros && \
    usermod -aG sudo ros

USER ros
WORKDIR /home/ros

# Sourcing this before .bashrc runs breaks ROS completions
RUN echo "\nsource /opt/ros/kinetic/setup.bash" >> /home/ros/.bashrc

# Copy entrypoint script into the image, this currently echos hello world
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
2个回答

3

我个人的偏好是通过注入显示变量并与类似以下内容共享Unix套接字或X Windows:

docker run -it --rm -e DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -v /etc/localtime:/etc/localtime:ro \
  my-gui-image

共享本地时间只是为了让时区匹配,我一直在使用这个方法来发送电子邮件应用程序。

另一个选择是启动一个VNC服务器,在该服务器上运行您的应用程序,然后使用VNC客户端连接到容器。我不太喜欢这种方法,因为您最终会在容器内运行两个进程,使信号处理和日志记录变得更加困难。但它的优点是应用程序更好地被隔离,因此如果被黑客攻击,它无法访问您的X显示。


0
我假设:
  • 主机系统是Linux
  • 窗口API是X11
我将使用podman,但你可以通过在每个命令中将podman替换为docker来复制,我只是喜欢podman并且从未使用过docker。 我的系统:Opensuse Tumbleweedkde [Intel Optimus笔记本电脑]。

解决方案

应用程序需要向Xserver请求显示其图形用户界面元素。而为了安全起见,有一个授权系统。

这个授权系统被称为xauth。xauth通常将会话cookie存储在/home/username/.Xauthority文件中(通常但不总是)。

检查当前正在使用的cookie

xauth list

输出将如下所示,
localhost.localdomain/unix:0  MIT-MAGIC-COOKIE-1  99aaccf2d83177ddf581e2989ebbcea1
#ffff##:0  MIT-MAGIC-COOKIE-1  99aaccf2d83177ddf581e2989ebbcea1

如果Xauthority文件不在通常的位置,请检查正在使用的授权文件。
xauth

输出将类似于这样,如果不使用标准文件的话,
Using authority file /run/user/1000/xauth_Abcde

我们需要向容器提供这两个东西,
- MIT-MAGIC-COOKIE-1 - 现在只需将其视为协议或标识符。这对于每个会话都是恒定的。 - 99aaccf2d83177ddf581e2989ebbcea1 - 这个32位字母密钥是会话密钥,或者你想给它起的任何花哨的名字。密钥对于每个会话都是唯一的。
现在,为了解决与显示相关的问题,我们需要执行以下五个步骤:
1. 如果容器中不存在,请创建一个 .Xauthority 文件,就像我们的示例(debian)一样。 2. 将我们的会话密钥和协议添加到我们创建的 .Xauthority 文件中。 3. 将 DISPLAY 环境变量传递给容器。 4. 将主机的 Xserver socket(通常位于 /tmp/.X11-unix)挂载到容器中。 5. 将网络设置为 "host"(用于显示渲染)。
这五个步骤将解决所有与显示相关的问题。

配置 - 示例场景

示例中通过容器运行firefox图形界面。

待办事项

1. 创建一个.Xauthority文件

在Containerfile中执行RUN touch .Xauthority

2. 将我们的会话密钥协议添加到我们创建的.Xauthority文件中

两者都作为环境变量传递

在Containerfile中设置协议:ENV PROTOCOL=MIT-MAGIC-COOKIE-1

将会话密钥作为参数传递给podman run--env KEY=$(xauth list | sed '2,$d'| tr -d '\n' | tail -c 32) \(无法在Containerfile中作为常量传递,因为每个会话都会更改)

然后通过Containerfile中的CMD xauth add ${HOST}:0 $PROTOCOL $KEY将其添加到.Xauthority

3. 将DISPLAY环境变量传递给容器

作为参数传递给podman run命令 --env DISPLAY \

4. 将Xserver套接字(通常位于/tmp/.X11-unix)挂载到容器中

作为参数传递给podman run命令 --mount type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix,readonly \

5. 将网络类型设置为host

在构建镜像时进行配置 podman build --network=host --tag guitest .

Containerfile

FROM debian:latest

ARG DEBIAN_FRONTEND=noninteractive

RUN apt update && apt upgrade
RUN apt install --no-install-recommends --yes firefox-esr pipewire pipewire-alsa pipewire-pulse ffmpeg xauth

ENV PROTOCOL=MIT-MAGIC-COOKIE-1

ENV HOME /home/def
ENV USER def

RUN useradd --create-home --home-dir ${HOME} -G audio,video ${USER} && chown -R ${USER}:${USER} ${HOME}

WORKDIR ${HOME}
USER ${USER}

RUN touch .Xauthority

CMD xauth add ${HOST}:0 $PROTOCOL $KEY && firefox

构建命令

podman build --network=host --tag guitest .

使用我们构建的镜像运行容器
podman run -it --rm --name guiapp \
--env DISPLAY \
--env KEY=$(xauth list | sed '2,$d'| tr -d '\n' | tail -c 32) \
--mount type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix,readonly \
guitest

BINGO!火狐浏览器的图形用户界面就在你眼前。

免责声明此示例中无法工作的音频,需要额外的步骤使音频正常运行。


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