Windows docker gui apps

Docker: запуск графических приложений в контейнерах

Строго говоря, Docker не был создан для подобного рода вещей, а именно запуска графических приложений. Однако, время от времени в темах про Docker звучат вопросы о том, нельзя ли запустить GUI-приложение в контейнере. Причины могут быть разные, но чаще всего, это желание сменить излишне громоздкую виртуальную машину на что-то полегче, не потеряв в удобстве и сохранив при этом достаточный уровень изоляции.

Это небольшой обзор способов запуска графических приложений в контейнерах Docker.

Оглавление

Монтирование устройств

Один из самых простых способов заставить контейнер говорить и показывать — это дать ему доступ к нашему экрану и звуковым устройствам.

Для того, чтобы приложения в контейнере могли подключиться к нашему экрану можно воспользоваться доменными сокетами Unix для X11, которые, обычно, лежать в директории /tmp/.X11-unix . Сокеты можно расшарить, смонтировав эту директорию с помощью параметра -v . Также необходимо настроить переменную окружения DISPLAY, которая указывает приложениям экран для вывода графики. Так как выводить мы будем на наш экран, то достаточно скопировать значение DISPLAY хостовой машины. Обычно, это :0.0 или просто :0 . Пустое имя хоста (перед двоеточием) подразумевает локальное соединение с использованием самого эффективного транспорта, что в большинстве случаев означает доменные сокеты Unix — как раз то, что нам нужно:

Приставка «unix» к DISPLAY здесь для явного указания использования unix-сокетов, но чаще всего в этом нет необходимости.

Ошибка авторизации

При запуске можно столкнуться с ошибкой вида:

Такое происходит, когда расширение Xsecurity блокирует неавторизованные подключения к X-серверу. Решить эту проблему можно, например, разрешив все локальные несетевые подключения:

Или ограничиться разрешением только для root-пользователя:

В большинстве случаев этого должно быть достаточно.

Другим вариантом будет дать контейнеру возможность самостоятельно авторизовываться на X-сервере с помощью заранее подготовленного и смонтированного Xauthority-файла. Создать такой можно с помощью утилиты xauth, которая способна извлекать и экспортировать данные для авторизации. Загвоздка, однако, в том, что такая авторизационная запись содержит в себе имя хоста, на котором запущен X-сервер. Просто скопировать ее в контейнер бесполезно — запись будет проигнорирована при попытке локального подключения к серверу. Решить эту проблему можно по-разному. Опишу пару способов.

Подмена имени хоста. Идея проста. Извлекаем авторизационную запись с помощью xauth list , меняем имя хоста на другое (нужно придумать заранее) и экспортируем получившийся ключ в Xauthority-файл, который затем монтируем в контейнер:

Подмена кода соединения. Первые два байта в каждой записи из Xauthority-файла содержат код соответствия семейству соединений (TCP/IP, DECnet, локальные соединения). Если этому параметру присвоить специальное значение FamilyWild (код 65535 или 0xffff), то запись будет соответствовать любому экрану и может быть использована для любого соединения (то есть имя хоста не будет иметь значения):

А что со звуком?

Подключение звуковых устройств также не составляет большого труда. В версиях Docker до 1.2 их можно смонтировать с помощью параметра -v , при этом контейнер должен быть запущен в привилегированном режиме:

В Docker 1.2 был добавлен специальный параметр —device для подключения устройств. К сожалению, на данный момент (версия 1.2), —device в качестве значения может принимать только одно устройство за раз, а значит придется явно перечислять их все. Например:

Читайте также:  Что нельзя удалять с папки windows

Возможно, функция обработки всех устройств в директории через —device будет добавлена в будущих релизах (на github есть соответствующий запрос).

В итоге

Суммируя, команда для запуска контейнера с графическим приложением выглядит примерно так:

Или, для Docker версии ниже 1.2:

Пример №1

