Czy możesz uruchamiać aplikacje GUI w kontenerze Docker?

409

Jak uruchomić aplikacje GUI w kontenerze Docker ?

Czy są jakieś obrazy, które się konfigurują vncserverlub coś, co pozwala - na przykład - dodać dodatkową piaskownicę Speedbump wokół powiedzmy Firefox?

Będzie
źródło
To pytanie wydaje się dotyczyć tylko Linuksa (na podstawie wieku i treści odpowiedzi), a nie Windows. Jeśli tak, czy możemy edytować tytuł, aby to wyjaśnić? Dzięki
UuDdLrLrSs
Powiązane: Jak wyświetlić Xvfb?
kenorb
Zapoznaj się z instrukcją obsługi kontenerów wizualizacyjnych HPC, aby uzyskać kilka pomysłów.
kenorb

Odpowiedzi:

238

Możesz po prostu zainstalować serwer vncserver wraz z Firefoksem :)

Wepchnąłem obraz, vnc / firefox, tutaj: docker pull creack/firefox-vnc

Obraz został utworzony za pomocą tego pliku Dockerfile:

# Firefox over VNC
#
# VERSION               0.1
# DOCKER-VERSION        0.2

FROM    ubuntu:12.04
# Make sure the package repository is up to date
RUN     echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN     apt-get update

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN     apt-get install -y x11vnc xvfb firefox
RUN     mkdir ~/.vnc
# Setup a password
RUN     x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN     bash -c 'echo "firefox" >> /.bashrc'

Spowoduje to utworzenie kontenera Docker z uruchomionym VNC z hasłem 1234:

W przypadku Docker w wersji 18 lub nowszej:

docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

W przypadku Docker w wersji 1.3 lub nowszej:

docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

W przypadku Dockera przed wersją 1.3:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create
skrzyp
źródło
2
Jak miałbym używać klienta VNC, aby wyświetlać to zdalnie? Wpisywanie w porcie IP + wydaje się nie działać.
user94154
17
Najpierw musisz sprawdzić przydzielony port (robiąc to docker inspect <container id>lub po prostu docker ps, a następnie łączysz się z
adresem
9
obraz creackfirefox-vnc kończy się niepowodzeniem z błędem: wprowadź hasło VNC: stty: standardowe wejście: niewłaściwy ioctl dla urządzeń przenośnych: brak takiego pliku lub katalogu stty: standardowe wejście: niewłaściwy ioctl dla urządzenia x11vnc -usepw: nie można znaleźć hasła do użycia.
alfonsodev
6
Dobrze korzystaj z dokera> Uruchamianie aplikacji GUI z Docker fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker
Dennis C
7
Nie ma nazwy użytkownika, hasło jest wyraźnie wskazane w odpowiedzi, a zrobi to każdy klient vnc. W moim przypadku lubię natywną wersję OSX. (w wyszukiwarce naciśnij klawisze + K i połącz się z vnc: // <docker ip>: <port narażony na kontener>)
creack
195

Xauthority staje się problemem w nowszych systemach. Mogę odrzucić dowolną ochronę za pomocą xhost + przed uruchomieniem kontenerów dokerów lub przekazać dobrze przygotowany plik Xauthority. Typowe pliki Xauthority są specyficzne dla nazwy hosta. W przypadku dokera każdy kontener może mieć inną nazwę hosta (ustawioną za pomocą polecenia -h run -h), ale nawet ustawienie nazwy hosta kontenera identycznego z systemem hosta nie pomogło w moim przypadku. xeyes (podoba mi się ten przykład) po prostu zignoruje magiczne ciasteczko i nie przekaże poświadczeń serwerowi. Dlatego pojawia się komunikat o błędzie „Nie określono protokołu Nie można otworzyć wyświetlacza”

Plik Xauthority można zapisać w taki sposób, aby nazwa hosta nie miała znaczenia. Musimy ustawić rodzinę uwierzytelniania na „FamilyWild”. Nie jestem pewien, czy xauth ma do tego odpowiednią linię poleceń, więc oto przykład, który łączy w sobie xauth i sed. Musimy zmienić pierwsze 16 bitów wyjścia nlist. Wartość FamilyWild wynosi 65535 lub 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
Jürgen Weigert
źródło
8
Tylko notatkę, którą -v $XSOCK:$XSOCK -v $XAUTH:$XAUTHmożna skrócić-v $XSOCK -v $XAUTH
Piotr Aleksander Chmielowski
2
@PiotrAleksanderChmielowski, który nie działał dla mnie, Docker wersja 1.12.0, kompilacja 8eab29e
tbc0
14
@Dirk: Czasami warto wymienić :0z $DISPLAY. To znaczy xauth nlist $DISPLAY | ...i docker run -ti -e DISPLAY=$DISPLAY .... Zwykle X DISPLAY jest :0, ale nie zawsze (a zwłaszcza nie, jeśli łączysz się przez ssh -X).
johndodo
4
Na Ubuntu 16.04 xauth tworzy /tmp/.docker.xauthplik z 600uprawnieniami. Powoduje to, że xauth wewnątrz kontenera dokowanego nie może odczytać pliku. Możesz to sprawdzić, uruchamiając xauth listw kontenerze dokera. Dodałem chmod 755 $XAUTHpo xauth nlist :0 | ...poleceniu, aby to rozwiązać.
Abai
2
@Abai Po co używać 755, jeśli wystarczy 444 lub 644?
Daniel Alder,
68

Właśnie znalazłem ten wpis na blogu i chcę się nim z tobą podzielić, ponieważ uważam, że jest to najlepszy sposób na zrobienie tego i to takie proste.

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

Plusy:
+ brak x elementów serwera w kontenerze dokera
+ brak potrzeby klienta / serwera vnc
+ brak ssh z przekazywaniem x
+ dużo mniejsze kontenery

CONS:
- użycie x na hoście (nie jest przeznaczone do bezpiecznego piaskownicy)

na wypadek, gdyby link kiedyś się nie
udał, umieściłem tutaj najważniejszą część: plik dockerfile:

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

zbuduj obraz:

docker build -t firefox .

i polecenie uruchomienia:

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

oczywiście możesz to również zrobić w poleceniu uruchomienia za pomocą sh -c "echo script-here"

WSKAZÓWKA: w sprawie dźwięku spójrz na: https://stackoverflow.com/a/28985715/2835523

A. Binzxxxxxx
źródło
Jak mogę to zrobić w systemie Windows 7? Czy muszę zainstalować serwer X?
walksignison
3
Ponieważ większość odpowiedzi tutaj dotyczy to tylko unixa, myślę - dopóki Windows nie obsługuje systemu okien X-server.
A. Binzxxxxxx
Czy uważasz, że to może zadziałać, jeśli zainstaluję serwer X w systemie Windows lub nawet dołączę serwer X do mojego kontenera Docker?
walksignison
1
Myślę, że musisz również zainstalować w Dockerfile, apt-get -y install sudoaby utworzyć /etc/sudoers.dfolder.
mulg0r
1
może być również konieczne zezwolenie na połączenia z X z dowolnego hosta z$ xhost +
Bandoos
52

Dzięki woluminom danych dokera bardzo łatwo jest wyeksponować gniazdo domeny unix xorg wewnątrz kontenera.

Na przykład za pomocą takiego pliku Docker:

FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes

Możesz wykonać następujące czynności:

$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes

Jest to w zasadzie to samo, co przekazywanie X. Zapewnia kontenerowi pełny dostęp do serwera xserver na hoście, więc jest to zalecane tylko wtedy, gdy ufasz temu, co jest w środku.

Uwaga: Jeśli obawiasz się o bezpieczeństwo, lepszym rozwiązaniem byłoby ograniczenie aplikacji do obowiązkowej lub opartej na rolach kontroli dostępu. Docker osiąga całkiem dobrą izolację, ale został zaprojektowany z myślą o innym celu. Skorzystaj z AppArmor , SELinux lub GrSecurity , które zostały zaprojektowane, aby rozwiązać Twój problem.

Aryeh Leib Taurog
źródło
5
Musisz także zezwolić na dostęp do X Server z innych hostów za pomocą narzędzia takiego jak xhost. Aby całkowicie go otworzyć, użyj go xhost +na hoście.
Tully,
3
@ Tylko Tully xhost +localjest konieczne. Lepiej byłoby jednak udostępnić ~/.Xauthorityplik w kontenerze, aby mógł się uwierzytelnić.
Aryeh Leib Taurog
3
czy udało Ci się uruchomić go na komputerze Mac (przy użyciu boot2docker)?
Karl Forner
4
Działało mi to całkiem nieźle na laptopie Ubuntu 14.04 z dokerem 1.5 wcześniej; ale teraz zawodzi dla mnie w Ubuntu 15.04, doker 1.6.2, z błędem Can't open display: :0. Jakieś pomysły?
cboettig,
6
Kiedyś xhost +si:localuser:$USERautoryzowałem tylko użytkownika uruchamiającego kontener.
Nick Breen
26

Możesz także użyć subuser: https://github.com/timthelion/subuser

Umożliwia to pakowanie wielu aplikacji GUI w oknie dokowanym. Firefox i emacs zostały do ​​tej pory przetestowane. W przypadku Firefoxa webGL nie działa. Chrom w ogóle nie działa.

EDYCJA: Dźwięk działa!

EDYCJA 2: Od czasu, gdy opublikowałem to po raz pierwszy, subuser bardzo się rozwinął. Mam teraz stronę internetową na subuser.org i nowy model bezpieczeństwa do łączenia się z X11 poprzez mostkowanie XPRA .

timthelion
źródło
3
Pamiętaj, że subuser jest wciąż bardzo nowy i stosunkowo niesprawdzony. Jeśli napotkasz jakiekolwiek problemy, prześlij raporty błędów!
timthelion
Unikałbym X11, jeśli jest jakikolwiek sposób. Twoja aplikacja-zabójca uruchomiłaby proxy Tora w oknie dokowanym i działającą pełną przeglądarkę z wtyczkami w podrzędnym oknie dokowanym, tak że zapora ogniowa itp. Wymusza na całej sieci wyjście za pośrednictwem okna dokowanego Tor. Spowodowałoby to okrążenie obecnego pakietu przeglądarki Tor dla użyteczności sieci, ponieważ przepuszczasz bogatą zawartość.
Czy
1
Czy masz problem z bezpieczeństwem X11? A może chcesz, aby działało to w systemie Windows? Czy chcesz, aby działało to zdalnie? Wszystkie powyższe? Myślę, że wykonanie tej pracy z vnc jest całkiem możliwe (chociaż nie uczyniłbym tego domyślną metodą, ponieważ dodaje zależność od vnc). Zdalne uruchamianie subuser nie jest tak naprawdę możliwe / sensowne. Jest też: github.com/rogaha/docker-desktop, ale z raportów o błędach wynika, że ​​xpra może być bezużyteczna w prawdziwym życiu.
timthelion
24

OSX

Jürgen Weigert ma najlepszą odpowiedź, która działała dla mnie na Ubuntu, jednak w OSX doker działa w VirtualBox, więc rozwiązanie nie działa bez dalszej pracy.

Pracuję z tymi dodatkowymi składnikami:

  1. Xquartz (OSX nie jest już dostarczany z serwerem X11)
  2. przekierowywanie gniazd za pomocą socat (brew install socat)
  3. skrypt bash, aby uruchomić kontener

Doceniam komentarze użytkowników, aby poprawić tę odpowiedź dla OSX, nie jestem pewien, czy przekazywanie gniazd dla X jest bezpieczne, ale moim zamierzonym zastosowaniem jest uruchamianie kontenera dokera tylko lokalnie.

Ponadto skrypt jest nieco delikatny, ponieważ nie jest łatwo uzyskać adres IP urządzenia, ponieważ jest on w naszej lokalnej sieci bezprzewodowej, więc zawsze jest to losowy adres IP.

Skrypt BASH, którego używam do uruchomienia kontenera:

#!/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

Jestem w stanie uzyskać xeyes i matplotlib do pracy z tym podejściem.

Windows 7+

Z MobaXterm jest trochę łatwiej w Windows 7+:

  1. Zainstaluj MobaXterm dla Windows
  2. Uruchom MobaXterm
  3. Skonfiguruj serwer X: Ustawienia -> X11 (karta) -> ustaw Zdalny dostęp X11 na pełnej
  4. Użyj tego skryptu BASH, aby uruchomić kontener

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

xeyes działający na PC

Nacięcie
źródło
nie rozumiem, co masz na myśli przez skrypt bash - jak uruchomić go w systemie Windows?
deller
@deller Robię programowanie w systemie Windows za pomocą GIT, więc mam dostępną powłokę GIT-bash.
Nick
Postępowałem zgodnie ze wskazówkami. Jednak dostaję error: XDG_RUNTIME_DIR not set in the environment.i Error: cannot open display: VAIO:0.0. Czy napotkałeś coś takiego?
user3275095,
1
Pojawia się błąd związany z brakiem znalezienia użytkownika, tj. „Brak pasujących wpisów w pliku passwd” Jakieś potencjalne szanse?
walksignison
19

Udostępnianie wyświetlacza hosta: 0, jak stwierdzono w niektórych innych odpowiedziach, ma dwie wady:

  • Przerywa izolację pojemnika z powodu niektórych wycieków bezpieczeństwa X. Na przykład, rejestrowanie za pomocą klawiszy z xevlub xinputjest możliwe, a także zdalne sterowanie aplikacjami hosta za pomocą xdotool.
  • Aplikacje mogą mieć problemy z renderowaniem i błędami dostępu do pamięci RAM z powodu braku pamięci współdzielonej dla rozszerzenia X MIT-SHM. (Można to również naprawić za pomocą opcji obniżającej izolację --ipc=host).

Poniżej przykładowy skrypt do uruchomienia obrazu dokera w Xephyr, który rozwiązuje te problemy.

  • Unika przecieków bezpieczeństwa X, gdy aplikacje dokujące działają na zagnieżdżonym serwerze X.
  • MIT-SHM jest wyłączony, aby uniknąć błędów dostępu do pamięci RAM.
  • Zwiększono bezpieczeństwo kontenerów --cap-drop ALL --security-opt no-new-privileges. Również użytkownik kontenera nie jest rootem.
  • Pliki cookie X są tworzone w celu ograniczenia dostępu do wyświetlania Xephyr.

Skrypt oczekuje pewnych argumentów, najpierw menedżera okien hosta do uruchomienia w Xephyr, drugiego obrazu dokera, opcjonalnie trzeciego do wykonania polecenia obrazu. Aby uruchomić środowisko pulpitu w oknie dokowanym, użyj „:” zamiast menedżera okien hosta.

Zamknięcie okna Xephyr powoduje zamknięcie aplikacji kontenera dokującego. Zakończenie zadokowanych aplikacji zamyka okno Xephyr.

Przykłady:

  • xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
  • xephyrdocker : x11docker/lxde
  • xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom

skrypt xephyrdocker:

#! /bin/bash
#
# Xephyrdocker:     Example script to run docker GUI applications in Xephyr.
#
# Usage:
#   Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run without window manager from host, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"

# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"

# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
  [ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber

# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd

# 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
# --user $Useruid:$Usergid           Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro       /etc/passwd file with user entry
# --group-add audio                  Allow access to /dev/snd if shared with '--device /dev/snd' 
# --cap-drop ALL                     Security: disable needless capabilities
# --security-opt no-new-privileges   Security: forbid new privileges
Dockercommand="docker run --rm \
  -e DISPLAY=:$Newdisplaynumber \
  -e XAUTHORITY=/Xcookie \
  -v $Xclientcookie:/Xcookie:ro \
  -v $Newxsocket:$Newxsocket:rw \
  --user $Useruid:$Usergid \
  -v $Etcpasswd:/etc/passwd:ro \
  --group-add audio \
  --env HOME=/tmp \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  $(command -v docker-init >/dev/null && echo --init) \
  $Dockerimage"

echo "docker command: 
$Dockercommand
"

# command to run Xorg or Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber             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
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp                  disable tcp connections for security reasons
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
  -auth $Xservercookie \
  -extension MIT-SHM \
  -nolisten tcp \
  -screen 1000x750x24 \
  -retro"

echo "X server command:
$Xcommand
"

# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd

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

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

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

  echo "# create new XAUTHORITY cookie file" 
  echo ":> $Xclientcookie"
  echo "xauth add :$Newdisplaynumber . $(mcookie)"
  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 '":$Newdisplaynumber | sed -e 's/^..../ffff/')" 
  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
  echo "cp $Xclientcookie $Xservercookie"
  echo "chmod 644 $Xclientcookie"

  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 "# run docker"
  echo "$Dockercommand"
} > $Xinitrc

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

Ten skrypt jest utrzymywany na wiki x11docker . Bardziej zaawansowanym skryptem jest x11docker, który obsługuje również takie funkcje, jak przyspieszenie GPU, udostępnianie kamery internetowej i drukarek itd.

mviereck
źródło
18

Oto lekkie rozwiązanie, które pozwala uniknąć konieczności instalowania dowolnego Xserwera, vncserwera lub sshddemona na kontenerze. To, co zyskuje na prostocie, traci w bezpieczeństwie i izolacji.

Zakłada, że ​​łączysz się z hostem za sshpomocąX11 przekazywania.

W sshdkonfiguracji hosta dodaj linię

X11UseLocalhost no

Tak że przekazywane port serwera X na komputerze są otwarte na wszystkich interfejsach (nie tylko lo), w szczególności w wirtualnym interfejsem Docker, docker0.

Po uruchomieniu kontener potrzebuje dostępu do .Xauthoritypliku, aby mógł połączyć się z serwerem. W tym celu definiujemy wolumin tylko do odczytu wskazujący katalog domowy na hoście (być może nie jest to mądry pomysł!), A także odpowiednio ustawiamy XAUTHORITYzmienną.

docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority

To nie wystarczy, musimy również przekazać zmienną DISPLAY z hosta, ale zastępując nazwę hosta ip:

-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")

Możemy zdefiniować alias:

 alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'

I przetestuj tak:

dockerX11run centos xeyes
danidiaz
źródło
2
(Jest to idealne rozwiązanie dla zaufanych aplikacji. W przypadku każdego typu piaskownicy chcesz jednak uniknąć przekazywania X.)
Czy
1
Jeśli nie chcesz zamontować cały katalog domowy do pojemnika można po prostu zamontować .Xauthoritysam plik: -v $HOME/.Xauthority:/root/.Xauthority -e XAUTHORITY=/root/.Xauthority.
Robert Haines,
2
Zamiast zmiany X11UseLocalhostmożesz również użyć dodatkowej opcji --net=hostdla docker runpolecenia ( tutaj ).
ingomueller.net
--net=hostto zły pomysł, ponieważ teraz, jeśli otworzysz port w kontenerze, zostanie on również otwarty w hoście ...
MrR
16

Podczas gdy odpowiedź Jürgena Weigerta zasadniczo dotyczy tego rozwiązania, z początku nie było dla mnie jasne, co tam opisano. Dodam więc moje zdanie na wypadek, gdyby ktokolwiek potrzebował wyjaśnień.

Po pierwsze, odpowiednią dokumentacją jest strona bezpieczeństwa X. .

Liczne źródła online sugerują po prostu montaż gniazda unix X11 i ~/.Xauthority pliku w kontenerze. Te rozwiązania często działają na szczęście, nie do końca rozumiejąc, dlaczego np. Użytkownik kontenera ma ten sam UID co użytkownik, więc nie ma potrzeby autoryzacji za pomocą magicznego klucza.

Po pierwsze, plik Xauthority ma tryb 0600, więc użytkownik kontenera nie będzie mógł go odczytać, dopóki nie będzie miał tego samego UID.

Nawet jeśli skopiujesz plik do kontenera i zmienisz własność, jest jeszcze jeden problem. Jeśli uruchomisz xauth listna hoście i kontenerze, z tym samym Xauthorityplikiem, zobaczysz różne wpisy na liście. Wynika to z tego, że xauthfiltruje wpisy w zależności od miejsca ich uruchomienia.

Klient X w kontenerze (tj. Aplikacja GUI) będzie zachowywać się tak samo jak xauth. Innymi słowy, nie widzi magicznego ciasteczka dla sesji X uruchomionej na pulpicie użytkownika. Zamiast tego widzi wpisy dla wszystkich „zdalnych” sesji X, które wcześniej otworzyłeś (wyjaśnione poniżej).

Musisz zatem dodać nowy wpis z nazwą hosta kontenera i tym samym kluczem szesnastkowym, co plik cookie hosta (tj. Sesja X uruchomiona na pulpicie), np .:

containerhostname/unix:0   MIT-MAGIC-COOKIE-1   <shared hex key>

Problem polega na tym, że ciasteczko musi zostać dodane xauth addwewnątrz pojemnika:

touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>

W przeciwnym razie xauthoznacz go w taki sposób, aby był widoczny tylko poza kontenerem.

Format tego polecenia to:

xauth add hostname/$DISPLAY protocol hexkey

Gdzie .reprezentuje MIT-MAGIC-COOKIE-1protokół.

Uwaga: Nie ma potrzeby kopiowania ani łączenia z oprawą .Xauthoritydo kontenera. Wystarczy utworzyć pusty plik, jak pokazano, i dodać plik cookie.

Odpowiedź Jürgena Weigerta omija ten problem, używając FamilyWildtypu połączenia do utworzenia nowego pliku uprawnień na hoście i skopiowania go do kontenera. Zauważ, że najpierw wyodrębnia klucz szesnastkowy dla bieżącej sesji X z ~/.Xauthorityużyciaxauth nlist .

Tak więc niezbędne kroki to:

  • Wyodrębnij klucz szesnastkowy pliku cookie dla bieżącej sesji X użytkownika.
  • Utwórz nowy plik Xauthority w kontenerze, używając nazwy hosta kontenera i udostępnionego klucza szesnastkowego (lub utwórz plik cookie o FamilyWildtypie połączenia).

Przyznaję, że nie rozumiem zbyt dobrze, jak FamilyWilddziała, ani w jaki sposób xauthlub X klienci filtrują wpisy z pliku Xauthority w zależności od miejsca ich uruchomienia. Dodatkowe informacje na ten temat są mile widziane.

Jeśli chcesz rozpowszechniać aplikację Docker, potrzebujesz skryptu startowego do uruchomienia kontenera, który pobiera klucz szesnastkowy dla sesji X użytkownika i importuje go do kontenera na jeden z dwóch opisanych wcześniej sposobów.

Pomaga również zrozumieć mechanikę procesu autoryzacji:

  • Klient X (tj. Aplikacja GUI) działający w kontenerze szuka w pliku Xauthority pliku cookie zgodnego z nazwą hosta kontenera i wartością $DISPLAY.
  • Jeśli zostanie znaleziony pasujący wpis, klient X przekazuje go z żądaniem autoryzacji do serwera X, przez odpowiednie gniazdo w /tmp/.X11-unixkatalogu zamontowanym w kontenerze.

Uwaga: gniazdo X11 Unix nadal musi być zamontowane w kontenerze, w przeciwnym razie kontener nie będzie miał trasy do serwera X. Większość dystrybucji domyślnie wyłącza dostęp TCP do serwera X ze względów bezpieczeństwa.

Aby uzyskać dodatkowe informacje i lepiej zrozumieć, jak działa relacja klient / serwer X, warto również przyjrzeć się przykładowemu przypadkowi przekazywania SSH X:

  • Serwer SSH działający na zdalnym komputerze emuluje własny serwer X.
  • Ustawia wartość $DISPLAYw sesji SSH tak, aby wskazywała na własny serwer X.
  • Służy xauthdo tworzenia nowego pliku cookie dla zdalnego hosta i dodaje go do Xauthorityplików zarówno dla użytkowników lokalnych, jak i zdalnych.
  • Po uruchomieniu aplikacji GUI rozmawiają z emulowanym serwerem X SSH.
  • Serwer SSH przesyła te dane z powrotem do klienta SSH na pulpicie lokalnym.
  • Lokalny klient SSH wysyła dane do sesji X serwera działającej na pulpicie, tak jakby klient SSH był w rzeczywistości klientem X (tj. Aplikacją GUI).
  • Serwer X używa otrzymanych danych do renderowania GUI na pulpicie.
  • Na początku tej wymiany zdalny klient X wysyła również żądanie autoryzacji, używając właśnie utworzonego pliku cookie. Lokalny serwer X porównuje go z lokalną kopią.
orodbhen
źródło
12

Nie jest to lekkie, ale jest dobrym rozwiązaniem, które zapewnia równość funkcji dokera z pełną wirtualizacją pulpitu. Zarówno Xfce4, jak i IceWM dla Ubuntu i CentOS działają, a ta noVNCopcja umożliwia łatwy dostęp za pośrednictwem przeglądarki.

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

Działa noVNCtak samo jak tigerVNCvncserver. Następnie wzywa startxdanego menedżera okien. Ponadto libnss_wrapper.sosłuży do emulacji zarządzania hasłami dla użytkowników.

dashy
źródło
czy ktoś to przetestował?
guilhermecgs
3
@ guilhermecgs tak i działa dobrze. Od tego czasu próbowałem również xpraw oknie dokowanym, które nie zawiera rootów X. xprabyło najlepiej dostosowane IMO i jest bardziej wydajne niż VNC.
dashy
Żeby było jasne ... Czy mogę mieć pełny obraz pulpitu (GNOME, KDE) z tym obrazem?
guilhermecgs
Próbowałem tylko Xfce4 i IceWM (które jest w tym repozytorium). Oczywiście doświadczenie będzie ograniczone, na przykład urządzenia do montażu nie pojawią się na pulpicie (gvfs), chyba że przejdziesz --device /dev/...do dokera i nie ustawisz niezbędnych --capuprawnień. To przeczy celowi powstrzymywania, ale możesz przechodzić przez urządzenia. Z pewnymi poprawkami powinno być możliwe uruchomienie GNOME / KDE pod VNC. Uruchomiłem wiele X w oknie dokowanym z kartami NVIDIA (bez VNC lub Xpra), więc z pewnością jest to wykonalne.
dashy
Jak dotąd nie próbowaliśmy. Największym wyzwaniem byłoby stworzenie działającego demona D-Bus. Większość komputerów gnome lub KDE będzie ich potrzebować. Niech projekt ubuntu-desktop-lxde-vnc ci w tym pomoże.
toschneck
11

Rozwiązanie podane na stronie http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ wydaje się być łatwym sposobem uruchamiania aplikacji GUI z kontenerów (próbowałem dla Firefoxa nad Ubuntu 14.04), ale zauważyłem, że wymagana jest niewielka dodatkowa zmiana w rozwiązaniu opublikowanym przez autora.

W szczególności do uruchomienia kontenera autor wspomniał:

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

Ale odkryłem, że (na podstawie konkretnego komentarza na tej samej stronie), że dwie dodatkowe opcje

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

i

    -net=host 

należy określić podczas działania kontenera, aby Firefox działał poprawnie:

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

Utworzyłem obraz dokera z informacjami na tej stronie i tymi dodatkowymi ustaleniami: https://hub.docker.com/r/amanral/ubuntu-firefox/

Anshuman Manral
źródło
3
Przekonałem się, że wcale nie musisz przepuszczać /tmp/.X11-unixgniazda. Po prostu działa z mocowaniem .Xauthorityi --net=host.
CMCDragonkai
2
To właściwie jedyne rozwiązanie, które działa obecnie. Używanie /tmp/.X11-unixjako woluminu już nie działa, ponieważ doker po cichu odmawia montowania woluminów z lepkich katalogów.
Christian Hujer,
1
Myślę, że to zależy od używanej dystrybucji. Zdecydowanie można zamontować złącze X11 Unix na CentOS. Ważne jest również, aby zrozumieć, co --network=hostrobi. Daje to kontenerowi pełny dostęp do stosu sieciowego hosta, co może być niepożądane, w zależności od tego, co próbujesz zrobić. Jeśli po prostu majstrujesz przy uruchamianiu kontenerowych interfejsów GUI na pulpicie, nie powinno to mieć znaczenia.
orodbhen
7

Istnieje inne rozwiązanie autorstwa lord.garbage, aby uruchamiać aplikacje GUI w kontenerze bez korzystania z przekazywania VNC, SSH i X11. Jest tu również wspomniane .

niutech
źródło
1
Jest to świetne, jeśli bezpieczeństwo nie stanowi problemu. Jeśli celem dokowania jest izolowanie go, najlepiej unikać X11 wlotu z pojemnika.
Czy
7

Jeśli chcesz uruchomić aplikację GUI bez głowy, przeczytaj tutaj . Musisz stworzyć wirtualny monitor z xvfbinnym podobnym oprogramowaniem. Jest to bardzo pomocne, jeśli chcesz uruchomić testy Selenium na przykład w przeglądarkach.

Niczego nie wspomniano nigdzie, że niektóre programy same używają piaskownic w kontenerach Linux. Na przykład Chrome nigdy nie będzie działał normalnie, jeśli nie użyjesz odpowiedniej flagi --privilegedpodczas uruchamiania kontenera.

Pithikos
źródło
6

Jestem spóźniony na imprezę, ale dla użytkowników komputerów Mac, którzy nie chcą iść ścieżką XQuartz, oto działający przykład, który buduje obraz Fedory, używając środowiska graficznego (xfce) przy użyciu Xvfbi VNC. To proste i działa:

Na komputerze Mac możesz po prostu uzyskać do niego dostęp za pomocą aplikacji Udostępnianie ekranu (domyślne), łączącej się z localhost:5901.

Plik Docker:

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

Sprawdź połączony plik Readme pod kątem poleceń budowania i uruchamiania, jeśli chcesz / potrzebujesz.

Vince
źródło
5

Na podstawie odpowiedzi Jürgena Weigerta mam pewną poprawę:

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

Jedyna różnica polega na tym, że tworzy katalog $ XAUTH_DIR, który służy do umieszczenia pliku $ XAUTH i zamontowania katalogu $ XAUTH_DIR zamiast pliku $ XAUTH w kontenerze dokera.

Zaletą tej metody jest to, że możesz napisać polecenie w /etc/rc.local, aby utworzyć pusty folder o nazwie $ XAUTH_DIR w / tmp i zmienić jego tryb na 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

Po ponownym uruchomieniu systemu, przed zalogowaniem użytkownika, doker automatycznie zainstaluje katalog $ XAUTH_DIR, jeśli zasada restartu kontenera jest „zawsze”. Po zalogowaniu użytkownika możesz napisać polecenie w ~ / .profile, aby utworzyć plik $ XAUTH, a następnie kontener automatycznie użyje tego pliku $ 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

W końcu kontener automatycznie pobierze plik Xauthority przy każdym ponownym uruchomieniu systemu i zalogowaniu użytkownika.

allenyllee
źródło
4

Inne rozwiązania powinny działać, ale oto rozwiązanie docker-compose.

Aby naprawić ten błąd, musisz przekazać $ DISPLAY i .X11-unix do dokera, a także przyznać użytkownikowi, który rozpoczął dokowanie dostęp do xhost.

W docker-compose.ymlpliku:

version: '2'
services:
    node:
        build: .
        container_name: node
        environment:
            - DISPLAY
        volumes:
            - /tmp/.X11-unix:/tmp/.X11-unix

W terminalu lub skrypcie:

  • xhost +si:localuser:$USER
  • xhost +local:docker
  • export DISPLAY=$DISPLAY
  • docker-compose up
Unieważnić
źródło
3

Możesz zezwolić użytkownikowi Docker (tutaj: root) na dostęp do ekranu 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
wedesoft
źródło
2

OSX (10.13.6, wysoka sierra)

Podobne do @Nick , ale jego rozwiązanie nie działało dla mnie.

Najpierw zainstaluj socat, wykonując brew install socati zainstaluj XQuartz ( https://www.xquartz.org/ )

Następnie wykonaj następujące kroki tutaj ( http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ ) w sekcji komentarzy:

1. in one mac terminal i started:

socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"

2. and in another mac terminal I ran:

docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox

Mogłem również uruchomić CLion z mojego kontenera dokującego Debiana.

nommer
źródło
1

Doker z siecią BRIDGE. dla Ubuntu 16.04 z menedżerem wyświetlania lightdm:

cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf

[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp

możesz użyć więcej prywatnych uprawnień

xhost +

docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name
dabizharoman
źródło
1

Jeszcze jedna odpowiedź na wypadek, gdybyś już zbudował obraz:

  1. invoke docker bez sudo ( Jak naprawić okno dokowane: Mam problem odmowy uprawnień )

  2. udostępnij tego samego USER & home & passwd między hostem i kontenerem (porady: użyj identyfikatora użytkownika zamiast nazwy użytkownika)

  3. folder dev dla bibliotek zależnych od sterownika działa dobrze

  4. plus X11 do przodu.

    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

możesz zapytać, jaki jest sens używania dokera, jeśli tak wiele rzeczy jest takich samych? cóż, jednym z powodów, dla których mogę wymyślić, jest pokonanie piekła z depresją pakietów ( https://en.wikipedia.org/wiki/Dependency_hell ).

Myślę, że ten rodzaj użytkowania jest bardziej odpowiedni dla programistów.

TingQian LI
źródło
To jedyny, który by mi pasował. Do moich celów udało mi się to do tego zminimalizować: uruchom docker --network = host --volume = echo ~: / home / $ {USER} --user = id -u ${USER}--env = "DISPLAY" --volume = "/ etc / passwd: / etc / passwd: ro "-it REPO: TAG / bin / bash
user1145922
1

Udało mi się uruchomić strumień wideo z kamery USB, używając opencvw dockerwykonując następujące kroki:

  1. Pozwól dokerowi uzyskać dostęp do serwera X.

    xhost +local:docker
    
  2. Utwórz gniazdo X11 Unix i plik uwierzytelniania X.

    XSOCK=/tmp/.X11-unix
    XAUTH=/tmp/.docker.xauth
    
  3. Dodaj odpowiednie uprawnienia

    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
    
  4. Ustaw szybkość renderowania Qt na „natywną”, aby nie pomijała silnika renderowania X11

    export QT_GRAPHICSSYSTEM=native
    
  5. Powiedz Qt, aby nie korzystał z MIT-SHM (pamięci współdzielonej) - w ten sposób powinno być również bezpieczniejsze pod względem bezpieczeństwa

    export QT_X11_NO_MITSHM=1
    
  6. Zaktualizuj polecenie uruchamiania dokera

    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
    

Uwaga: po zakończeniu projektu przywróć kontrolę dostępu do wartości domyślnej - xhost -local:docker

Więcej informacji: Używanie GUI z Dockerem

Kredyt: Wykrywanie obiektów w czasie rzeczywistym i przetwarzanie wideo przy użyciu Tensorflow, OpenCV i Docker

tsveti_iko
źródło
„Utwórz gniazdo X11 Unix i plik uwierzytelniający X” nie tworzy żadnych plików, tylko definiuje zmienne?
MrR