Docker - способ дать доступ к хосту USB или последовательному устройству?

Ответы:

194

Есть несколько вариантов. Вы можете использовать --deviceфлаг, который можно использовать для доступа к USB-устройствам без --privilegedрежима:

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

В качестве альтернативы, если ваше USB-устройство доступно с работающими драйверами и т. Д. На хосте /dev/bus/usb, вы можете смонтировать его в контейнере, используя привилегированный режим и параметр томов . Например:

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

Обратите внимание , что , как следует из названия, --privilegedявляется небезопасным и должны быть обработаны с осторожностью.

Бен Уэйли
источник
4
Не нужно -v - привилегированный уже означает доступ ко всем устройствам
Art
12
Есть ли такой механизм для клиента Windows?
Паскаль
Используя это решение, я не вижу устройств из контейнера докера ... Вот подробности stackoverflow.com/questions/37213812 моей проблемы. Ценю любую помощь! Спасибо.
kashesandr
1
По-прежнему не работает, если USB-устройство подключено после того, как Docker уже запущен.
Франклин Даттейн
Я имею в виду, он не отображает устройство в / tty / USBX, несмотря на то, что lsusb может его отобразить.
Франклин Даттейн
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 связано с хостом, особенно при использовании терминала быстрого запуска Docker (VirtualBox Host) для Windows или Mac?
DanCat
1
Это хорошо работает, если имя вашего устройства никогда не меняется. Но если вы используете что-то динамическое, которое использует устройства в / dev / bus / usb, то это не будет работать, потому что имя устройства меняется, когда вы подключаете и отключаете его. Вместо этого вам понадобится решение выше -v (объемы).
Брэд Гриссом
1
@DanCat Правила udev могут гарантировать, что ваше устройство монтируется по статическому пути
C. Рид
1
Почему кому-то интересно иметь доступ только к одному USB-устройству? USB-устройства предназначены для подключения и отключения, и это необходимо сделать во время выполнения приложений. USB - это не SATA или что-то, вы не можете ожидать, что что-то всегда будет там ... И я не думаю, что люди просто запускают приложения через докер для одиночных запусков и выходят из них, как только USB-устройства отключены, верно? Я бы вообразил больше как приложения с сервисным типом, а не одноразовые банки ... Но спасибо, действительно, это могло бы помочь некоторым, для которых этот очень ограниченный сценарий подойдет
Arturas M
17

--deviceработает до тех пор, пока ваше USB-устройство не будет отключено / повторно подключено, а затем перестает работать. Вы должны использовать cgroup devices.allow обойти это.
Вы можете просто использовать, -v /dev:/devно это небезопасно, поскольку оно сопоставляет все устройства вашего хоста с контейнером, включая устройства с необработанным диском и так далее. По сути, это позволяет контейнеру получить права root на хосте, что обычно не то, что вам нужно.
Использование подхода cgroups лучше в этом отношении и работает на устройствах, которые добавляются после контейнера при запуске.

Подробности здесь: Доступ к USB-устройствам в Docker без использования --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

без этого любое новое подключенное или перезагружаемое устройство после запуска контейнера получит новый идентификатор шины и не получит доступ к контейнеру.

Марк Мерлин
источник
7
Тем, кто это сделал, помогите и скажите, что бы вы хотели улучшить. Я написал эту страницу, чтобы помочь другим, кто столкнулся с проблемой, которую мы сделали. Я буду немного честен, если скажу, что меня отвлекают от попыток поделиться информацией и помочь людям в работе со стеком: - /
Марк Мерлин
Если вы прочитаете мой ответ, вы увидите, что добавление тома -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
Крейг Юнкинс,
1
Есть новая функция Docker, которая делает это немного проще: «--device-cgroup-rule» ( docs.docker.com/engine/reference/commandline/create/… )
tianon
14

Я хотел бы расширить уже предоставленные ответы, чтобы включить поддержку динамически подключенных устройств, которые не были захвачены, /dev/bus/usbи как заставить это работать при использовании хоста Windows вместе с виртуальной машиной boot2docker.

Если вы работаете с Windows, вам нужно добавить любые правила USB для устройств, к которым вы хотите, чтобы Docker имел доступ в диспетчере VirtualBox. Для этого вы можете остановить виртуальную машину, выполнив:

host:~$ docker-machine stop default

Откройте VirtualBox Manager и добавьте поддержку USB с фильтрами по мере необходимости.

Запустите boot2docker VM:

host:~$ docker-machine start default

Поскольку устройства USB подключены к виртуальной машине boot2docker, команды необходимо запускать с этого компьютера. Откройте терминал с виртуальной машиной и выполните команду docker run:

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

Обратите внимание, что когда команда запускается следующим образом, захватываются только ранее подключенные USB-устройства. Флаг томов требуется только в том случае, если вы хотите, чтобы он работал с устройствами, подключенными после запуска контейнера. В этом случае вы можете использовать:

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

Обратите внимание, я должен был использовать /devвместо /dev/bus/usbв некоторых случаях для захвата устройства, как /dev/sg2. Я могу только предположить, что то же самое будет верно для таких устройств, как /dev/ttyACM0или /dev/ttyUSB0.

Команды docker run также будут работать с хостом Linux.

rrpilot
источник
Хороший момент в монтировании / dev: / dev. Это дает больше гибкости с точки зрения захвата других устройств, а также помогает с динамическим элементом.
Котакотакота
а также ставит под угрозу безопасность и изоляцию вашего хост-компьютера.
Exadra37
@ Exadra37 Это так ... и если это имеет значение в вашем приложении, вы не должны использовать это. Тем не менее, важно отметить, что есть некоторые приложения, которые вас не интересуют и не используют docker для своей изоляции. В моем конкретном случае вы можете запустить упакованное приложение Linux в Windows.
пилот
3

Другой вариант - настроить udev, который управляет монтированием устройств и какими привилегиями. Полезно, чтобы разрешить доступ без полномочий root к последовательным устройствам. Если у вас есть постоянно подключенные устройства, этот --deviceвариант - лучший способ. Если у вас есть эфемерные устройства, вот что я использовал:

1. Установите правило Udev

По умолчанию последовательные устройства монтируются так, что только корневые пользователи могут получить доступ к устройству. Нам нужно добавить правило 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:

-v /dev:/dev

Если ваше устройство постоянно подключено, то с точки зрения безопасности, вероятно, лучше использовать параметр --device или более конкретное монтирование тома.

3. Запустите контейнер в привилегированном режиме.

Если вы не использовали опцию --device и монтировали его во всей папке / dev, вам потребуется запустить контейнер в привилегированном режиме (я собираюсь проверить материал cgroup, упомянутый выше, чтобы увидеть, можно ли его удалить ). Вы можете сделать это, добавив в команду Docker следующую команду:

--privileged

4. Доступ к устройству из папки / dev / serial / by-id

Если ваше устройство может быть подключено и отключено, Linux не гарантирует, что оно всегда будет установлено в одном и том же месте ttyUSBxxx (особенно если у вас несколько устройств). К счастью, Linux автоматически создаст символическую ссылку на устройство в папке / dev / serial / by-id. Файл в этой папке всегда будет называться одинаково.

Это краткое изложение, у меня есть статья в блоге , в которой более подробно.

BadCanyon
источник
2

Нам сложно привязать определенное USB-устройство к док-контейнеру, который также специфичен. Как видите, рекомендуемый способ достижения:

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

Может быть, это поможет.

williamfzc
источник
0

Для последних версий Docker этого достаточно:

docker run -ti --privileged ubuntu bash

Это даст доступ ко всем системным ресурсам (например, в / dev)

Томас Гримо
источник
2
Привилегированный - ужасный вариант использования для безопасности, хотя да, он работает.
Марк Мерлин
2
Если это используется для программирования таких вещей, как Arduino, то это решение хорошо
Хосе Кабрера
0

В дополнение к ответам выше, для тех, кто хочет быстро использовать внешнее USB-устройство (жесткий диск, флэш-диск), работающее в докере и не использующее привилегированный режим:

Найдите 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>

или в докере составь под тома:

services:
  whatevermyserviceis:
    volumes:
      - /media/<mount folder name>:/media/<mount folder name>

И теперь, когда вы запустите и войдете в свой контейнер, вы сможете получить доступ к диску внутри контейнера в /media/<mount folder name>

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ:

  1. Это, вероятно, не будет работать для последовательных устройств, таких как веб-камеры и т. Д. Я проверял это только для USB-накопителей.
  2. Если вам необходимо регулярно подключать и отключать устройства, этот метод будет раздражать, а также не будет работать, если вы не сбросите путь монтирования и не перезапустите контейнер.
  3. Я использовал докер 17.06 +, как предписано в документации
toing_toing
источник
0

Если вы хотите получить динамический доступ к USB-устройствам, которые могут быть подключены, когда док-контейнер уже запущен, например, получить доступ к только что подключенной USB-камере по адресу / dev / video0, вы можете добавить правило cgroup при запуске контейнера. Эта опция не требует контейнера --privileged и разрешает доступ только к определенным типам оборудования.

Шаг 1

Проверьте основной номер устройства типа устройства, которое вы хотите добавить. Вы можете посмотреть это в документации ядра Linux . Или вы можете проверить это для вашего устройства. Например, чтобы проверить основной номер устройства для веб-камеры, подключенной к / dev / video0, вы можете сделать a 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 с вашим док-контейнером с помощью -v /dev:/dev

Заворачивать

Итак, чтобы добавить все USB-камеры и устройства serial2usb в ваш докер-контейнер, выполните:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
Wout_bb
источник