Docker:在多个容器上共享/ dev / snd会导致“设备或资源繁忙”

时间:2018-08-15 13:27:01

标签: docker alsa

adding host device--device /dev/snd)到Docker容器时,有时会遇到Device or resource busy错误。

示例

我通过一个涉及音频(alsa)的最小示例重现了该问题。这是我的Dockerfile(产生图像docker-device-example):

FROM    debian:buster

RUN     apt-get update \
 &&     apt-get install -y --no-install-recommends \
            alsa-utils \
 &&     rm -rf /var/lib/apt/lists/*

我正在运行以下命令(speaker-test是用于生成可用来测试扬声器的音调的工具),并且共享了/dev/snd

docker run --rm \
    -i -t \
    --device /dev/snd \
    docker-device-example \
    speaker-test

问题

运行上一个命令时,会播放粉红声音,但仅在某些情况下

  • 如果我没有在主机上播放任何声音:例如,如果我正在播放视频,并且即使视频暂停了,命令也会失败
  • 如果我没有运行另一个容器来访问/dev/snd设备

使用/dev/snd时似乎被“锁定”了,如果是这样,我得到以下输出(错误由后两行表示):

speaker-test 1.1.6

Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Using 16 octaves of pink noise
ALSA lib pcm_dmix.c:1099:(snd_pcm_dmix_open) unable to open slave
Playback open error: -16,Device or resource busy

反之亦然,如果在容器上播放了粉红色的声音,那么我将无法在主机上播放任何声音(Ubuntu)。但是主机上的命令不会因相同的消息而失败。取而代之的是,主机上的命令(例如aplay test.wav播放简单的声音)会无限期地被阻止(即使随后关闭容器)。

我尝试通过运行strace aplay test.way进行调试,并且该命令似乎在poll系统调用中被阻止:

poll([{fd=3, events=POLLIN|POLLERR|POLLNVAL}], 1, 4294967295

问题

如何从两个(或更多)不同的容器中,或从主机和容器中同时播放声音?

其他信息

我已经用/dev/snd重现了该问题,但我不知道在使用其他设备时是否发生了类似的事情,或者是否与声音设备或alsa有关。

还请注意,当同时在主机上全部同时运行多个speaker-testaplay命令(不涉及容器)时,将播放所有声音。

1 个答案:

答案 0 :(得分:4)

我不知道如何使用ALSA解决此问题,但是可以提供2种可能的方法来使用Pulseaudio。如果这些设置失败,请在映像中安装pulseaudio,以确保完全满足依赖关系。

ALSA直接访问声音硬件,并阻止其他客户端对其进行访问。但是可以设置ALSA服务于多个客户端。那必须由其他人来回答。可能需要进行一些ALSA dmix plugin设置。


  1. 带有共享套接字的脉冲音频:

创建pulseaudio套接字:

pactl load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket

为Pulseaudio客户端创建/tmp/pulseaudio.client.conf

default-server = unix:/tmp/pulseaudio.socket
# Prevent a server running in the container
autospawn = no
daemon-binary = /bin/true
# Prevent the use of shared memory
enable-shm = false

与docker共享套接字和配置文件,并设置环境变量PULSE_SERVERPULSE_COOKIE。容器用户必须与主机上的用户相同:

docker run --rm \
    --env PULSE_SERVER=unix:/tmp/pulseaudio.socket \
    --env PULSE_COOKIE=/tmp/pulseaudio.cookie \
    --volume /tmp/pulseaudio.socket:/tmp/pulseaudio.socket \
    --volume /tmp/pulseaudio.client.conf:/etc/pulse/client.conf \
    --user $(id -u):$(id -g) \
    imagename

cookie将由Pulseaudio本身创建。


  1. 基于TCP的脉冲音频:

从主机获取IP地址:

# either an arbitrary IPv4 address
Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep -v 127.0.0.1 | head -n1)"

# or especially IP from docker daemon
Hostip="$(ip -4 -o a| grep docker0 | awk '{print $4}' | cut -d/ -f1)"

运行docker映像。您需要一个免费的TCP端口,此处使用34567。 (TCP端口号必须在cat /proc/sys/net/ipv4/ip_local_port_range范围内,并且不能使用。请检查ss -nlp | grep 34567。)

docker run --rm \
    --name pulsecontainer \
    --env PULSE_SERVER=tcp:$Hostip:34567 \
    imagename

docker run获得以下容器的IP:

Containerip="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' pulsecontainer)"

加载已通过容器IP验证的pulseaudio TCP模块:

pactl load-module module-native-protocol-tcp  port=34567 auth-ip-acl=$Containerip

请注意,在容器启动并运行后 会加载TCP模块。直到Pulseaudio服务器可用于容器应用程序为止,需要一些时间。 如果TCP连接失败,请检查iptablesufw设置。


总结这些设置的方法:https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio