Docker-一种访问主机USB或串行设备的方法?


Answers:


194

有两种选择。您可以使用--device可用于访问无--privileged模式USB设备的标志:

docker run -t -i --device=/dev/ttyUSB0 ubuntu bash

或者,假设您的USB设备在主机上的驱动程序可用等情况下可用/dev/bus/usb,则可以使用特权模式volumes选项将其安装在容器中。例如:

docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash

请注意,顾名思义,它--privileged不安全的 ,应谨慎处理。


4
不需要-v-特权已经意味着可以访问所有设备
Art

12
Windows docker客户端是否有类似的机制?
帕斯卡

使用此解决方案,我看不到来自Docker容器的设备...这是我的问题的详细信息stackoverflow.com/questions/37213812。感谢任何帮助!谢谢。
kashesandr

1
如果在Docker已经运行后连接了USB设备,仍然无法正常工作。
富兰克林·达特因

我的意思是,尽管lsusb能够列出设备,但它不会将设备映射到/ tty / USBX下。
富兰克林·达特因

78

在最新版本的Docker中,您可以使用 --device标志来实现所需的功能,而无需授予对所有USB设备的访问权限。

例如,如果您只想/dev/ttyUSB0在Docker容器中进行访问,则可以执行以下操作:

docker run -t -i --device=/dev/ttyUSB0 ubuntu bash

3
请注意,该设备目前不能是符号链接。github.com/docker/docker/issues/13840
wligtenberg

6
使用该--device标志,如何确定/dev/<device>主机上关联的Android设备,尤其是在Windows或Mac上使用Docker Quickstart Terminal(VirtualBox主机)时?
DanCat

1
如果您的设备名称从未更改,则此方法效果很好。但是,如果您使用的是动态的东西,它使用/ dev / bus / usb中的设备,那么它将无法正常工作,因为插入和拔出设备时,设备名称都会更改。您将需要上面的-v(卷)解决方案。
布拉德·格里索姆

1
@DanCat udev规则可以确保您的设备安装到静态路径
C. Reed

1
为什么有人会对只使用一台USB设备感兴趣?USB设备是要连接的,然后断开连接,需要在应用程序运行时完成。USB不是SATA之类的东西,您不能指望总有东西……而且我不认为人们会通过docker启动应用程序进行单次运行并在USB设备断开连接后立即退出应用程序,对吗?我会想象更像是服务类型的应用程序,而不是单次运行的jars ...但是,确实,这可能会帮助一些非常有限的情况适合的应用程序
Arturas M

17

--device直到您的USB设备拔出/重新插入,然后停止工作。您必须使用cgroup devices.allow解决它。
您可以使用-v /dev:/dev它,但这是不安全的,因为它会将主机中的所有设备映射到容器中,包括原始磁盘设备等。基本上,这使容器可以在主机上获得根,这通常不是您想要的。
在这方面,使用cgroups方法更好,并且可以在容器启动后添加的设备上使用。

在此处查看详细信息:在不使用--privileged的情况下访问Docker中的USB设备

粘贴起来有点困难,但是简而言之,您需要获取字符设备的主要编号并将其发送给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,并且将不允许在容器中进行访问。


7
对于-1版的用户,请帮助并说出您希望改进的地方。我写这个页面是为了帮助其他遇到我们问题的人。我会说些老实话,说我也无法尝试分享和帮助人们进行stackoverflow:-/
Marc Merlin

如果您阅读了我的回答,将会看到添加卷'-v / dev:/ dev'可以访问动态插入的设备。
rrpilot,

5
rrpilot:-v / dev:/ dev确实为您提供了/ dev的所有内容,包括/ dev / sda以及您确实不想向容器中的root用户公开的其他内容。换句话说,您的解决方案确实有效,但这是不安全的。我的解决了这个问题。我将编辑答案以指出这一点。
马克·默林

1
通过显示如何获取主号码并阐明189必须替换的主号码,可以使答案更好。有关发送内容的说明,请devices.allow参见:kernel.org/doc/Documentation/cgroup-v1/devices.txt
Craig Younkins,

