Привет Хабр! С вами снова ServerFlow, и сегодня мы хотим поговорить об удалённом доступе к графическим приложениям на Linux-серверах. Тема эта стала особенно актуальной в последнее время – всё больше задач требует работы с GPU на удалённых машинах. Будь то рендеринг в Blender на мощном сервере, работа с нейросетями или даже облачный гейминг.
X11 Forwarding: начинаем с простого
В нашей практике мы часто сталкиваемся с ситуациями, когда клиентам нужен доступ к графическому интерфейсу на сервере. Кому-то нужна GPU-ферма для рендеринга 3D графики, другим важно тестировать свои приложения в изолированной среде. А некоторые просто хотят организовать себе удалённое рабочее место с графическим ускорением. Сегодня разберём три популярных способа организации такого доступа – от простого к сложному.
Самый простой способ запустить графическое приложение с сервера – это X11 forwarding через SSH. Для этого даже не нужно ничего дополнительно устанавливать на сервер, достаточно базового пакета openssh.
Подключаемся к серверу командой:
ssh -X -p 47645 serverflow@IP_SSH_Сервера
Флаг -X включает проброс X11. Теперь можно запустить, например, Firefox прямо из консоли:
firefox &
На первый взгляд всё работает, но... При запуске браузера сразу становятся заметны подтормаживания интерфейса. Не говоря о том что интерфейс и пользовательский ввод отображаются на экране с очень заметной задержкой измеряемой даже не секундами, а порой минутами. С чем это связано? X11 forwarding работает напрямую с примитивами отрисовки, передавая по сети каждое действие с окном, почти никак не оптимизируя и не сжимая передаваемые данные, которых при передаче видео весьма не мало. На медленном соединении это создаёт заметные задержки.
Даже простая прокрутка веб-страницы генерирует тысячи X11-команд, которые должны быть переданы по сети. А теперь представьте, что происходит при воспроизведении видео или работе с 3D-графикой. Протокол просто не был рассчитан на такие сценарии использования.
Впрочем, винить за это мы X11 не будем, так как создавался он ещё в 1980х, для куда более простых графических интерфейсов, в куда меньших разрешениях и предназначался в основном для академической и корпоративной работы с мэинфреймами.
RDP в Docker: серьёзный подход
Перейдём к более современному решению – RDP-серверу в контейнере Docker с поддержкой GPU. В отличие от X11, протокол RDP изначально проектировался для работы с удалённым рабочим столом через сеть. Он использует умные алгоритмы сжатия, кэширование элементов интерфейса и различные оптимизации специально для графики.
Например, при прокрутке веб-страницы RDP не передаёт каждый кадр целиком. Вместо этого он может передать команду "возьми область экрана X и сдвинь её на Y пикселей вниз", а затем досылает только изменившуюся часть. При воспроизведении видео включаются специальные алгоритмы сжатия, похожие на те, что используются в современных видеокодеках.
Но прежде чем мы запустим наш RDP-сервер, нужно правильно настроить поддержку GPU в Docker. Начнём с установки драйверов и тулкита:
sudo pacman -S nvidia nvidia-container-toolkit
После установки настраиваем Docker для работы с GPU:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Nvidia Container Toolkit добавляет специальный рантайм для Docker, который умеет правильно пробрасывать драйверы и библиотеки CUDA в контейнер. Без этого слоя графическое ускорение работать не будет, так как контейнер по умолчанию изолирован от железа.
Теперь самое интересное – запуск контейнера с RDP-сервером. Мы используем образ от LinuxServer.io с предустановленным XFCE. Создаём директорию для настроек и запускаем:
mkdir -p ~/rdesktop-data
docker run -d \
--name=rdesktop-arch-xfce \
--gpus "device=0" \
--runtime=nvidia \
--security-opt seccomp=unconfined \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Europe/Moscow \
-p 3389:3389 \
-v ~/rdesktop-data:/config \
--device /dev/dri:/dev/dri \
--shm-size="1gb" \
--restart unless-stopped \
lscr.io/linuxserver/rdesktop:arch-xfce
Разберем каждый параметр, потому что от их правильной настройки зависит стабильность работы нашего удаленного рабочего стола:
Флаг -d запускает контейнер в фоновом режиме. Это значит, что даже если мы закроем терминал, контейнер продолжит работать. Удобно для долгоживущих сервисов вроде нашего RDP-сервера.
--name=rdesktop-arch-xfce даёт контейнеру понятное имя. Потом будет проще найти его в списке контейнеров или перезапустить при необходимости.
Следующие два параметра отвечают за работу с GPU:
--gpus "device=0" указывает какую именно видеокарту использовать, если их несколько
--runtime=nvidia включает специальный режим работы Docker для поддержки GPU
--security-opt seccomp=unconfined отключает некоторые ограничения безопасности. Обычно Docker сильно ограничивает что может делать приложение в контейнере, но для графического интерфейса и работы с GPU нам нужно больше свободы. Да, это немного снижает безопасность, но без этого параметра наш рабочий стол просто не запустится.
Далее идут переменные окружения:
PUID=1000 и PGID=1000 задают ID пользователя и группы внутри контейнера. Важно чтобы они совпадали с вашим пользователем на основной системе, иначе будут проблемы с правами доступа к файлам
TZ=Europe/Moscow устанавливает часовой пояс. Не забудьте поменять на свой, если живёте в другом регионе
-p 3389:3389 пробрасывает порт для RDP. Первое число – порт на вашей машине, второе – внутри контейнера. Можно изменить первое число если порт 3389 уже занят.
-v ~/rdesktop-data:/config монтирует директорию с настройками. Все изменения в рабочем столе будут сохраняться на вашем диске и переживут перезапуск контейнера. Очень удобно для бэкапов.
--device /dev/dri:/dev/dri даёт контейнеру прямой доступ к графическому оборудованию для аппаратного ускорения. Без этого параметра графика будет работать только через программный рендеринг.
--shm-size="1gb" увеличивает размер разделяемой памяти. По умолчанию он всего 64 мегабайта, чего явно мало для современных браузеров и других приложений. Гигабайт обычно хватает с запасом.
--restart unless-stopped говорит Docker'у перезапускать контейнер если он вдруг упадёт или сервер перезагрузится. Единственное исключение – если вы сами остановили контейнер командой stop.
Наконец, lscr.io/linuxserver/rdesktop:arch-xfce указывает какой образ использовать. Мы берём готовый образ от команды LinuxServer.io, который построен на базе Arch Linux и использует лёгкий рабочий стол XFCE.
Клиент для RDP – Remmina
После запуска контейнера нам понадобится RDP-клиент для подключения. В Linux одним из лучших вариантов является Remmina – он поддерживает множество протоколов, включая RDP, и умеет работать через SSH-туннель.
Сначала создаем туннель до нашего сервера:
ssh -L 3389:127.0.0.1:3389 -p 47645 serverflow@IP_SSH_Сервера
Запускаем Remmina и создаём новое подключение. В появившемся окне настройки выставляем следующие параметры:
На вкладке "Basic":
Name: любое удобное название
Protocol: RDP
Server: localhost:3389
Username: abc
Password: abc (это стандартные данные для образа, рекомендуем сменить после первого входа)
Переходим на вкладку "SSH Tunnel" и включаем туннелирование:
Enable SSH tunnel: включаем
Custom: выбираем этот вариант
SSH Server: вводим наш адрес сервера – IP_SSH_Сервера:47645
Username: serverflow
Authentication: выбираем Password или SSH key, если используете ключи
После сохранения профиля можно подключаться. При первом подключении Remmina спросит про сертификат RDP – соглашаемся и сохраняем его. Через несколько секунд мы увидим рабочий стол XFCE.
В чём преимущество такой схемы подключения через SSH туннель? Во-первых, это безопасность – весь RDP трафик шифруется SSH. Во-вторых, нам не нужно открывать порт RDP наружу, что снижает риски взлома. Ну и в-третьих, SSH умеет сжимать трафик, что иногда помогает на медленных каналах связи.
Теперь проверим работает ли проброс GPU в контейнер, для начала обновим систему через консоль и установим nvtop–
sudo pacman -Syu
sudo pacman -S nvtop
И как можно заметить система видит установленную в сервер видеокарту от Nvidia, Tesla P100.
Чтобы точно проверить всё ли работает, воспользуемся простой утилитой для базового бенчмарка - glmark2. Для начала установим её –
sudo pacman -S glmark2
И запустим –
nvidia-smi --query-gpu=utilization.gpu,temperature.gpu,memory.used --format=csv -l 1 & glmark2 && kill $!
Как можно заметить, для рендеринга glmark2 использует видеокарту и нагружает её на ~4-5%.
Подводим итоги
В итоге у нас получился полноценный удаленный рабочий стол с поддержкой GPU, работающий в изолированном контейнере. Производительность на высоте – можно комфортно работать с браузером, запускать требовательные приложения и даже смотреть видео без задержек. Во многом это заслуга RDP протокола, который гораздо эффективнее X11 forwarding в плане передачи графики по сети.
Контейнеризация дает нам гибкость – можно быстро развернуть такой же рабочий стол на другом сервере или сделать бэкап всех настроек. А поддержка GPU открывает интересные возможности, например для рендеринга 3D графики или работы с нейросетями прямо через удалённый доступ.
Конечно, настройка получилась чуть сложнее чем простой проброс X11, зато результат того стоит. К тому же, разобравшись один раз, повторить процесс уже гораздо проще.
В следующей статье мы пойдём дальше и покажем, как на базе этого решения сделать свой домашний игровой стрим-сервис. Окажется, что облачный гейминг в духе GeForce Now – это не сложнее чем VDI по RDP. Нужно будет только добавить поддержку геймпадов и настроить передачу звука. Так что оставайтесь на связи!
А пока делитесь в комментариях – как вы решаете задачу удаленного доступа к графическим приложениям? Может быть есть интересные кейсы использования GPU на удаленных серверах? До встречи в следующей статье!