[x11] 도커 컨테이너에서 GUI 애플리케이션을 실행할 수 있습니까?


Answers

Xauthority는 새로운 시스템에서 문제가됩니다. 도커 컨테이너를 실행하기 전에 xhost +로 보호를 해제하거나 제대로 준비된 Xauthority 파일을 전달할 수 있습니다. 일반적인 Xauthority 파일은 호스트 이름에 따라 다릅니다. docker를 사용하면 각 컨테이너마다 다른 호스트 이름 (docker run -h로 설정)을 가질 수 있지만 호스트 시스템과 동일한 컨테이너의 호스트 이름을 설정해도 내 경우에는 도움이되지 않습니다. xeyes (이 예제가 마음에 든다)는 단순히 마법 쿠키를 무시하고 서버에 자격 증명을 전달하지 않습니다. 따라서 '프로토콜이 지정되지 않았습니다. 디스플레이를 열 수 없습니다'라는 오류 메시지가 나타납니다.

Xauthority 파일은 호스트 이름이 중요하지 않도록 작성 될 수 있습니다. 인증 패밀리를 'FamilyWild'로 설정해야합니다. xauth에 적절한 명령 줄이 있다면 잘 모르겠다. xauth와 sed를 결합한 예가 여기에있다. nlist 출력의 처음 16 비트를 변경해야합니다. FamilyWild의 값은 65535 또는 0xffff입니다.

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=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes
Question

도커 컨테이너에서 GUI 응용 프로그램을 어떻게 실행할 수 있습니까?

거기에 vncserver 또는 뭔가를 설정할 수있는 이미지가 있습니까? 예를 들어 파이어 폭스 샌드 박스 주위에 여분의 스피드 범프 샌드 박스를 추가 할 수 있습니까?




Nvidia 드라이버를 사용한 OpenGL 렌더링의 경우 다음 이미지를 사용하십시오.

https://github.com/thewtex/docker-opengl-nvidia

다른 OpenGL 구현의 경우 이미지가 호스트와 동일한 구현을 가지는지 확인하십시오.




호스트 디스플레이 공유 : 0은 다른 답변에서 언급했듯이 두 가지 단점이 있습니다.

  • X 보안 누출로 인해 컨테이너 격리가 중단됩니다. 예를 들어, xev 또는 xinput 하여 keylogging이 가능합니다.
  • 응용 프로그램은 X 확장 MIT-SHM의 공유 메모리 누락으로 인해 렌더링 오류 및 잘못된 RAM 액세스 오류가 발생할 수 있습니다.

X 보안 누출을 피하고 MIT-SHM 문제를 피하기 위해 x11docker를 github에 게시 했습니다 . 주된 아이디어는 자체 인증 쿠키와 MIT-SHM을 비활성화하여 두 번째 X 서버를 실행하는 것입니다. 도커 컨테이너는 새 X 서버에 액세스하고 호스트 표시 : 0에서 분리됩니다. X / Xephyr은 호스트에 의해 제공되므로 이미지 내에 X 종속성이 없습니다.

다음은 Xephyr에서 도커 이미지를 실행하는 예제 스크립트입니다. Xephyr에서 호스트 윈도우 관리자를 실행하고, 두 번째로 도커 이미지를 실행하고, 선택적으로 실행될 이미지 명령을 선택적으로 제공합니다. 도커에서 데스크탑 환경을 실행하려면 호스트 창 관리자 대신 ":"을 사용하십시오. 루트 암호가없는 시스템에서는 변수 $Getroot 변경하십시오.

Xephyr은 xinit를 사용하여 시작됩니다. 커스텀 xinitrc는 쿠키를 생성하고, 키보드 레이아웃을 설정하고, 윈도우 매니저를 실행하고, xtermrc를 사용하여 xterm을 실행하여 docker를 실행하기위한 패스워드를 요구합니다.

Xephyr 창을 닫으면 도커 컨테이너 응용 프로그램이 종료됩니다. 도킹 된 응용 프로그램을 종료하면 Xephyr 창이 닫힙니다.

주석 :

  • docker 옵션 --ipc=host MIT-SHM 문제를 피할 수도 있지만, 이는 컨테이너 격리를 분해하고 권장하지 않습니다.
  • 공유 X 소켓을 사용하는 경우 @ Jurgen Weigert가 설명한대로 쿠키를 "familiy wild"로 변경해야합니다. X over TCP를 사용하면 필요하지 않습니다.
  • 유사한 해결책은 Xpra, Xorg 및 Xvnc에서 가능합니다.
  • TCP를 통한 X의 경우, docker 데몬 ip (대부분 172.17.42.1)을 찾아 DISPLAY = 172.17.42.1 : 1을 공유하고, 쿠키를 만들고, NewXsocket을 공유하지 않으며, X / Xephyr 옵션 -nolisten tcp를 사용하지 않습니다. tcp를 통한 X를 사용하면 MIT-SHM이 자동으로 비활성화됩니다.

예 :

  • x11docker_example "openbox --sm-disable" x11docker/lxde pcmanfm
  • x11docker_example : x11docker/lxde

x11docker_example 스크립트 :

#! /bin/bash
# x11docker_example : Example script to run docker GUI applications in Xephyr.
#                     Look at x11docker on github: https://github.com/mviereck/x11docker
#
# Usage:
#   x11docker_example WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run a desktop environment in docker, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"

# command to get root permissions to run docker
Getroot="su -c"
# Getroot="sudo su -c"      # Use this on systems without a root password like Ubuntu or Sparky

# define new display and its X socket.
Newdisplay=:1
# To make sure $Newdisplay is not already in use, check /tmp/.Xn-lock
[ -e "/tmp/.X1-lock" ] && echo "Error: Display :1 is already in use" >&2 && exit 1
NewXsocket=/tmp/.X11-unix/X1


# cache folder and files
Cachefolder=/tmp/x11docker_example
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xtermrc=$Cachefolder/xtermrc
Xinitrc=$Cachefolder/xinitrc
Dockerpidfile=$Cachefolder/docker.pid
Dockerlogfile=$Cachefolder/docker.log


# command to run docker
# --rm                           created container will be discarded.
# -e DISPLAY=$Newdisplay         set environment variable to new display
# -e XAUTHORITY=/Xcookie         set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro  provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro  Share new X socket of Xephyr
Dockercommand="docker run --rm -e DISPLAY=$Newdisplay -e XAUTHORITY=/Xcookie -v $Xclientcookie:/Xcookie:ro -v $NewXsocket:$NewXsocket:ro $Dockerimage"  


# command to run X/Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given
# $Newdisplay                    first argument has to be new display
# -auth $Xservercookie           path to cookie file for X server. Must be different from cookie file of client, not sure why
# -nolisten tcp                  disable tcp connections for security reasons
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr $Newdisplay -auth $Xservercookie -nolisten tcp -extension MIT-SHM -retro"


# create xinitrc
{ echo "#! /bin/bash"

  echo "# set environment variables to new display and new cookie"
  echo "export DISPLAY=$Newdisplay"
  echo "export XAUTHORITY=$Xclientcookie"

  echo "# same keyboard layout as on host"
  echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - $Newdisplay"

  echo "# create new XAUTHORITY cookie file" 
  echo ":> $Xclientcookie"
  echo "xauth generate $Newdisplay . untrusted"
  echo "cp $Xclientcookie $Xservercookie"
  echo "# create prepared cookie with localhost identification disabled by ffff,"
  echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
  echo 'Cookie=$(xauth nlist '"$Newdisplay | sed -e 's/^..../ffff/')" 
  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'

  echo "# run window manager in Xephyr"
  echo $Windowmanager' & Windowmanagerpid=$!'

  echo "# show docker log"
  echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'

  echo "# prompt for password"
  echo "xterm -title x11docker_example -e '/bin/bash $Xtermrc'"

  echo "# wait for docker to finish"
  echo 'Dockerpid=$(cat '$Dockerpidfile') && {'
  echo '  while [ -n "$(pgrep docker | grep $Dockerpid)" ] ; do'
  echo '    sleep 1'
  echo '  done }'

  [ "$Windowmanager" = ":" ] || echo 'kill $Windowmanagerpid'
  echo 'kill $Tailpid'
} > $Xinitrc


# create xtermrc for a password prompt
{ echo "#! /bin/bash"
  echo "echo 'x11docker_example will start docker on display $Newdisplay with command:'"
  echo "echo $Getroot '$Dockercommand'"
  echo "echo 'Please type in your password to run docker:'"
  echo "$Getroot 'nohup $Dockercommand 2>&1 1>$Dockerlogfile & echo "'$!'" > $Dockerpidfile'"
} > $Xtermrc


xinit  $Xinitrc -- $Xcommand
rm -Rf $Cachefolder



OSX

Jürgen Weigert 는 우분투에서 저에게 가장 잘 맞는 답변을 가지고 있습니다. 그러나 OSX에서는 docker가 VirtualBox 내부에서 실행되기 때문에 솔루션이 더 이상 작동하지 않으면 작동하지 않습니다.

