Запускать приложения с использованием аудио в док-контейнере
Этот вопрос вдохновлен тем, можете ли вы запускать приложения с графическим интерфейсом в Docker-контейнере?,
Основная идея - запускать приложения с аудио и пользовательским интерфейсом (vlc, firefox, skype, ...)
Я искал докер-контейнеры с использованием pulseaudio, но все контейнеры, которые я нашел, использовали Pulseaudio с потоковой передачей по tcp. (безопасная песочница приложений)
- https://gist.github.com/hybris42/ce429de428e5af3a344a
- https://github.com/jlund/docker-chrome-pulseaudio
- https://github.com/tomparys/docker-skype-pulseaudio
В моем случае я бы предпочел проигрывать аудио из приложения внутри контейнера непосредственно на мой хост pulseaudio. (без туннелирования ssh и раздутых образов Docker)
Pulseaudio, потому что мое приложение QT использует его;)
6 ответов
Это заняло у меня некоторое время, пока я не узнал, что нужно. (Ubuntu)
мы начнем с команды запуска Docker docker run -ti --rm myContainer sh -c "echo run something"
ALSA:
нам нужно /dev/snd
и некоторый аппаратный доступ, как это выглядит. когда мы собрали это вместе, мы имеем
docker run -ti --rm \
-v /dev/snd:/dev/snd \
--lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
myContainer sh -c "echo run something"`
В новых версиях докера без флагов lxc вы должны использовать это:
docker run -ti --rm \
-v /dev/snd:/dev/snd \
--privileged \
myContainer sh -c "echo run something"`
PulseAudio:
Здесь нам нужно в основном /dev/shm
, /etc/machine-id
а также /run/user/$uid/pulse
, Но это еще не все (возможно, из-за Ubuntu и того, как они делали это в прошлом). Переменная окружения XDG_RUNTIME_DIR
должно быть одинаковым в хост-системе и в вашем док-контейнере. Вам также может понадобиться /var/lib/dbus
потому что отсюда некоторые приложения обращаются к идентификатору машины (может содержать только символическую ссылку на "реальный" идентификатор машины). И, по крайней мере, вам может понадобиться скрытая домашняя папка ~/.pulse
для некоторых временных данных (я не уверен в этом).
docker run -ti --rm \
-v /dev/shm:/dev/shm \
-v /etc/machine-id:/etc/machine-id \
-v /run/user/$uid/pulse:/run/user/$uid/pulse \
-v /var/lib/dbus:/var/lib/dbus \
-v ~/.pulse:/home/$dockerUsername/.pulse \
myContainer sh -c "echo run something"
В новых версиях докера вам может понадобиться добавить --privileged
,
Конечно, вы можете объединить оба вместе и использовать его вместе с xServer
Переадресация пользовательского интерфейса, как здесь: /questions/16455501/mozhete-li-vyi-zapuskat-prilozheniya-s-graficheskim-interfejsom-v-docker-kontejnere/16455537#16455537
Просто упомянуть:
- Вы можете обработать большую часть этого (все без используемого идентификатора) в
dockerfile
- с помощью
uid=$(id -u)
чтобы получить идентификатор пользователя и GID сid -g
- создание пользователя докера с этим идентификатором
создать скрипт пользователя:
mkdir -p /home/$dockerUsername && \
echo "$dockerUsername:x:${uid}:${gid}:$dockerUsername,,,:/home/$dockerUsername:/bin/bash" >> /etc/passwd && \
echo "$dockerUsername:x:${uid}:" >> /etc/group && \
mkdir /etc/sudoers.d && \
echo "$dockerUsername ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$dockerUsername && \
chmod 0440 /etc/sudoers.d/$dockerUsername && \
chown ${uid}:${gid} -R /home/$dockerUsername
Сегодня у нас есть Pipewire, и если вы хотите, чтобы приложение внутри Docker-контейнера воспроизводило/записывало звуки с помощью вашего сервера Pipewire, работающего на хосте, вот что вы можете сделать:
- Прежде всего, нам, очевидно, нужен сервер Pipewire, работающий на нашем хосте. В современных дистрибутивах Pipewire запускается, когда systemd достигает
graphical.target
и менеджер входа в систему (gdm, sddm, что угодно) выполняется. А затем, позже, когда пользователь входит в систему, Pipewire перезапускается и становится собственностью этого пользователя.
Чтобы приложение внутри Docker-контейнера могло использовать Pipewire, мы собираемся передать в этот контейнер дескриптор файла сокета . Для этого нам нужно знать, кто управляет сервером Pipewire на вашем хост-компьютере. Вы можете проверить это, посмотрев, под кем выполняются процессы Pipewire, а затем узнать, какой файл сокета будет правильным:
testuser@asrock-ubuntu:~$ ps -fp $(pgrep -d, -x pipewire)
UID PID PPID C STIME TTY TIME CMD
testuser 4250 4206 1 13:32 ? 00:00:27 /usr/bin/pipewire
testuser@asrock-ubuntu:~$ id testuser
uid=1000(testuser) gid=1000(testuser) groups=1000(testuser),4(adm),6(disk),24(cdrom),27(sudo),30(dip),46(plugdev),105(input),107(kvm),122(lpadmin),134(lxd),135(sambashare),140(libvirt),64055(libvirt-qemu),997(docker)
Итак, в нашем случае я ожидаю, что должен быть файл сокета/run/user/1000/pipewire-0
. Проверьте это в своей системе.
- Теперь мы собираемся создать облегченный докер-контейнер Alpine, передав ему файл сокета Pipewire и установив переменную XDG_RUNTIME_DIR, которая необходима для клиента Pipewire внутри контейнера.
docker run -it -v /run/user/1000/pipewire-0:/tmp/pipewire-0 -e XDG_RUNTIME_DIR=/tmp --rm alpine /bin/ash
- Внутри контейнера мы устанавливаем в него несколько пакетов, чтобы все работало:
- alsa-utils — предоставляет набор утилит ALSA, которые мы собираемся использовать в качестве примера программного обеспечения, для которого требуется ALSA.
- Pipewire — двоичные файлы сервера (и клиента) Pipewire. Но мы не собираемся запускать полноценный сервер в контейнере, вовсе нет, нам просто нужно, чтобы там была его клиентская часть.
- Pipewire-alsa — представляет виртуальное устройство ALSA «pipewire», которое мы собираемся использовать в нашем программном обеспечении, для которого требуется ALSA. Проще говоря, это мост между ALSA и нашим клиентом Pipewire.
apk add pipewire-alsa pipewire alsa-utils
- Теперь проверим, все ли работает. Убедитесь, что вы видите устройство «pipewire» при запуске
aplay -L
а затем запустите наш простой тест, чтобы воспроизвести шум через это устройство:
speaker-test -Dpipewire -c2
В этот момент вы должны услышать приближающийся звук. Если это не так, проверьте устройство-приемник Pipewire по умолчанию на своем хосте . Если вы используете Gnome, откройте «Настройки Gnome» -> «Звук» -> «Вывод».
Теперь внутри контейнера мы можем записать волновой файл с помощью arecord и воспроизвести его с помощью чистых инструментов ALSA:
arecord --duration=5 --device=pipewire test.wav
aplay --device=pipewire test.wav
- И последнее, прежде чем мы продолжим: давайте также проверим, могут ли собственные клиенты Pipewire выполняться внутри этого контейнера. Для этого мы устанавливаем
pipewire-tools
. Пример wav-файла, который я собираюсь воспроизвести, поставляется вместе сalsa-utils
пакет, который мы установили ранее:
apk add pipewire-tools
pw-play /usr/share/sounds/alsa/Rear_Center.wav
Вкратце, общая установка выглядит примерно так:
Вдохновленный ссылками, которые вы разместили, я смог создать следующее решение. Он настолько легкий, насколько я мог. Однако я не уверен, является ли он (1) безопасным, и (2) полностью соответствует вашему варианту использования (так как он все еще использует сеть).
- устанавливать
paprefs
в вашей хост-системе, например, используяsudo apt-get install paprefs
на машине с Ubuntu. - Запустите PulseAudio Preferences, перейдите на вкладку "Сетевой сервер" и установите флажок "Включить сетевой доступ к локальным звуковым устройствам" [1].
- Перезагрузите компьютер. (Только перезапуск Pulseaudio не работал для меня в Ubuntu 14.10)
- Установите Pulseaudio в свой контейнер, например
sudo apt-get install -y pulseaudio
- В вашем контейнере запустите
export "PULSE_SERVER=tcp:<host IP address>:<host Pulseaudio port>"
, Например,export "PULSE_SERVER=tcp:172.16.86.13:4713"
[2]. Вы можете узнать свой IP-адрес, используяifconfig
и порт Pulseaudio, используяpax11publish
[1]. - Вот и все. Шаг 5, вероятно, следует автоматизировать, если IP-адрес и порт Pulseaudio могут быть изменены. Кроме того, я не уверен, что Docker постоянно хранит переменные окружения, такие как
PULSE_SERVER
: Если этого не произойдет, вы должны инициализировать его после каждого запуска контейнера.
Буду очень признателен за предложения по улучшению моего подхода, поскольку в настоящее время я работаю над такой же проблемой, что и ОП.
Рекомендации:
[1] https://github.com/jlund/docker-chrome-pulseaudio
[2] https://github.com/jlund/docker-chrome-pulseaudio/blob/master/Dockerfile
ОБНОВЛЕНИЕ (и, вероятно, лучшее решение):
Это также работает с использованием сокета Unix вместо сокета TCP:
- Начать контейнер с
-v /run/user/$UID/pulse/native:/path/to/pulseaudio/socket
- В контейнере беги
export "PULSE_SERVER=unix:/path/to/pulseaudio/socket"
/path/to/pulseaudio/socket
может быть что угодно, для тестирования я использовал /home/user/pulse
,
Возможно, он даже будет работать с тем же путем, что и на хосте (с учетом части $UID) в качестве сокета по умолчанию, таким образом, окончательное решение будет -v /run/user/$UID/pulse/native:/run/user/<UID in container>/pulse
; Я не проверял это однако.
Попробовав большинство описанных здесь решений, я обнаружил, что только PulseAudio по сети действительно работает. Однако вы можете сделать это безопасно, сохранив аутентификацию.
Установите paprefs (на хост-машине):
$ apt-get install paprefs
запуск
paprefs
(Настройки PulseAudio) > Сетевой сервер> [X] Включить сетевой доступ к локальным звуковым устройствам.Перезапустите PulseAudio:
$ service pulseaudio restart
Проверьте, сработало или перезагрузите машину:
$ (pax11publish || xprop -root PULSE_SERVER) | grep -Eo 'tcp:[^ ]*' tcp:myhostname:4713
Теперь используйте этот сокет:
$ docker run \
-e PULSE_SERVER=tcp:$(hostname -i):4713 \
-e PULSE_COOKIE=/run/pulse/cookie \
-v ~/.config/pulse/cookie:/run/pulse/cookie \
...
Убедитесь, что пользователь, работающий внутри контейнера, имеет доступ к файлу cookie ~/.config/pulse/cookie
,
Чтобы проверить это работает:
$ apt-get install mplayer
$ mplayer /usr/share/sounds/alsa/Front_Right.wav
Для получения дополнительной информации можете проверить Docker Mopidy проекта.
Предполагая, что pulseaudio установлен на хосте и в образе, можно передавать звук pulaudio через tcp всего за несколько шагов. pulseaudio не нужно перезапускать, и не нужно настраивать ни хост, ни образ. Таким образом, он включен в x11docker, без необходимости использования VNC или SSH:
Во-первых, найдите бесплатный порт TCP:
read LOWERPORT UPPERPORT < /proc/sys/net/ipv4/ip_local_port_range
while : ; do
PULSE_PORT="`shuf -i $LOWERPORT-$UPPERPORT -n 1`"
ss -lpn | grep -q ":$PULSE_PORT " || break
done
Получить IP-адрес демона Docker. Я всегда нахожу это 172.17.42.1/16
ip -4 -o a | grep docker0 | awk '{print $4}'
Загрузите tcp модуль pulseaudio, аутентифицируйте соединение с ip докера:
PULSE_MODULE_ID=$(pactl load-module module-native-protocol-tcp port=$PULSE_PORT auth-ip-acl=172.17.42.1/16)
При запуске докера создайте переменную среды PULSE_SERVER
docker run -e PULSE_SERVER=tcp:172.17.42.1:$PULSE_PORT yourimage
После этого выгрузите модуль tcp. (Примечание: по неизвестным причинам выгрузка этого модуля может остановить демон pulseaudio на хосте):
pactl unload-module $PULSE_MODULE_ID
Мне удалось докеризировать Java-игру следующими способами, эффективно пропуская звук игры.
Этот подход требует создания образа и проверки наличия у приложения всех необходимых зависимостей, в данном случае — pulseaudio и x11. Если вы уверены, что в ваших изображениях есть все необходимое, вы можете действовать, как указано в предыдущих ответах.
Здесь нам нужно создать образ, после чего мы сможем его запустить.
docker build -t my-unciv-image . # Run from directory where Dockerfile is
docker run --name unciv # image name\
--device /dev/dri \
-e DISPLAY=$DISPLAY \
-e PULSE_SERVER=unix:/run/user/1000/pulse/native \
--privileged \
-u $(id -u):$(id -g) \
-v /path/to/Unciv:/App \
-v /run/user/$(id -u)/pulse:/run/user/(id -u)/pulse \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-w /App \
my-unciv-image \
java -jar /App/Unciv.jar
Во второй команде указывается следующее:
-
--name
: контейнеру дается имя -
--device
: видеоустройство* -
-e
: требуемые переменные окружения-
DISPLAY
: отображаемый номер - : Сокет аудиосервера PulseAudio
-
-
--privileged
: запустите ip привилегированный *, чтобы он мог получить доступ ко всем устройствам -
-v
: Установленные тома:- Путь к игре, смонтированной в /App в контейнере**
- Разъем для аудиосервера
- Показать сокет сервера
-
-w
: Рабочий каталог
Вотdocker-compose.yml
его версия:
# docker-compose.yml
version: '3'
services:
unciv:
build: .
container_name: unciv
devices:
- /dev/dri:/dev/dri # * Either this
entrypoint: java -jar /App/Unciv.jar
environment:
- DISPLAY=$DISPLAY
- PULSE_SERVER=unix:/run/user/1000/pulse/native
privileged: true # * or this
user: 1000:1000
volumes:
- /path/to/game/:/App
- /run/user/1000/pulse:/run/user/1000/pulse
- /tmp/.X11-unix:/tmp/.X11-unix
working_dir: /App
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install openjdk-11-jre -y
RUN apt-get install -y xserver-xorg-video-all
RUN apt-get install -y libgl1-mesa-glx libgl1-mesa-dri
RUN apt-get install -y pulseaudio
USER unciv
Примечания:
- *Требуется только для игр или всего, что использует openGL. Либо передавать устройства явно, либо запускать их как привилегированные, но я думаю, что достаточно передать устройство, сделать его привилегированным может быть излишним.
- **Эта математика может быть включена в образ докера, но для демонстрации.
- Для аудио необходимо передать переменную env
PULSE_SERVER
и монтаж разъема pulseaudio