答案 0 :(得分:149)
有几种选择。首先,正如下面的@Mark所述,Docker版本1.2。0(2014/08年度发布)添加了--device
标志,用于在没有--privileged
模式的情况下访问USB设备:
docker run -t -i --device=/dev/ttyUSB0 ubuntu bash
或者,假设您的USB设备可用于/dev/bus/usb
主机上的驱动程序等,您可以使用privileged mode和volumes option将其安装在容器中。例如:
docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash
答案 1 :(得分:72)
使用当前版本的Docker,您可以使用--device
标志来实现您的目标,而无需访问所有USB设备。
例如,如果您只想在Docker容器中访问/dev/ttyUSB0
,则可以执行以下操作:
docker run -t -i --device=/dev/ttyUSB0 ubuntu bash
答案 2 :(得分:9)
我想扩展已经给出的答案,以包括对未用/dev/bus/usb
捕获的动态连接设备的支持,以及如何在将Windows主机与boot2docker VM一起使用时如何使它正常工作。
如果使用Windows,则需要在VirtualBox管理器中为希望Docker访问的设备添加任何USB规则。为此,您可以通过运行以下命令停止VM:
host:~$ docker-machine stop default
打开VirtualBox Manager并根据需要添加带有过滤器的USB支持。
启动boot2docker VM:
host:~$ docker-machine start default
由于USB设备已连接到boot2docker VM,因此需要从该计算机上运行命令。使用VM打开终端并运行docker run命令:
host:~$ docker-machine ssh
docker@default:~$ docker run -it --privileged ubuntu bash
注意,当像这样运行命令时,将仅捕获以前连接的USB设备。仅当您希望它与容器启动后连接的设备一起使用时,才需要volumes标志。在这种情况下,您可以使用:
docker@default:~$ docker run -it --privileged -v /dev:/dev ubuntu bash
请注意,在某些情况下,我必须使用/dev
而不是/dev/bus/usb
来捕获/dev/sg2
之类的设备。我只能假设对于/dev/ttyACM0
或/dev/ttyUSB0
之类的设备也是如此。
docker run命令也将与Linux主机一起使用。
答案 3 :(得分:7)
如果您想动态访问可在Docker容器运行时插入的USB设备,例如访问/ dev / video0上刚刚连接的USB网络摄像头,则可以在启动容器时添加cgroup规则。此选项不需要--privileged容器,仅允许访问特定类型的硬件。
检查要添加的设备类型的设备主号码。您可以在linux kernel documentation中查找它。或者,您可以为您的设备检查它。例如,要检查连接到/ dev / video0的网络摄像机的设备主号码,您可以执行ls -la /dev/video0
。结果是:
crw-rw----+ 1 root video 81, 0 Jul 6 10:22 /dev/video0
第一个数字(81)是设备的主要数字。一些常见的设备主号码:
在启动Docker容器时添加规则:
--device-cgroup-rule='c major_number:* rmw'
规则-v /run/udev:/run/udev:ro
在您的USB设备上获取更多信息-v /dev:/dev
将/ dev卷映射到您的docker容器中因此,要将所有USB网络摄像头和serial2usb设备添加到Docker容器,请执行以下操作:
docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
答案 4 :(得分:6)
按照说明一行行,所有步骤都有说明
想法是正确配置cgroup规则。首先,让我们找到您的 USB 设备的 cgroup 属性。运行以下命令:
$ ls -l /dev/ | grep ttyUSB
crw-rw-rw- 1 root dialout 188, 0 Mar 1 18:23 ttyUSB0 #Example output
根据输出,您可以看到在我的情况下,tty 设备的主要组是 188
,因此我将继续进行。
您可以运行 docker 镜像 allowing access to range of devices with specific major number,docker 会在您的主机中为您添加所需的规则(这将在分离模式下运行 docker,我们稍后会附加到它):
docker run --device-cgroup-rule='c 188:* rmw' -itd --name my_container ubuntu
现在的想法是添加一个脚本,每次插入或拔出 USB 设备时都会运行该脚本。关于自定义规则 here 和 here on passing arguments 的一些解释。在 ubuntu 上,您应该以超级用户 (sudo) 身份创建文件 /etc/udev/rules.d/99-docker-tty.rules
:
ACTION=="add", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'added' '%E{DEVNAME}' '%M' '%m'"
ACTION=="remove", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'removed' '%E{DEVNAME}' '%M' '%m'"
此文件为您的规则添加了新条目,基本上是说:每次插入 tty 设备 - add
或拔出 - remove
运行提供的脚本并传递一些参数。如果您想更具体,可以使用 udevadm info --name=<device name>
查找其他参数,您可以根据这些参数过滤设备。您可以按照建议的 here 测试规则。要应用这些规则:
root@~$ udevadm control --reload
现在我们还需要以超级用户 (sudo) 身份在 /usr/local/bin/docker_tty.sh
中创建以下脚本。您可以看到它被设置为在我们之前创建的 udev 规则中运行。
#!/usr/bin/env bash
echo "Usb event: $1 $2 $3 $4" >> /tmp/docker_tty.log
if [ ! -z "$(docker ps -qf name=env_dev)" ]
then
if [ "$1" == "added" ]
then
docker exec -u 0 env_dev mknod $2 c $3 $4
docker exec -u 0 env_dev chmod -R 777 $2
echo "Adding $2 to docker" >> /tmp/docker_tty.log
else
docker exec -u 0 env_dev rm $2
echo "Removing $2 from docker" >> /tmp/docker_tty.log
fi
fi
此脚本将在您正在运行的 docker 容器中创建 tty 设备,或者根据设备是否已插入或已拔出将其删除(类似于 Ubuntu 机器发生的情况 - 每次插入设备时,您都可以看到它在 /dev/
目录下)。提示:检查文件 /tmp/docker_tty.log
以获得主机上的一些调试输出,此外,按照建议的 here 调试 bash 脚本。
不要忘记使脚本可执行:
root@~$ chmod +x /usr/local/bin/docker_tty.sh
现在连接到 docker 并查看插入和拔出设备时设备是否出现在 /dev/
目录中:
docker exec -it my_container bash
答案 5 :(得分:5)
--device
一直工作到USB设备拔出/重新插入,然后停止工作为止。您必须使用 cgroup设备。允许绕开它。
您可以只使用-v /dev:/dev
,但这是不安全的,因为它会将主机中的所有设备都映射到容器中,包括原始磁盘设备等。基本上,这可以使容器在主机上获得根,而这通常不是您想要的。
在这方面,使用cgroups方法更好,并且可以在容器启动后添加的设备上使用。
在此处查看详细信息:Accessing USB Devices In Docker without using --privileged
粘贴起来有点困难,但是简而言之,您需要获取字符设备的主要编号并将其发送给cgroup:
189是/ dev / ttyUSB *的主要号码,可以通过'ls -l'获得。您的系统上可能与我的系统上不同:
root@server:~# echo 'c 189:* rwm' > /sys/fs/cgroup/devices/docker/$A*/devices.allow
(A contains the docker containerID)
然后像这样启动容器:
docker run -v /dev/bus:/dev/bus:ro -v /dev/serial:/dev/serial:ro -i -t --entrypoint /bin/bash debian:amd64
如果不执行此操作,则在容器启动后任何新插入或重新启动的设备都将获得新的总线ID,并且将不允许在该容器中访问。
答案 6 :(得分:0)
另一个选项是调整udev,它控制设备的安装方式和特权。允许非root用户访问串行设备很有用。如果您已永久连接设备,则--device
选项是最好的选择。如果您拥有临时设备,这就是我一直在使用的设备:
默认情况下,将挂载串行设备,以便只有root用户才能访问该设备。我们需要添加udev规则,以使非root用户可以读取它们。
创建一个名为/etc/udev/rules.d/99-serial.rules的文件。将以下行添加到该文件:
KERNEL=="ttyUSB[0-9]*",MODE="0666"
MODE =“ 0666”将授予所有用户对ttyUSB设备的读/写(但不执行)权限。这是最宽松的选项,您可能想根据安全要求进一步限制它。您可以阅读udev,以了解有关控制将设备插入Linux网关时发生的情况的更多信息。
串行设备通常是临时设备(可以随时插入和拔出)。因此,我们无法挂载在直接设备甚至/ dev / serial文件夹中,因为当拔下电源时,它们可能会消失。即使您将它们重新插入并重新显示设备,从技术上讲,它也是与装入的文件不同的文件,因此Docker无法看到它。因此,我们将整个/ dev文件夹从主机安装到容器。您可以通过在Docker run命令中添加以下volume命令来实现此目的:
-v /dev:/dev
如果您的设备已永久连接,则从安全角度来看,使用--device选项或更特定的卷挂载可能是更好的选择。
如果您未使用--device选项并安装在整个/ dev文件夹中,则将需要以特权模式运行容器(我将检查上述cgroup的内容,看看是否可以删除)。您可以通过在Docker运行命令中添加以下内容来实现此目的:
--privileged
如果可以插入和拔出设备,Linux不能保证将其始终安装在同一ttyUSBxxx位置(特别是如果您有多个设备)。幸运的是,Linux会自动在/ dev / serial / by-id文件夹中建立到设备的符号链接。此文件夹中的文件将始终命名为相同的文件。
这是一个简短的摘要,我有一个blog article,其中有更多详细信息。
答案 7 :(得分:0)
我们很难将特定的USB设备绑定到同样特定的Docker容器。如您所见,推荐的实现方法是:
docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash
它将所有设备绑定到此容器。不安全每个容器都被允许操作所有容器。
另一种方法是通过devpath绑定设备。可能看起来像:
docker run -t -i --privileged -v /dev/bus/usb/001/002:/dev/bus/usb/001/002 ubuntu bash
或--device
(最好不要使用privileged
)
docker run -t -i --device /dev/bus/usb/001/002 ubuntu bash
更安全。但是实际上很难知道特定设备的devpath是什么。
我已经写了这个仓库来解决这个问题。
https://github.com/williamfzc/usb2container
部署此服务器后,您可以通过HTTP请求轻松获取所有已连接设备的信息:
curl 127.0.0.1:9410/api/device
并获得:
{
"/devices/pci0000:00/0000:00:14.0/usb1/1-13": {
"ACTION": "add",
"DEVPATH": "/devices/pci0000:00/0000:00:14.0/usb1/1-13",
"DEVTYPE": "usb_device",
"DRIVER": "usb",
"ID_BUS": "usb",
"ID_FOR_SEAT": "xxxxx",
"ID_MODEL": "xxxxx",
"ID_MODEL_ID": "xxxxx",
"ID_PATH": "xxxxx",
"ID_PATH_TAG": "xxxxx",
"ID_REVISION": "xxxxx",
"ID_SERIAL": "xxxxx",
"ID_SERIAL_SHORT": "xxxxx",
"ID_USB_INTERFACES": "xxxxx",
"ID_VENDOR": "xxxxx",
"ID_VENDOR_ENC": "xxxxx",
"ID_VENDOR_FROM_DATABASE": "",
"ID_VENDOR_ID": "xxxxx",
"INTERFACE": "",
"MAJOR": "189",
"MINOR": "119",
"MODALIAS": "",
"PRODUCT": "xxxxx",
"SEQNUM": "xxxxx",
"SUBSYSTEM": "usb",
"TAGS": "",
"TYPE": "0/0/0",
"USEC_INITIALIZED": "xxxxx",
"adb_user": "",
"_empty": false,
"DEVNAME": "/dev/bus/usb/001/120",
"BUSNUM": "001",
"DEVNUM": "120",
"ID_MODEL_ENC": "xxxxx"
},
...
}
并将其绑定到您的容器。例如,您可以看到此设备的DEVNAME是/dev/bus/usb/001/120
:
docker run -t -i --device /dev/bus/usb/001/120 ubuntu bash
也许会有所帮助。
答案 8 :(得分:0)
对于想要快速使用docker内部工作的外部USB设备(HDD,闪存驱动器),并且不使用特权模式的人,
在主机上找到设备的devpath:
sudo fdisk -l
您可以从列表中很容易地通过其容量识别驱动器。复制此路径(在下面的示例中为/dev/sda2
)。
Disque /dev/sda2 : 554,5 Go, 57151488 octets, 111624 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
安装此devpath(最好是/media
):
sudo mount <drive path> /media/<mount folder name>
然后您可以将其用作docker run
的参数,例如:
docker run -it -v /media/<mount folder name>:/media/<mount folder name>
或在docker下的卷下组成:
services:
whatevermyserviceis:
volumes:
- /media/<mount folder name>:/media/<mount folder name>
现在,当您运行并输入容器时,您应该可以在/media/<mount folder name>
处访问容器内的驱动器
免责声明:
答案 9 :(得分:-1)
使用最新版本的docker,这就足够了:
docker run -ti --privileged ubuntu bash
它将允许访问所有系统资源(例如,在/ dev中)