Я подумал, что для проверки графики и звука подойдет какой-нибудь аудиоплеер с графическим интерфейсом и выбрал DeaDBeeF в качестве подопытного. Для запуска нам не понадобиться ничего, кроме образа с установленным плеером.

Теперь можно запустить его и послушать, например, радио (если решите попробовать — учтите, что плеер запустится на полной громкости):

Результатом должен стать работающий плеер

SSH -X

И это все, что вам нужно! Ну, почти. Параметр -X при создании ssh соединения включает перенаправление X11, что позволяет отображать на локальной машине графическое приложение, запущенное на удаленной. В данном случае под удаленной машиной можно понимать docker-контейнер.

В контейнере при этом должен быть установлен и запущен ssh-сервер. Также следует удостовериться, что в настройках сервера разрешено перенаправление X11. Проверить это можно заглянув в /etc/ssh/sshd_config и поискав параметр X11Forwarding (или его синонимы: ForwardX11 , AllowX11Forwarding ), который должен быть установлен в yes :

А звук?

После изменения настроек PulseAudio следует перезапустить:

В некоторых случаях может потребоваться перезагрузка. Проверить принялись ли настройки можно с помощью команды pax11publish , вывод которой должен выглядеть примерно так:

Здесь можно увидеть, что аудиосервер «слушает» на unix-сокете (unix:/run/user/1000/pulse/native), 4713-м TCP и TCP6 портах (стандартный порт для PulseAudio).

Для того, чтобы X-приложения в контейнере подключались к нашему pulse-серверу нужно указать его адрес в переменной окружения PULSE_SERVER:

Здесь «172.17.42.1» — это адрес моего docker-хоста. Узнать этот адрес можно, например, так:

То есть строка «tcp:172.17.42.1:4713» говорит, что подключиться к pulse-серверу можно по IP-адресу 172.17.42.1, где он слушает TCP-порт 4713.

В общем случае такой настройки достаточно. Отмечу только, что при использовании вышеописанного метода весь звук будет передаваться в открытом виде (нешифрованном), что в случае использования контейнера на локальном компьютере не имеет особого значения. Но при желании этот трафик можно и зашифровать. Для этого PULSE_SERVER следует настроить на любой свободный порт на localhost (например: «tcp:localhost:64713»). В результате аудиопоток будет идти на локальный порт 64713, который в свою очередь можно пробросить на 4713-й порт хостовой машины с помощью ssh -R :

Аудиоданные при этом будут передаваться по зашифрованному каналу.

Пример №2

Как и в предыдущем примере опишем образ плеера DeaDBeF c ssh-сервером. Я буду исходить из того, что все описанные выше предварительные настройки PulseAudio проведены, а значит остается только приступить к созданию docker-образа.

Код установки плеера будет повторят код из примера ранее. Нам лишь остается добавить установку openssh-сервера.

На этот раз кроме Dockerfile создадим еще один файл — entrypoint.sh. Это скрипт для «точки входа», т.е. он будет выполняться каждый раз при запуске контейнера. В нем мы будем создавать пользователя со случайным паролем, настраивать переменную окружения PULSE_SERVER и запускать ssh-сервер.

Порт и адрес pulse-сервера будем передавать в качестве параметров при запуске контейнера, чтобы иметь возможность поменять их на нестандартные (не забыв о значениях по умолчанию).

Запустим контейнер, не забыв указать хост PulseAudio (порт я опущу, так как у меня он стандартный), и назовем его «dead_player»:

Узнать пароль пользователя для подключения можно с помощью команды docker logs :

Для подключения по ssh можно использовать адрес как самого контейнера, так и адрес docker-хоста (при этом порт подключения будет отличаться от стандартного 22-го; в данном случае это будет 2222 — тот, который мы указали при запуске контейнера). Узнать IP контейнера можно с помощью команды docker inspect :

Читайте также:  Изменить цветовую температуру windows

Подключение к контейнеру:

Или через docker-шлюз:

Наконец, после авторизации, можно расслабиться и послушать музыку:

