Альтернативы ssh X11-forwarding для контейнеров Docker
Я использую контейнер Docker в основном как изолированную среду разработки для R
язык. (Использование R
здесь ортогонально к остальной части поста, то есть вы можете просто принять любую общую программу, которая может работать в repl
-сессия.) Много раз это будет включать в себя такие вещи, как прорисовка, создание графики и т. д.; и мне нужно посмотреть на это. Следовательно, я предпочел бы иметь возможность показа графики, которую я создал в своем контейнере. Вот как я это делаю до сих пор. Сначала я создаю Dockerfile
, Оставляя тривиальные шаги, наиболее важными являются:
# 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"]
Я создаю изображение, а затем запускаю контейнер с помощью:
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
и может затем SSH в мой контейнер:
ssh -X docker@localhost -p 5000.
Это даст мне то, что я хочу. Но я хотел бы знать, есть ли другой более дружественный к ресурсам способ получения графики / графического интерфейса пользователя из контейнера? (Я бы предпочел, если возможно, решения не будут включать vnc
.)
3 ответа
Есть хороший и полу-простой способ получения графического вывода из контейнера Docker без необходимости запуска sshd
демон внутри контейнера. Docker может обеспечить чистую производительность при запуске одного процесса, который в этом случае должен быть R
, Запуск демона sshd, как бы мало это ни было, привнесет дополнительные издержки. Это не улучшится, если запустить демон sshd как дочерний процесс демона supervisor. С обоими можно обойтись, если хорошо использовать крепления. После создания образа, из которого предполагается запустить контейнер, мы запускаем интерактивный контейнер и связываем монтируем/tmp/.X11-unix
папка в него. Я изложу всю команду и подробно объясню, что она делает:
docker run -i -t --rm \
-i
устанавливает интерактивный сеанс;-t
выделяет псевдотет;--rm
делает этот контейнер эфемерным
-e DISPLAY=$DISPLAY \
- устанавливает отображение хоста на дисплей локальных машин (который обычно
:0
)
-у докер \
-u
указать процесс должен быть запущен пользователем (здесьdocker
) а не по руту. Этот шаг важен (vi)!
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v
привязка монтируетX11
гнездо, проживающее в/tmp/.X11-unix
на вашей локальной машине в/tmp/.X11-unix
в контейнере и:ro
делает сокет только для чтения.
--name = "rdev" ubuntu-r R
--name=""
укажите название контейнера (здесьrdev
); изображение, из которого вы хотите запустить контейнер (здесьubuntu-r
); процесс, который вы хотите запустить в контейнере (здесьR
). (Последний шаг определения процесса необходим, только если вы не установили значение по умолчаниюCMD
или жеENTRYPOINT
для вашего имиджа.)
После выдачи этой команды вы должны смотреть на красивые R
начать вывод. Если бы вы попробовали demo(graphics)
чтобы увидеть, работает ли графический вывод, вы должны заметить, что это не так. Это из-за Xsecurity
расширение, не позволяющее вам получить доступ к сокету. Теперь вы можете напечатать xhost +
на вашем локальном компьютере и попробуйте demo(graphics)
снова в вашем контейнере. Теперь у вас должен быть графический вывод. Однако этот метод настоятельно не рекомендуется, поскольку вы разрешаете доступ к вашему xsocket любому удаленному хосту, к которому вы в данный момент подключены. Пока вы взаимодействуете только с однопользовательскими системами, это может быть как-то оправдано, но как только в нем участвуют несколько пользователей, это будет абсолютно небезопасно! Следовательно, вы должны использовать менее опасный метод. Хороший способ - использовать интерпретированный сервер
xhost +si:localuser:username
который может быть использован для указания одного локального пользователя (см. man xhost
). Это означаетusername
должно быть имя пользователя, который запускает X11
сервер на вашей локальной машине и который запускает Docker-контейнер. Это также причина, по которой важно указывать пользователя при запуске вашего контейнера. Наконец, что не менее важно, всегда есть более сложное решение использования xauth
а также .Xauthority
файлы для предоставления доступа к X11
гнездо (см. man xauth
). Это, однако, также будет включать в себя немного больше знаний о том, как X
работает.
Положительный эффект, который это может иметь, можно увидеть в количестве процессов, которые необходимо запустить для достижения желаемого.
(1) с supervisor
а также sshd
работает в контейнере:
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
при входе через ssh
и работает 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) с методом крепления:
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
Вот, безусловно, лучшее решение, которое я нашел до сих пор:
/questions/16455501/mozhete-li-vyi-zapuskat-prilozheniya-s-graficheskim-interfejsom-v-docker-kontejnere/16455517#16455517 (все кредиты принадлежат Юргену Вайгерту)
Преимущества:
- Внутри докера умереть UID не актуален (но я все же рекомендую не использовать root)
- На хосте вам не нужно менять настройки безопасности (
xhost +
)
Основываясь на ответе lord.garbage, мы можем перенаправить сокет X11 хоста. Точно так же мы можем создать пользователя во время создания образа докера в соответствии с очень хорошей идеей Юргена Вайгерта, изложенной выше (спасибо Хьюго / Даниэлю). Однако что, если мы не хотим изменять xhost ACL или создавать образ, который статически привязан к учетные данные одного пользователя? Мы можем сделать еще одно уточнение:
При запуске вашего контейнера перенаправить идентификатор пользователя / группу из оболочки хоста:
XSOCK=/tmp/.X11-unix
docker run -it --rm \
-e HOST_USER=$(id -u) \
-e HOST_GROUP=$(id -g) \
-e HOST_USER_NAME=$(whoami) \
-e DISPLAY=$DISPLAY \
-v $XSOCK:$XSOCK \
my_container:latest
Создайте сценарий оболочки и установите его как ENTRYPOINT вашего контейнера. В сценарии создайте нового пользователя с идентификатором пользователя / идентификатором группы хоста, а затем используйте sudo для запуска целевого приложения «как хост»:
groupadd -o -g $HOST_GROUP docker
useradd -d /home/$HOST_USER_NAME -s /bin/bash -u $HOST_USER -g docker $HOST_USER_NAME
sudo -u $HOST_USER_NAME bash -c "<some bash commands etc.>"
Поскольку программа будет выполняться с правильным пользователем, изменения управления доступом не требуются. Благодаря отличной идее Юргена Вайгерта и этой небольшой доработке у нас есть решение, которое не требует xhost и будет работать где угодно.