1
“--device-cgroup的规则”(:有使这稍微简单泊坞窗的新十岁上下的功能docs.docker.com/engine/reference/commandline/create/...
tianon

14

我想扩展已经给出的答案,以包括对未捕获的动态连接的设备的支持,/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主机一起使用。


挂载/ dev:/ dev的好处。这在捕获其他设备方面提供了更大的灵活性,并且还有助于动态元素。
kotakotakota

还会损害主机的安全性和隔离性。
Exadra37

@ Exadra37它确实...如果在您的应用程序中很重要,则不应使用此功能。但是,重要的是要注意,有些应用程序不在乎它们,也没有使用Docker对其进行隔离。在我的特定情况下,您可以在Windows上运行打包的Linux应用程序。
rrpilot

3

另一个选择是调整udev,它控制设备的安装方式和特权。允许非root用户访问串行设备很有用。如果您拥有永久连接的设备,则该--device选项是最佳选择。如果您拥有临时设备,这就是我一直在使用的设备:

1.设置udev规则

默认情况下,将挂载串行设备,以便只有root用户才能访问该设备。我们需要添加udev规则,以使非root用户可以读取它们。

创建一个名为/etc/udev/rules.d/99-serial.rules的文件。将以下行添加到该文件:

KERNEL=="ttyUSB[0-9]*",MODE="0666"

MODE =“ 0666”将授予所有用户对ttyUSB设备的读/写(但不执行)权限。这是最宽松的选项,您可能想根据安全要求进一步限制它。您可以阅读udev以了解有关控制将设备插入Linux网关时发生的情况的更多信息。

2.从主机挂载到/ dev文件夹到容器

串行设备通常是临时的(可以随时插入和拔出)。因此,我们无法挂载在直接设备甚至/ dev / serial文件夹中,因为当拔出电源时,它们可能会消失。即使将它们重新插入并重新显示设备,从技术上讲,它也是与装入的文件不同的文件,因此Docker无法看到它。因此,我们将整个/ dev文件夹从主机安装到容器。您可以通过在Docker run命令中添加以下volume命令来执行此操作:

-v /dev:/dev

如果您的设备是永久连接的,那么从安全角度来看,使用--device选项或更特定的卷安装可能是更好的选择。

3.以特权模式运行容器

如果您未使用--device选项并安装在整个/ dev文件夹中,则将需要以特权模式运行容器(我将检查上述cgroup的内容,以查看是否可以删除此容器) )。您可以通过在Docker run命令中添加以下内容来做到这一点:

--privileged

4.从/ dev / serial / by-id文件夹访问设备

如果可以插入和拔出设备,Linux不能保证将其始终安装在同一ttyUSBxxx位置(特别是如果您有多个设备)。幸运的是,Linux会自动在/ dev / serial / by-id文件夹中建立到设备的符号链接。此文件夹中的文件将始终被命名为相同的文件。

这是一个快速的总结,我有一篇博客文章,其中有更多详细信息。


2

对于我们来说,很难将特定的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

也许会有所帮助。


0

使用最新版本的docker,这就足够了:

docker run -ti --privileged ubuntu bash

它将允许访问所有系统资源(例如,在/ dev中)


2
特权是用于安全性的一个糟糕选择,即使可以,它也可以工作。
马克·默林

2
如果用于对Arduino相关的东西进行编程,则此解决方案很好
Jose Cabrera Zuniga

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>

免责声明:

  1. 这可能不适用于网络摄像头等串行设备。我仅针对USB存储驱动器进行了测试。
  2. 如果需要定期重新连接和断开设备,则此方法很烦人,并且除非重置安装路径并重新启动容器,否则该方法将不起作用。
  3. 我按照文档中的规定使用了docker 17.06 +

0

如果您想动态访问可在Docker容器运行时插入的USB设备,例如访问/ dev / video0上刚刚连接的USB网络摄像头,则可以在启动容器时添加cgroup规则。此选项不需要--privileged容器,仅允许访问特定类型的硬件。

第1步

检查您要添加的设备类型的设备主号码。您可以在linux内核文档中进行查找。或者,您可以为您的设备检查它。例如,要检查连接到/ dev / video0的网络摄像机的设备主号码,可以执行ls -la /dev/video0。结果是:

crw-rw----+ 1 root video 81, 0 Jul  6 10:22 /dev/video0

其中第一个数字(81)是设备的主要数字。一些常见的设备主号码:

  • 81:USB网络摄像头
  • 188:USB至串行转换器

第2步

启动Docker容器时添加规则:

  • --device-cgroup-rule='c major_number:* rmw'为要访问的每种设备添加规则
  • 添加对udev信息的访问权限,以便Docker容器可以通过以下方式在您的USB设备上获取更多信息 -v /run/udev:/run/udev:ro
  • 使用以下命令将/ dev卷映射到您的docker容器 -v /dev:/dev

包起来

因此,要将所有USB网络摄像头和serial2usb设备添加到Docker容器,请执行以下操作:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.