Subuser

Subuser позволяет запускать программы в изолированных контейнерах docker, беря на себя всю работу, связанную с созданием и настройкой контейнеров, поэтому пользоваться им могут даже люди, ничего не знающие о docker. Во всяком случае, такова идея проекта. Для каждого приложения-контейнера при этом устанавливаются ограничения в зависимости от его назначения — ограниченный доступ к директориям хоста, к сети, звуковым устройствам и т.д. По сути, subuser реализует удобную обертку над описанным здесь первым способом запуска графических приложений, так как запуск осуществляется с помощью монтирования нужных директорий, устройств и т.д.

К каждому создаваемому образу прилагается файл permissions.json, который определяет настройки доступа для приложения. Так, например, выглядит этот файл для образа vim:

Subuser имеет репозиторий (на данный момент — небольшой) готовых приложений, список которых можно увидеть с помощью команды:

Добавить приложение из репозитория можно так:

Эта команда установит приложение, назвав его firefox-flash, основанное на одноименном образе из репозитория по умолчанию.

Запуск приложения выглядит так:

Кроме использования стандартного репозитория, можно создавать и добавлять свои собственные subuser-приложения.

Проект довольно молодой и пока выглядит сыроватым, но свою задачу он выполняет. Код проекта можно найти на github: subuser-security/subuser

Пример №3

/.subuser-repo сойдет. В ней следует инициализировать git-репозиторий:

Здесь же создадим директорию для DeaDBeef:

Структура директории для subuser-образа выглядит следующим образом:

SubuserImagefile — это тот же Dockerfile. Разница лишь в возможности использовать инструкцию FROM-SUBUSER-IMAGE , которая принимает идентификатор существующего subuser-образа в качестве аргумента. Список доступных базовых образов можно посмотреть здесь: SubuserBaseImages.

Итак, подготовив структуру каталога, остается создать два файла: SubuserImagefile и permissions.json.

SubuserImagefile практически ничем не будет отличаться от приведенного ранее Dockerfile:

В permissions.json опишем параметры доступа для нашего плеера. Нам понадобиться экран, звук и интернет:

Параметр as-root позволяет запускать приложения в контейнере от имени root. По умолчанию subuser запускает контейнер с параметром —user , присваивая ему идентификатор текущего пользователя. Но deadbeef при этом отказывается запускаться (не может создать сокет-файл в домашней директории, которой не существует).

Закоммитим изменения в нашем импровизированном subuser-репозитории:

Теперь можно установить плеер из локального репозитория:

Наконец, запустить его можно с помощью следующей команды:

Удаленный рабочий стол

Раз уж контейнер так похож на виртуальную машину, а взаимодействие с ним напоминает сетевое, то в голову сразу приходит решение в виде систем удаленного доступа, таких как: TightVNC, Xpra, X2Go и т.п.

Такой вариант выглядит более накладным, так как требует установки дополнительного софта — как в контейнере, так и на локальном компьютере. Но у него есть и преимущества, например:

  • высокая изоляция контейнера, что увеличивает безопасность при запуске подозрительных программ
  • шифрование траффика
  • оптимизация передачи данных по сети
  • мультиплатформенность
  • а также возможность запуска полноценного графического окружения

Пример №4

В качестве примера я воспользуюсь X2Go, т.к. из опробованных мною решений он приглянулся своей простотой в использовании, а также встроенной поддержкой трансляции звука. Для подключения к x2go-серверу используется специальная программа x2goclient . Подробнее об установке сервера и клиента можно найти на официальном сайте проекта.

Что касается того, что мы будем ставить, то можно, конечно, в контейнер установить полноценную графическую оболочку вроде LXDE, XFCE, или даже Gnome с KDE. Но мне это показалось излишним для условий данного примера. Нам хватит и OpenBox.

Читайте также:  Linux mint зависает интерфейс

