web-dev-qa-db-fra.com

Alternatives à ssh X11-forwarding pour les conteneurs Docker

J'exécute un conteneur Docker principalement comme un environnement de développement isolé pour le langage R. (L'utilisation de R ici est orthogonale au reste de l'article, c'est-à-dire que vous pouvez simplement supposer que n'importe quel programme générique pouvant s'exécuter dans une session repl-.) Souvent, cela impliquera faire des trucs comme tracer, faire des graphiques et ainsi de suite; et je dois les regarder. Par conséquent, je préférerais avoir la possibilité d'afficher les graphiques que j'ai créés dans mon conteneur. Voici comment je fais cela jusqu'à présent. Je crée d'abord un Dockerfile. En laissant de côté les étapes triviales, les plus pertinentes sont:

# Set root passwd 
RUN echo "root:test" | chpasswd

# Add user so that container does not run as root 
RUN useradd -m docker 
RUN echo "docker:test" | chpasswd 
RUN usermod -s /bin/bash docker 
RUN usermod -aG Sudo docker 
ENV HOME /home/docker

RUN mkdir /var/run/sshd 
RUN mkdir -p /var/log/supervisor

# copy servisord.conf which lists the processes to be spawned once this 
# container is started (currently only one: sshd) 
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 22 
CMD ["/usr/bin/supervisord"]

Je crée l'image puis démarre le conteneur en utilisant:

docker run -d -p 127.0.0.1:5000:22 -h ubuntu-r -v /home/chb/files/Data:/home/docker/Data -P --name="rdev" ubuntu-r

et peut ensuite ssh dans mon conteneur:

ssh -X docker@localhost -p 5000.

Cela me donnera ce que je veux. Mais je voudrais savoir s'il existe un autre moyen plus respectueux des ressources pour obtenir des sorties graphiques/GUI à partir d'un conteneur? (Je préférerais, si possible, que les solutions n'impliquent pas vnc.)

49
lord.garbage

Il existe un moyen agréable et semi-facile d'obtenir la sortie graphique d'un conteneur Docker sans avoir à exécuter un démon sshd à l'intérieur du conteneur. Docker peut fournir des performances nues lors de l'exécution d'un seul processus qui dans ce cas est censé être R. L'exécution d'un démon sshd, aussi marginale soit-elle, introduira une surcharge supplémentaire. Cela n'est pas amélioré en exécutant le démon sshd en tant que processus enfant du démon superviseur. Les deux peuvent être supprimés lorsque l'on fait bon usage des supports de reliure. Après avoir construit l'image à partir de laquelle le conteneur est censé être exécuté, nous démarrons un conteneur interactif et y lions le dossier /tmp/.X11-unix. Je vais énoncer la commande complète et expliquer en détail ce qu'elle fait:

docker run -i -t --rm \

  • -i Organise une session interactive; -t Alloue un pseudo tty; --rm Rend ce conteneur éphémère

-e DISPLAY = $ DISPLAY \

  • définit l'affichage de l'hôte sur l'affichage des machines locales (qui sera généralement :0)

-u docker \

  • -u Spécifiez que le processus doit être exécuté par un utilisateur (ici docker) et non par root. Cette étape est importante (v.i.)!

-v /tmp/.X11-unix:/tmp/.X11-unix:ro \

  • La liaison -v Monte le socket X11 Résidant dans /tmp/.X11-unix Sur votre machine locale dans /tmp/.X11-unix Dans le conteneur et :ro Rend le socket en lecture seule .

--name = "rdev" ubuntu-r R

  • --name="" Spécifiez le nom du conteneur (ici rdev); l'image à partir de laquelle vous souhaitez exécuter le conteneur (ici ubuntu-r); le processus que vous souhaitez exécuter dans le conteneur (ici R). (La dernière étape de spécification d'un processus n'est nécessaire que si vous n'avez pas défini un CMD ou ENTRYPOINT par défaut pour votre image.)

Après avoir lancé cette commande, vous devriez regarder la belle sortie de démarrage R. Si vous deviez essayer demo(graphics) pour voir si la sortie graphique fonctionne déjà, vous remarquerez que ce n'est pas le cas. Cela est dû à l'extension Xsecurity qui vous empêche d'accéder au socket. Vous pouvez maintenant taper xhost + Sur votre machine locale et réessayer demo(graphics) dans votre conteneur. Vous devriez maintenant avoir une sortie graphique. Cependant, cette méthode est fortement déconseillée car vous autorisez l'accès à votre xsocket à tout hôte distant auquel vous êtes actuellement connecté. Tant que vous n'interagissez qu'avec des systèmes à utilisateur unique, cela peut être justifié d'une manière ou d'une autre, mais dès qu'il y aura plusieurs utilisateurs impliqués, ce sera absolument dangereux! Par conséquent, vous devez utiliser une méthode moins dangereuse. Un bon moyen est d'utiliser le serveur interprété

xhost +si:localuser:username

qui peut être utilisé pour spécifier un seul utilisateur local (voir man xhost). Cela signifie que username doit être le nom de l'utilisateur qui exécute le serveur X11 Sur votre machine locale et qui exécute le conteneur Docker. C'est également la raison pour laquelle il est important de spécifier un utilisateur lors de l'exécution de votre conteneur. Enfin, il existe toujours la solution la plus complexe qui consiste à utiliser les fichiers xauth et .Xauthority Pour accorder l'accès à la socket X11 (Voir man xauth). Cela impliquera cependant un peu plus de connaissances sur le fonctionnement de X.

L'effet positif que cela peut avoir peut être vu dans le nombre de processus qui doivent être exécutés pour atteindre ce que l'on souhaite.

(1) avec supervisor et sshd s'exécutant dans le conteneur:

UID                 PID                 PPID                C                STIME               TTY                 TIME                CMD
root                4564                718                 1                18:16               ?                   00:00:00            /usr/bin/python /usr/bin/supervisord
root                4576                4564                0                18:16               ?                   00:00:00            /usr/sbin/sshd

lorsque vous êtes connecté via ssh et exécutez R:

UID                 PID                 PPID                C                 STIME               TTY                 TIME                CMD
root                4564                718                 0                 18:16               ?                   00:00:00            /usr/bin/python /usr/bin/supervisord
root                4576                4564                0                 18:16               ?                   00:00:00            /usr/sbin/sshd
root                4674                4576                0                 18:17               ?                   00:00:00            sshd: docker [priv]   
chb                 4725                4674                0                 18:18               ?                   00:00:00            sshd: docker@pts/0
chb                 4728                4725                1                 18:18               pts/0               00:00:00            -bash

(2) avec la méthode de montage bind:

UID                 PID                 PPID                C                 STIME               TTY                 TIME                CMD
chb                 4356                718                 0                 18:12               pts/4               00:00:00            /usr/local/lib/R/bin/exec/R --no-save --no-restore
79
lord.garbage

Voici clairement la meilleure solution que j'ai trouvée jusqu'à présent:

https://stackoverflow.com/a/25280523/13539 (tout le mérite revient à Jürgen Weigert)

Avantages:

  • Dans Docker, l'UID n'est pas pertinent (mais je recommande toujours de ne pas utiliser root)
  • Sur l'hôte, vous n'avez pas à modifier les paramètres de sécurité (xhost +)
4
Daniel Alder