나는이 추가 성분들로 작업하고있다.

  1. Xquartz (OSX는 더 이상 X11 서버와 함께 제공되지 않음)
  2. socat으로 소켓 전달 (brew install socat)
  3. 컨테이너를 시작하는 bash 스크립트

OSX에서이 대답을 향상시키기 위해 사용자 의견을 보내 주시면 감사하겠습니다. X 용 소켓 전달이 안전할지 모르겠지만 의도 한 용도는 도커 컨테이너를 로컬에서만 실행하는 것입니다.

또한이 스크립트는 약간의 약점을 가지고 있는데, 로컬 무선을 사용하기 때문에 컴퓨터의 IP 주소를 얻는 것이 쉽지 않기 때문에 항상 임의의 IP입니다.

컨테이너를 시작하는 데 사용하는 BASH 스크립트 :

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0

# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # random display number between 100 and 200

PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother

socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &

XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/Users/$USER" \
    -v "/Users/$USER:/home/$USER:rw" \
    -v $XSOCK:$XSOCK:rw \
    -v $XAUTH:$XAUTH:rw \
    -e DISPLAY=$IPADDR:$DISP_NUM \
    -e XAUTHORITY=$XAUTH \
    $CONTAINER \
    $COMMAND

rm -f $XAUTH
kill %1       # kill the socat job launched above

xeyes와 matplotlib를이 접근 방식으로 작업 할 수 있습니다.

Windows 7 이상

MobaXterm을 사용하면 Windows 7 이상에서 좀 더 편리합니다.

  1. Windows 용 MobaXterm 설치
  2. MobaXterm 시작
  3. X 서버 구성 : 설정 -> X11 (탭) -> X11 원격 액세스전체로 설정
  4. 이 BASH 스크립트를 사용하여 컨테이너를 시작하십시오.

run_docker.bash :

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/home/$USER" \
    -v "/c/Users/$USER:/home/$USER:rw" \
    -e DISPLAY \
    $CONTAINER \
    $COMMAND




lord.garbage 는 VNC, SSH 및 X11 전달을 사용하지 않고 컨테이너에서 GUI 응용 프로그램을 실행 하는 또 다른 솔루션을 제공 합니다. 여기에 언급되어 here .




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



방금이 블로그 항목을 발견하고 그것을 공유하고 싶습니다. 왜냐하면 최선의 방법이라고 생각하기 때문에 쉽게 공유 할 수 있습니다.

fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker

PROS :
+ 도커 컨테이너에 x 서버 항목 없음
+ vnc 클라이언트 / 서버 필요 없음
+ x 포워딩이없는 ssh 없음
+ 더 작은 도커 컨테이너

단점 :
- 호스트에서 x 사용 (보안 샌드 박스를위한 것이 아닙니다)

링크가 언젠가 실패하게 될 경우를 대비하여 여기에 가장 중요한 부분을 넣었습니다.
도커 파일 :

FROM ubuntu:14.04

RUN apt-get update && apt-get install -y firefox

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox

이미지를 빌드하십시오.

docker build -t firefox .

및 실행 명령 :

docker run -ti --rm \
   -e DISPLAY=$DISPLAY \
   -v /tmp/.X11-unix:/tmp/.X11-unix \
   firefox

물론 sh -c "echo script-here" 명령을 사용하여 실행 명령에서이 작업을 수행 할 수도 sh -c "echo script-here"

힌트 : 오디오는 https://.com/a/28985715/2835523 에서 살펴보십시오.




fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker 에서 주어진 해결책은 컨테이너 내부에서 GUI 응용 프로그램을 시작할 수있는 쉬운 방법 인 것 같습니다 (firefox 우분투 14.04 이상)하지만 저자가 게시 한 솔루션에는 약간의 추가 변경이 필요하다는 사실을 발견했습니다.

특히 컨테이너를 실행하기 위해 작성자는 다음과 같이 언급했습니다.

    docker run -ti --rm \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    firefox

그러나 나는 (동일한 사이트의 특정 코멘트를 기반으로) 두 개의 추가 옵션

    -v $HOME/.Xauthority:$HOME/.Xauthority

    -net=host 

firefox가 제대로 작동하려면 컨테이너를 실행하는 동안 지정해야합니다.

    docker run -ti --rm \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v $HOME/.Xauthority:$HOME/.Xauthority \
    -net=host \
    firefox

해당 페이지의 정보와 다음 추가 발견 사항으로 도커 이미지를 만들었습니다. https://hub.docker.com/r/amanral/ubuntu-firefox/




Links