В контейнере кроме x2go-сервера также понадобится ssh-сервер. Поэтому код будет во многом похож на приведенный в примере №2. В той части, где ставится плеер, openssh-сервер, и сервер pulseaudio. То есть останется только добавить x2go-сервер и openbox:

Также немного изменим скрипт entrypoint.sh. Нам теперь нет необходимости настраивать переменную окружения PULSE_SERVER, поэтому от этого кода можно избавиться. Кроме того, пользователя для подключения следует добавить в группу x2gouser, иначе он не сможет запустить x2go-сессию:

Запустим контейнер в режиме демона:

Теперь, когда контейнер работает, к нему можно подключиться с помощью x2goclient, как мы подключались бы к любой удаленной машине. В настройках подключения в качестве хоста следует указать адрес либо самого контейнера, либо docker-хоста (в этом случае стоит также учесть нестандартный порт подключения ssh). Узнать логин и пароль для авторизации можно с помощью команды docker logs , как показано в примере №2. Для запуска openbox-сесcии в настройках «Session type» следует выбрать «Custom desktop» и в поле «Command» прописать «openbox-session».

После подключения можно запустить плеер через меню openbox (правый клик мышью) и проверить его работоспособность:

Но это уже совсем другая история.

Добавлю еще, что X2Go позволяет запускать одиночные приложения, так, как если бы они запускались на локальной машине. Для этого надо в клиенте в настройках «Session type» выбрать «Single application» и в «Command» прописать путь к исполняемому файлу. При этом в контейнере даже нет необходимости устанавливать графическую среду — достаточно иметь X2Go-сервер и желаемое приложение.

Графические приложения в Docker

Система управления контейнерами docker нацелена на то, чтобы процессы в ней работали без графического интерфейса. Однако иногда надо использовать её не совсем по назначению, например, запускать приложения, которые в обычном режиме будут работать в режиме без интерфейса, но для отладки надо посмотреть что они делают и что выводят.

В этой небольшой статье мы разберемся как запустить графическое приложение в Docker в операционных системах семейства Linux. Мы будем подключать наш основной X сервер к контейнеру.

Графические приложения в Docker

Первым делом надо сделать небольшое изменение для хоста, иначе, хост не разрешит приложению контейнера подключаться к X серверу. Создаем файл /tmp/docker.xauth:

А затем помещаем в него данные для аутентификации:

xauth nlist $DISPLAY | sed -e ‘s/^. /ffff/’ | xauth -f touch /tmp/docker.xauth nmerge —

Эти строки можно добавить в

/.xinitrc, чтобы файл аутентификации создавался автоматически при запуске X сервера:

XAUTH=/tmp/docker.xauth
touch $XAUTH
xauth nlist $DISPLAY | sed -e ‘s/^. /ffff/’ | xauth -f $XAUTH nmerge —

Теперь всё готово к запуску контейнера docker с gui. В контейнер мы примонтируем этот файл, UNIX сокет X сервера и передадим переменные, которые помогут приложениям в контейнере это всё найти. Для примера запустим gimp:

docker run —rm -ti -v /tmp/.X11-unix:/tmp/.X11-unix -v /tmp/docker.xauth:/tmp/docker.xauth -e DISPLAY=unix$DISPLAY -e XAUTHORITY=/tmp/docker.xauth jamesnetherton/gimp

Если вы хотите запустить более сложную программу, например браузер Google Chrome, то вам понадобиться подключить ещё и поддержку аппаратного ускорения видео, а также звук:

docker run —rm -ti -v /tmp/.X11-unix:/tmp/.X11-unix -v /tmp/docker.xauth:/tmp/docker.xauth -e DISPLAY=unix$DISPLAY -e XAUTHORITY=/tmp/docker.xauth —device /dev/dri:/dev/dri —device /dev/sni:/dev/sni dmitryrck/google-chrome

Выводы

В этой статье мы кратко разобрали как можно запускать графические приложения Docker. GUI — это довольно нестандартная задача, но и её можно решить.

Оцените статью