关于Docker:Docker-一种访问主机USB或串行设备的方式?

Docker - a way to give access to a host USB or serial device?

上次我检查时,Docker没有任何方法允许容器访问主机串行或USB端口。 有没有办法做到这一点?


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

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

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

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

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


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

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

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


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

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

粘贴起来有点困难,但是简而言之,您需要获取字符设备的主要编号并将其发送给cgroup:

189是/ dev / ttyUSB *的主要号码,您可以通过'ls -l'获得。您的系统上的系统可能与我的系统上的系统不同:

1
2
root@server:~# echo 'c 189:* rwm' > /sys/fs/cgroup/devices/docker/$A*/devices.allow  
(A contains the docker containerID)

然后像这样启动您的容器:

1
docker run -v /dev/bus:/dev/bus:ro -v /dev/serial:/dev/serial:ro -i -t --entrypoint /bin/bash debian:amd64

如果不执行此操作,则容器启动后任何新插入或重新引导的设备都将获得新的总线ID,并且将不允许在容器中访问该设备。


我想扩展已经给出的答案,以包括对未使用/dev/bus/usb捕获的动态连接设备的支持,以及如何在将Windows主机与boot2docker VM一起使用时如何使该设备正常工作。

如果使用Windows,则需要在VirtualBox管理器中为要Docker访问的设备添加任何USB规则。为此,您可以通过运行以下命令来停止VM:

1
host:~$ docker-machine stop default

打开VirtualBox Manager,然后根据需要添加带有过滤器的USB支持。

启动boot2docker VM:

1
host:~$ docker-machine start default

由于USB设备已连接到boot2docker VM,因此需要从该计算机上运行命令。使用VM打开终端并运行docker run命令:

1
2
host:~$ docker-machine ssh
docker@default:~$ docker run -it --privileged ubuntu bash

请注意,以这种方式运行命令时,将仅捕获以前连接的USB设备。仅当您希望它与容??器启动后连接的设备一起使用时,才需要volumes标志。在这种情况下,您可以使用:

1
docker@default:~$ docker run -it --privileged -v /dev:/dev ubuntu bash

注意,在某些情况下,必须使用/dev而不是/dev/bus/usb来捕获/dev/sg2之类的设备。我只能假设对于/dev/ttyACM0/dev/ttyUSB0这样的设备也是如此。

docker run命令也将与Linux主机一起使用。


另一个选择是调整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文件夹中建立到设备的符号链接。此文件夹中的文件将始终被命名为相同的文件。

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


对于想要快速使用docker内部工作的外部USB设备(HDD,闪存驱动器)并且不使用特权模式的用户,请添加以下答案:

在主机上找到设备的devpath:

1
sudo fdisk -l

您可以从列表中很容易地通过容量识别驱动器。复制此路径(对于以下示例,该路径为/dev/sda2)。

1
2
3
4
Disque /dev/sda2: 554,5 Go, 57151488octets, 111624secteurs
Unités: secteur de 1 × 512 = 512octets
Taille de secteur (logique / physique): 512octets / 512octets
taille d'E/S (minimale / optimale): 512octets / 512octets

挂载此devpath(最好是/media):

1
sudo mount <drive path> /media/<mount folder name>

然后,您可以将其用作docker run的参数,例如:

1
docker run -it -v /media/<mount folder name>:/media/<mount folder name>

或在docker下根据卷撰写:

1
2
3
4
services:
  whatevermyserviceis:
    volumes:
      - /media/<mount folder name>:/media/<mount folder name>

现在,当您运行并输入容器时,您应该可以通过/media/访问容器内的驱动器

免责声明:

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

  • 对于我们而言,很难将特定的USB设备绑定到同样特定的Docker容器。如您所见,推荐的实现方法是:

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

    它将所有设备绑定到此容器。不安全每个容器都被允许操作所有这些容器。

    另一种方法是通过devpath绑定设备。它可能看起来像:

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

    --device(更好,没有privileged):

    1
    docker run -t -i --device /dev/bus/usb/001/002 ubuntu bash

    安全得多。但是实际上很难知道特定设备的devpath是什么。

    我已经写了这个仓库来解决这个问题。

    https://github.com/williamfzc/usb2container

    部署此服务器后,您可以通过HTTP请求轻松获得所有已连接设备的信息:

    1
    curl 127.0.0.1:9410/api/device

    并得到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    {
       "/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

    1
    docker run -t -i --device /dev/bus/usb/001/120 ubuntu bash

    也许会有所帮助。


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

    1
    docker run -ti --privileged ubuntu bash

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