运行 Docker 容器中的 X 应用程序可靠地位于通过 SSH 连接的服务器上,无需“--net host”
Run X application in a Docker container reliably on a server connected via SSH without "--net host"
没有Docker容器,使用SSH X11转发(ssh -X)直接在远程服务器上运行一个X11程序.当服务器上的 Docker 容器中的应用程序 运行s 时,我试图让同样的事情工作。当使用 -X 选项通过 SSH 连接到服务器时,会设置 X11 隧道,并且环境变量“$DISPLAY”通常会自动设置为 "localhost:10.0" 或类似值。如果我只是尝试 运行 Docker 中的 X 应用程序,我会收到此错误:
Error: GDK_BACKEND does not match available displays
我的第一个想法是使用“-e”选项实际将 $DISPLAY 传递到容器中,如下所示:
docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image
这有帮助,但不能解决问题。错误消息更改为:
Unable to init server: Broadway display type not supported: localhost:10.0
Error: cannot open display: localhost:10.0
在网上搜索后,我发现我可以做一些 xauth 魔术来修复身份验证。我添加了以下内容:
SOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH name_of_docker_image
但是,这只有在将“--net host”添加到 docker 命令时才有效:
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH --net host name_of_docker_image
这是不可取的,因为它使整个主机网络对容器可见。
为了在没有“--net host”的情况下 docker 中的远程服务器上 运行 现在缺少什么?
我明白了。当您使用 SSH 连接到计算机并使用 X11 转发时,/tmp/.X11-unix 不用于 X 通信,与 $XSOCK 相关的部分是不必要的。
任何 X 应用程序宁愿使用 $DISPLAY 中的主机名,通常 "localhost" 并使用 TCP 连接。然后通过隧道返回到 SSH 客户端。当对 Docker 使用“--net host”时,Docker 容器的 "localhost" 将与 Docker 主机相同,因此它会正常工作。
未指定“--net host”时,Docker使用默认桥接网络模式。 这意味着"localhost"意味着容器内部与主机不同的东西,并且容器内部的X应用程序将无法通过引用[=57]看到X服务器=].因此,为了解决这个问题,必须将 "localhost" 替换为主机的实际 IP 地址。这通常是“172.17.0.1”或类似的。检查 "ip addr" 以获得 "docker0" 接口。
这可以通过 sed 替换来完成:
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1/'`
此外,SSH 服务器通常未配置为接受到此 X11 隧道的远程连接。然后必须通过编辑 /etc/ssh/sshd_config(至少在 Debian 中)和设置来更改:
X11UseLocalhost no
然后重启SSH服务器,用"ssh -X"重新登录服务器。
差不多就这样了,但还剩下一个复杂的问题。如果任何防火墙在 Docker 主机上 运行ning,则必须打开与 X11 隧道关联的 TCP 端口。端口号是$DISPLAY中:和.之间的数字加上6000.
获取TCP端口号,可以运行:
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*//'`
TCPPORT=`expr 6000 + $X11PORT`
然后(如果使用 ufw 作为防火墙),为 172.17.0.0 子网中的 Docker 容器打开此端口:
ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp
所有命令都可以放在一个脚本中:
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | sudo xauth -f $XAUTH nmerge -
sudo chmod 777 $XAUTH
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*//'`
TCPPORT=`expr 6000 + $X11PORT`
sudo ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1/'`
sudo docker run -ti --rm -e DISPLAY=$DISPLAY -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH name_of_docker_image
假设您不是 root,因此需要使用 sudo。
您可以 运行:
而不是 sudo chmod 777 $XAUTH
sudo chown my_docker_container_user $XAUTH
sudo chmod 600 $XAUTH
防止服务器上的其他用户在知道您为什么创建 /tmp/.docker.auth 文件的情况下也能够访问 X 服务器。
我希望这能让它在大多数情况下都能正常工作。
就我而言,我坐在“远程”并连接到“docker_host”上的“docker_container”:
remote --> docker_host --> docker_container
为了使用 VScode 更轻松地调试脚本,我将 SSHD 安装到“docker_container”,在端口 22 上报告,映射到“[=48=”上的另一个端口(比如 1234) ]".
所以我可以通过 ssh(从“远程”)直接连接 运行 容器:
ssh -Y -p 1234 appuser@docker_host.local
(其中 appuser
是“docker_container”中的用户名。我现在在我的本地子网上工作,所以我可以通过 .local 映射引用我的服务器。对于外部 IP,只要确保你的路由器映射到这台机器的这个端口。)
这会通过 ssh 直接从我的“远程”到“docker_container”建立连接。
remote --> (ssh) --> docker_container
在“docker_container”中,我安装了 sshd
sudo apt-get install openssh-server
(您可以将其添加到您的 Dockerfile 以在构建时安装)。
要允许 X11 转发工作,请编辑 /etc/ssh/sshd_config
文件:
X11Forwarding yes
X11UseLocalhost no
然后重启容器内的ssh。您应该从 shell 执行到容器中,从“docker_host”执行此操作,而不是当您通过 ssh 连接到“docker_container”时执行此操作:(docker exec -ti docker_container bash
)
重启sshd:
sudo service ssh restart
当您通过 ssh 连接到“docker_container”时,请检查 $DISPLAY
环境变量。它应该是这样的
appuser@3f75a98d67e6:~/data$ echo $DISPLAY
3f75a98d67e6:10.0
通过 ssh 从“docker_container”中执行您最喜欢的 X11 图形程序进行测试(如 cv2.imshow())
如果您设置 X11UseLocalhost = no
,您甚至允许 外部流量到达 X11 套接字 。即指向本机外网IP的流量可以到达SSHD X11转发。还有两个 可能 适用的安全机制(防火墙、X11 身份验证)。尽管如此,如果您正在摆弄用户甚至 application-specific 问题,我还是希望单独留下 系统全局设置 。
这是在 sshd 配置中更改 X11UseLocalhost
的替代方法:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------- veth123@if5 --|-- eth0@if6 |
| (bridge) (veth pair) | (veth pair) |
| | |
| 127.0.0.1 +-------------------------+
routing +- lo
| (loopback)
|
| 192.168.1.2
+- ens33
(physical host interface)
默认情况下 X11UseLocalhost yes
,sshd 在根网络名称空间上侦听 127.0.0.1
。我们需要从 docker 网络命名空间内部获取 X11 流量到根网络 ns 中的环回接口。 veth 对连接到 docker0
网桥,因此两端可以在没有任何路由的情况下与 172.17.0.1 通信。根net ns中的三个接口(docker0
、lo
和ens33
)可以通过路由进行通信。
我们希望实现以下目标:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo >------- sshd -+
(loopback) |
v
192.168.1.2 |
ens33 ------<-----+
(physical host interface)
我们可以让 X11 应用程序直接与 172.17.0.1
对话以“逃避”docker 网络 ns。这是通过适当设置 DISPLAY
来实现的:export DISPLAY=172.17.0.1:10
:
+ docker container net ns+
| |
172.17.0.1 | 172.17.0.2 |
docker0 --------- veth123@if5 --|-- eth0@if6 -----< xeyes |
(bridge) (veth pair) | (veth pair) |
| |
127.0.0.1 +-------------------------+
lo
(loopback)
192.168.1.2
ens33
(physical host interface)
现在,我们添加一条iptables规则,在根网ns中从172.17.0.1路由到127.0.0.1:
iptables \
--table nat \
--insert PREROUTING \
--proto tcp \
--destination 172.17.0.1 \
--dport 6010 \
--jump DNAT \
--to-destination 127.0.0.1:6010
sysctl net.ipv4.conf.docker0.route_localnet=1
也许您可以通过仅路由来自该容器 (veth end) 的流量来改进这一点。另外,老实说,我不太确定为什么需要 route_localnet
。 127/8
似乎是一个奇怪的数据包源/目的地,因此默认情况下禁用路由。您可能还可以将流量从 docker 网络 ns 内的环回接口重新路由到 veth 对,然后从那里到根网络 ns 中的环回接口。
使用上面给出的命令,我们得到:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo
(loopback)
192.168.1.2
ens33
(physical host interface)
但是,现在我们正在尝试以 172.17.0.1:10
身份访问 X11 服务器。这不会在 x 权限文件 (~/.Xauthority
) 中找到该条目,通常类似于 <hostname>:10
。使用 Ruben 的建议在 docker 容器中添加一个可见的新条目:
xauth add 172.17.0.1:10 . <cookie>
其中<cookie>
是SSH X11转发设置的cookie,例如通过 xauth list
.
您可能还必须在防火墙中允许流量进入 172.17.0.1:6010
。
您还可以从 docker 容器网络命名空间内的主机启动应用程序:
sudo nsenter --target=<pid of process in container> --net su - $USER <app>
如果没有 su
,您将成为 运行 root。当然,你也可以使用另一个容器,共享网络命名空间:
sudo docker run --network=container:<other container name/id> ...
上面显示的 X11 转发机制适用于整个网络名称空间(实际上,适用于连接到 docker0
网桥的所有内容)。因此,它适用于容器网络命名空间内的任何应用程序。
我使用了一种可以完全在 docker 容器中执行的自动化方法。
只需要将DISPLAY变量传递给容器,然后挂载.Xauthority
。
此外,它仅使用 DISPLAY 变量中的端口,因此它也适用于 DISPLAY=localhost:XY.Z
.
的情况
创建文件 source-me.sh
,内容如下:
# Find the containers address in /etc/hosts
CONTAINER_IP=$(grep $(hostname) /etc/hosts | awk '{ print }')
# Assume the docker-host IP only differs in the last byte
SUBNET=$(echo $CONTAINER_IP | sed 's/\.[^\.]$//')
DOCKER_HOST_IP=${SUBNET}.1
# Get the port from the DISPLAY variable
DISPLAY_PORT=$(echo $DISPLAY | sed 's/.*://' | sed 's/\..*//')
# Create the correct display-name
export DISPLAY=$DOCKER_HOST_IP:$DISPLAY_PORT
# Find an existing xauth entry for the same port (DISPLAY_PORT),
# and copy everything except the dispay-name
# filtering out entries containing /unix: which correspond to "same-machine" connections
ENTRY=$(xauth -n list | grep -v '/unix\:' | grep "\:${DISPLAY_PORT}" | head -n 1 | sed 's/^[^ ]* *//')
# Prepend our display-name
ENTRY="$DOCKER_HOST_IP:$DISPLAY_PORT $ENTRY"
# Add the new xauth entry.
# Because our .Xauthority file is mounted, a new file
# named ${HOME}/.Xauthority-n will be created, and a warning
# is printed on std-err
xauth add $ENTRY 2> /dev/null
# replace the content of ${HOME}/.Xauthority with that of ${HOME}/.Xauthority-n
# without creating a new i-node.
cat ${HOME}/.Xauthority-n > ${HOME}/.Xauthority
创建以下 Dockerfile 进行测试:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y xauth
COPY source-me.sh /root/
RUN cat /root/source-me.sh >> /root/.bashrc
# xeyes for testing:
RUN apt-get install -y x11-apps
构建并运行:
docker build -t test-x .
docker run -ti \
-v $HOME/.Xauthority:/root/.Xauthority:rw \
-e DISPLAY=$DISPLAY \
test-x \
bash
容器内,运行:
xeyes
至 运行 non-interactively,您必须确保 source-me.sh
来源:
docker run \
-v $HOME/.Xauthority:/root/.Xauthority:rw \
-e DISPLAY=$DISPLAY \
test-x \
bash -c "source source-me.sh ; xeyes"
没有Docker容器,使用SSH X11转发(ssh -X)直接在远程服务器上运行一个X11程序.当服务器上的 Docker 容器中的应用程序 运行s 时,我试图让同样的事情工作。当使用 -X 选项通过 SSH 连接到服务器时,会设置 X11 隧道,并且环境变量“$DISPLAY”通常会自动设置为 "localhost:10.0" 或类似值。如果我只是尝试 运行 Docker 中的 X 应用程序,我会收到此错误:
Error: GDK_BACKEND does not match available displays
我的第一个想法是使用“-e”选项实际将 $DISPLAY 传递到容器中,如下所示:
docker run -ti -e DISPLAY=$DISPLAY name_of_docker_image
这有帮助,但不能解决问题。错误消息更改为:
Unable to init server: Broadway display type not supported: localhost:10.0
Error: cannot open display: localhost:10.0
在网上搜索后,我发现我可以做一些 xauth 魔术来修复身份验证。我添加了以下内容:
SOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH name_of_docker_image
但是,这只有在将“--net host”添加到 docker 命令时才有效:
docker run -ti -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH --net host name_of_docker_image
这是不可取的,因为它使整个主机网络对容器可见。
为了在没有“--net host”的情况下 docker 中的远程服务器上 运行 现在缺少什么?
我明白了。当您使用 SSH 连接到计算机并使用 X11 转发时,/tmp/.X11-unix 不用于 X 通信,与 $XSOCK 相关的部分是不必要的。
任何 X 应用程序宁愿使用 $DISPLAY 中的主机名,通常 "localhost" 并使用 TCP 连接。然后通过隧道返回到 SSH 客户端。当对 Docker 使用“--net host”时,Docker 容器的 "localhost" 将与 Docker 主机相同,因此它会正常工作。
未指定“--net host”时,Docker使用默认桥接网络模式。 这意味着"localhost"意味着容器内部与主机不同的东西,并且容器内部的X应用程序将无法通过引用[=57]看到X服务器=].因此,为了解决这个问题,必须将 "localhost" 替换为主机的实际 IP 地址。这通常是“172.17.0.1”或类似的。检查 "ip addr" 以获得 "docker0" 接口。
这可以通过 sed 替换来完成:
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1/'`
此外,SSH 服务器通常未配置为接受到此 X11 隧道的远程连接。然后必须通过编辑 /etc/ssh/sshd_config(至少在 Debian 中)和设置来更改:
X11UseLocalhost no
然后重启SSH服务器,用"ssh -X"重新登录服务器。
差不多就这样了,但还剩下一个复杂的问题。如果任何防火墙在 Docker 主机上 运行ning,则必须打开与 X11 隧道关联的 TCP 端口。端口号是$DISPLAY中:和.之间的数字加上6000.
获取TCP端口号,可以运行:
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*//'`
TCPPORT=`expr 6000 + $X11PORT`
然后(如果使用 ufw 作为防火墙),为 172.17.0.0 子网中的 Docker 容器打开此端口:
ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp
所有命令都可以放在一个脚本中:
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | sudo xauth -f $XAUTH nmerge -
sudo chmod 777 $XAUTH
X11PORT=`echo $DISPLAY | sed 's/^[^:]*:\([^\.]\+\).*//'`
TCPPORT=`expr 6000 + $X11PORT`
sudo ufw allow from 172.17.0.0/16 to any port $TCPPORT proto tcp
DISPLAY=`echo $DISPLAY | sed 's/^[^:]*\(.*\)/172.17.0.1/'`
sudo docker run -ti --rm -e DISPLAY=$DISPLAY -v $XAUTH:$XAUTH \
-e XAUTHORITY=$XAUTH name_of_docker_image
假设您不是 root,因此需要使用 sudo。
您可以 运行:
而不是sudo chmod 777 $XAUTH
sudo chown my_docker_container_user $XAUTH
sudo chmod 600 $XAUTH
防止服务器上的其他用户在知道您为什么创建 /tmp/.docker.auth 文件的情况下也能够访问 X 服务器。
我希望这能让它在大多数情况下都能正常工作。
就我而言,我坐在“远程”并连接到“docker_host”上的“docker_container”:
remote --> docker_host --> docker_container
为了使用 VScode 更轻松地调试脚本,我将 SSHD 安装到“docker_container”,在端口 22 上报告,映射到“[=48=”上的另一个端口(比如 1234) ]".
所以我可以通过 ssh(从“远程”)直接连接 运行 容器:
ssh -Y -p 1234 appuser@docker_host.local
(其中 appuser
是“docker_container”中的用户名。我现在在我的本地子网上工作,所以我可以通过 .local 映射引用我的服务器。对于外部 IP,只要确保你的路由器映射到这台机器的这个端口。)
这会通过 ssh 直接从我的“远程”到“docker_container”建立连接。
remote --> (ssh) --> docker_container
在“docker_container”中,我安装了 sshd
sudo apt-get install openssh-server
(您可以将其添加到您的 Dockerfile 以在构建时安装)。
要允许 X11 转发工作,请编辑 /etc/ssh/sshd_config
文件:
X11Forwarding yes
X11UseLocalhost no
然后重启容器内的ssh。您应该从 shell 执行到容器中,从“docker_host”执行此操作,而不是当您通过 ssh 连接到“docker_container”时执行此操作:(docker exec -ti docker_container bash
)
重启sshd:
sudo service ssh restart
当您通过 ssh 连接到“docker_container”时,请检查 $DISPLAY
环境变量。它应该是这样的
appuser@3f75a98d67e6:~/data$ echo $DISPLAY
3f75a98d67e6:10.0
通过 ssh 从“docker_container”中执行您最喜欢的 X11 图形程序进行测试(如 cv2.imshow())
如果您设置 X11UseLocalhost = no
,您甚至允许 外部流量到达 X11 套接字 。即指向本机外网IP的流量可以到达SSHD X11转发。还有两个 可能 适用的安全机制(防火墙、X11 身份验证)。尽管如此,如果您正在摆弄用户甚至 application-specific 问题,我还是希望单独留下 系统全局设置 。
这是在 sshd 配置中更改 X11UseLocalhost
的替代方法:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------- veth123@if5 --|-- eth0@if6 |
| (bridge) (veth pair) | (veth pair) |
| | |
| 127.0.0.1 +-------------------------+
routing +- lo
| (loopback)
|
| 192.168.1.2
+- ens33
(physical host interface)
默认情况下 X11UseLocalhost yes
,sshd 在根网络名称空间上侦听 127.0.0.1
。我们需要从 docker 网络命名空间内部获取 X11 流量到根网络 ns 中的环回接口。 veth 对连接到 docker0
网桥,因此两端可以在没有任何路由的情况下与 172.17.0.1 通信。根net ns中的三个接口(docker0
、lo
和ens33
)可以通过路由进行通信。
我们希望实现以下目标:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo >------- sshd -+
(loopback) |
v
192.168.1.2 |
ens33 ------<-----+
(physical host interface)
我们可以让 X11 应用程序直接与 172.17.0.1
对话以“逃避”docker 网络 ns。这是通过适当设置 DISPLAY
来实现的:export DISPLAY=172.17.0.1:10
:
+ docker container net ns+
| |
172.17.0.1 | 172.17.0.2 |
docker0 --------- veth123@if5 --|-- eth0@if6 -----< xeyes |
(bridge) (veth pair) | (veth pair) |
| |
127.0.0.1 +-------------------------+
lo
(loopback)
192.168.1.2
ens33
(physical host interface)
现在,我们添加一条iptables规则,在根网ns中从172.17.0.1路由到127.0.0.1:
iptables \
--table nat \
--insert PREROUTING \
--proto tcp \
--destination 172.17.0.1 \
--dport 6010 \
--jump DNAT \
--to-destination 127.0.0.1:6010
sysctl net.ipv4.conf.docker0.route_localnet=1
也许您可以通过仅路由来自该容器 (veth end) 的流量来改进这一点。另外,老实说,我不太确定为什么需要 route_localnet
。 127/8
似乎是一个奇怪的数据包源/目的地,因此默认情况下禁用路由。您可能还可以将流量从 docker 网络 ns 内的环回接口重新路由到 veth 对,然后从那里到根网络 ns 中的环回接口。
使用上面给出的命令,我们得到:
+ docker container net ns +
| |
172.17.0.1 | 172.17.0.2 |
+- docker0 --------< veth123@if5 --|-< eth0@if6 -----< xeyes |
| (bridge) (veth pair) | (veth pair) |
v | |
| 127.0.0.1 +-------------------------+
routing +- lo
(loopback)
192.168.1.2
ens33
(physical host interface)
但是,现在我们正在尝试以 172.17.0.1:10
身份访问 X11 服务器。这不会在 x 权限文件 (~/.Xauthority
) 中找到该条目,通常类似于 <hostname>:10
。使用 Ruben 的建议在 docker 容器中添加一个可见的新条目:
xauth add 172.17.0.1:10 . <cookie>
其中<cookie>
是SSH X11转发设置的cookie,例如通过 xauth list
.
您可能还必须在防火墙中允许流量进入 172.17.0.1:6010
。
您还可以从 docker 容器网络命名空间内的主机启动应用程序:
sudo nsenter --target=<pid of process in container> --net su - $USER <app>
如果没有 su
,您将成为 运行 root。当然,你也可以使用另一个容器,共享网络命名空间:
sudo docker run --network=container:<other container name/id> ...
上面显示的 X11 转发机制适用于整个网络名称空间(实际上,适用于连接到 docker0
网桥的所有内容)。因此,它适用于容器网络命名空间内的任何应用程序。
我使用了一种可以完全在 docker 容器中执行的自动化方法。
只需要将DISPLAY变量传递给容器,然后挂载.Xauthority
。
此外,它仅使用 DISPLAY 变量中的端口,因此它也适用于 DISPLAY=localhost:XY.Z
.
创建文件 source-me.sh
,内容如下:
# Find the containers address in /etc/hosts
CONTAINER_IP=$(grep $(hostname) /etc/hosts | awk '{ print }')
# Assume the docker-host IP only differs in the last byte
SUBNET=$(echo $CONTAINER_IP | sed 's/\.[^\.]$//')
DOCKER_HOST_IP=${SUBNET}.1
# Get the port from the DISPLAY variable
DISPLAY_PORT=$(echo $DISPLAY | sed 's/.*://' | sed 's/\..*//')
# Create the correct display-name
export DISPLAY=$DOCKER_HOST_IP:$DISPLAY_PORT
# Find an existing xauth entry for the same port (DISPLAY_PORT),
# and copy everything except the dispay-name
# filtering out entries containing /unix: which correspond to "same-machine" connections
ENTRY=$(xauth -n list | grep -v '/unix\:' | grep "\:${DISPLAY_PORT}" | head -n 1 | sed 's/^[^ ]* *//')
# Prepend our display-name
ENTRY="$DOCKER_HOST_IP:$DISPLAY_PORT $ENTRY"
# Add the new xauth entry.
# Because our .Xauthority file is mounted, a new file
# named ${HOME}/.Xauthority-n will be created, and a warning
# is printed on std-err
xauth add $ENTRY 2> /dev/null
# replace the content of ${HOME}/.Xauthority with that of ${HOME}/.Xauthority-n
# without creating a new i-node.
cat ${HOME}/.Xauthority-n > ${HOME}/.Xauthority
创建以下 Dockerfile 进行测试:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y xauth
COPY source-me.sh /root/
RUN cat /root/source-me.sh >> /root/.bashrc
# xeyes for testing:
RUN apt-get install -y x11-apps
构建并运行:
docker build -t test-x .
docker run -ti \
-v $HOME/.Xauthority:/root/.Xauthority:rw \
-e DISPLAY=$DISPLAY \
test-x \
bash
容器内,运行:
xeyes
至 运行 non-interactively,您必须确保 source-me.sh
来源:
docker run \
-v $HOME/.Xauthority:/root/.Xauthority:rw \
-e DISPLAY=$DISPLAY \
test-x \
bash -c "source source-me.sh ; xeyes"