Docker - sposób na udostępnienie hosta urządzenia USB lub szeregowego?

Odpowiedzi:

194

Jest kilka opcji. Możesz użyć --deviceflagi, która może użyć, aby uzyskać dostęp do urządzeń USB bez --privilegedtrybu:

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

Alternatywnie, zakładając, że twoje urządzenie USB jest dostępne z działającymi sterownikami itp. Na hoście w /dev/bus/usb, możesz to zamontować w kontenerze przy użyciu trybu uprzywilejowanego i opcji woluminów . Na przykład:

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

Zauważ, że jak sama nazwa wskazuje, --privilegedjest niepewny i należy obchodzić się z nim ostrożnie.

Ben Whaley
źródło
4
Nie trzeba -v - uprzywilejowany oznacza już dostęp do wszystkich urządzeń
Art
12
Czy istnieje taki mechanizm dla klienta dokowanego systemu Windows?
Pascal
Korzystając z tego rozwiązania, nie widzę urządzeń z kontenera docker ... Oto szczegóły stackoverflow.com/questions/37213812 mojego problemu. Doceń każdą pomoc! Dzięki.
kashesandr
1
Nadal nie działa, jeśli urządzenie USB jest podłączone po uruchomieniu platformy Docker.
Franklin Dattein
Chodzi mi o to, że nie mapuje urządzenia pod / tty / USBX, mimo że lsusb może je wyświetlić.
Franklin Dattein
78

W obecnych wersjach Dockera możesz użyć --deviceflagi, aby osiągnąć to, co chcesz, bez konieczności udzielania dostępu do wszystkich urządzeń USB.

Na przykład, jeśli chcesz, aby był /dev/ttyUSB0dostępny tylko w kontenerze Docker, możesz zrobić coś takiego:

docker run -t -i --device=/dev/ttyUSB0 ubuntu bash
znak
źródło
3
pamiętaj tylko, że urządzenie nie może być w tej chwili linkiem symbolicznym. github.com/docker/docker/issues/13840
wligtenberg
6
używając --deviceflagi, jak określić, które /dev/<device>z urządzeń z systemem Android jest skojarzone z hostem, zwłaszcza w przypadku korzystania z terminala Docker Quickstart (Host VirtualBox) dla systemu Windows lub Mac?
DanCat
1
Działa to dobrze, jeśli nazwa urządzenia nigdy się nie zmienia. Ale jeśli używasz czegoś dynamicznego, który używa urządzeń w / dev / bus / usb, to nie zadziała, ponieważ nazwa urządzenia zmienia się po podłączeniu i odłączeniu. Zamiast tego będziesz potrzebować powyższego rozwiązania -v (woluminy).
Brad Grissom,
1
Reguły @DanCat udev mogą zapewnić, że urządzenie zostanie zamontowane na statycznej ścieżce
C. Reed,
1
Dlaczego ktoś miałby być zainteresowany dostępem tylko do jednego urządzenia USB? Urządzenia USB mają być podłączone i odłączone, co należy zrobić w czasie działania aplikacji. USB to nie SATA czy coś w tym rodzaju, nie można oczekiwać, że coś tam zawsze będzie ... I nie sądzę, żeby ludzie po prostu uruchamiali aplikacje przez docker dla pojedynczych uruchomień i zamykali je, gdy tylko urządzenia USB zostaną odłączone, prawda? Wyobrażałbym sobie bardziej aplikacje typu usługi, a nie słoiki do pojedynczego uruchomienia ... Ale dzięki, rzeczywiście, może to pomóc niektórym, dla których ten bardzo ograniczony scenariusz byłby odpowiedni
Arturas M
17

--devicedziała do momentu odłączenia / ponownego podłączenia urządzenia USB, a następnie przestaje działać. Musisz użyć urządzeń cgroup .allow ominąć to.
Możesz po prostu użyć, -v /dev:/devale jest to niebezpieczne, ponieważ mapuje wszystkie urządzenia z twojego hosta do kontenera, w tym surowe urządzenia dyskowe i tak dalej. Zasadniczo umożliwia to kontenerowi uzyskanie roota na hoście, co zwykle nie jest tym, czego chcesz.
Korzystanie z podejścia cgroups jest lepsze pod tym względem i działa na urządzeniach, które są dodawane po uruchomieniu kontenera.

Zobacz szczegóły tutaj: Dostęp do urządzeń USB w Dockerze bez użycia --privileged

Trochę trudno to wkleić, ale w skrócie, musisz zdobyć główny numer urządzenia postaci i wysłać go do cgroup:

189 to główna liczba / dev / ttyUSB *, którą można uzyskać za pomocą 'ls -l'. W Twoim systemie może wyglądać inaczej niż w moim:

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

Następnie uruchom kontener w ten sposób:

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

bez tego każde nowo podłączone lub ponownie uruchomione urządzenie po uruchomieniu kontenera otrzyma nowy identyfikator magistrali i nie będzie miało dostępu do kontenera.

Marc Merlin
źródło
7
Ludziom, którzy to zrobili, pomóżcie i powiedzcie, co chcielibyście ulepszyć. Napisałem tę stronę, aby pomóc innym, którzy napotkali problem, który zrobiliśmy. Będę trochę szczery, mówiąc, że nie mogę się dzielić z innymi i pomagać ludziom w stackoverflow: - /
Marc Merlin,
Jeśli przeczytasz moją odpowiedź, zobaczysz, że dodanie woluminu „-v / dev: / dev” da dostęp do dynamicznie podłączanych urządzeń.
rrpilot,
5
rrpilot: -v / dev: / dev daje ci wszystko / dev, w tym / dev / sda i inne rzeczy, których naprawdę nie chcesz ujawniać użytkownikowi root w kontenerze. Innymi słowy, Twoje rozwiązanie działa, ale jest niebezpieczne. Mój rozwiązuje ten problem. Zmienię odpowiedź, aby to zaznaczyć.
Marc Merlin
1
Odpowiedź można poprawić, pokazując, jak uzyskać numer główny i wyjaśniając, że 189należy go zastąpić. Opis tego, co należy wysłać, devices.allowmożna znaleźć tutaj: kernel.org/doc/Documentation/cgroup-v1/devices.txt
Craig Younkins
1
Jest nowa funkcja Dockera, która nieco upraszcza to: „--device-cgroup-rule” ( docs.docker.com/engine/reference/commandline/create/… )
tianon
14

Chciałem rozszerzyć odpowiedzi już udzielone, aby uwzględnić obsługę dynamicznie podłączonych urządzeń, które nie są przechwytywane, /dev/bus/usbi jak to działa, gdy używam hosta Windows wraz z maszyną wirtualną boot2docker.

Jeśli pracujesz z systemem Windows, musisz dodać reguły USB dla urządzeń, do których chcesz mieć dostęp Docker w menedżerze VirtualBox. Aby to zrobić, możesz zatrzymać maszynę wirtualną, uruchamiając:

host:~$ docker-machine stop default

Otwórz VirtualBox Manager i dodaj obsługę USB z filtrami zgodnie z wymaganiami.

Uruchom maszynę wirtualną boot2docker:

host:~$ docker-machine start default

Ponieważ urządzenia USB są podłączone do maszyny wirtualnej boot2docker, polecenia muszą być uruchamiane z tej maszyny. Otwórz terminal z maszyną wirtualną i uruchom polecenie docker run:

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

Uwaga, gdy polecenie zostanie uruchomione w ten sposób, przechwycone zostaną tylko wcześniej podłączone urządzenia USB. Flaga woluminów jest wymagana tylko wtedy, gdy chcesz, aby działała z urządzeniami podłączonymi po uruchomieniu kontenera. W takim przypadku możesz użyć:

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

Uwaga, w niektórych przypadkach musiałem użyć /devzamiast tego, /dev/bus/usbaby przechwycić urządzenie takie jak /dev/sg2. Mogę tylko założyć, że to samo dotyczy urządzeń takich jak /dev/ttyACM0lub /dev/ttyUSB0.

Polecenia uruchamiania dockera będą działać również z hostem Linux.

rrpilot
źródło
Zamiast tego warto zamontować / dev: / dev. Daje to większą elastyczność w zakresie przechwytywania innych urządzeń, a także pomaga w przypadku elementu dynamicznego.
kotakotakota
a także naraża bezpieczeństwo i izolację komputera głównego.
Exadra37
@ Exadra37 Tak ... i jeśli ma to znaczenie w twojej aplikacji, nie powinieneś tego używać. Należy jednak pamiętać, że istnieją aplikacje, w których nie obchodzi Cię to i nie używasz dockera do jego izolacji. W moim konkretnym przypadku możesz uruchomić spakowaną aplikację Linux w systemie Windows.
rrpilot
3

Inną opcją jest dostosowanie udev, który kontroluje sposób montowania urządzeń i jakie uprawnienia. Przydatne, aby umożliwić innym użytkownikom dostęp do urządzeń szeregowych. Jeśli masz urządzenia podłączone na stałe, ta --deviceopcja jest najlepszym rozwiązaniem. Jeśli masz urządzenia efemeryczne, oto czego używam:

1. Ustaw regułę udev

Domyślnie urządzenia szeregowe są montowane tak, aby tylko użytkownicy root mieli dostęp do urządzenia. Musimy dodać regułę udev, aby były czytelne dla użytkowników innych niż root.

Utwórz plik o nazwie /etc/udev/rules.d/99-serial.rules. Dodaj następujący wiersz do tego pliku:

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

MODE = "0666" da wszystkim użytkownikom uprawnienia do odczytu / zapisu (ale nie wykonywania) na urządzeniach ttyUSB. Jest to najbardziej dopuszczalna opcja i możesz chcieć ją jeszcze bardziej ograniczyć w zależności od wymagań bezpieczeństwa. Możesz przeczytać o udev, aby dowiedzieć się więcej o kontrolowaniu tego, co się dzieje, gdy urządzenie jest podłączone do bramy Linuksa.

2. Zamontuj w folderze / dev z hosta do kontenera

Urządzenia szeregowe są często efemeryczne (można je podłączać i odłączać w dowolnym momencie). Z tego powodu nie możemy zamontować w urządzeniu bezpośrednim ani nawet w folderze / dev / serial, ponieważ mogą one zniknąć po odłączeniu urządzeń. Nawet jeśli podłączysz je ponownie, a urządzenie pojawi się ponownie, technicznie rzecz biorąc, jest to inny plik niż ten, który został zamontowany, więc Docker go nie zobaczy. Z tego powodu montujemy cały folder / dev z hosta do kontenera. Możesz to zrobić, dodając następujące polecenie głośności do polecenia uruchamiania platformy Docker:

-v /dev:/dev

Jeśli urządzenie jest podłączone na stałe, użycie opcji --device lub bardziej szczegółowego podłączenia woluminu jest prawdopodobnie lepszą opcją z punktu widzenia bezpieczeństwa.

3. Uruchom kontener w trybie uprzywilejowanym

Jeśli nie użyłeś opcji --device i zamontowano w całym folderze / dev, będziesz musiał uruchomić kontener w trybie uprzywilejowanym (mam zamiar sprawdzić wspomniane powyżej rzeczy cgroup aby sprawdzić czy da się to usunąć ). Możesz to zrobić, dodając następujące polecenie do polecenia uruchamiania platformy Docker:

--privileged

4. Uzyskaj dostęp do urządzenia z folderu / dev / serial / by-id

Jeśli twoje urządzenie można podłączyć i odłączyć, Linux nie gwarantuje, że będzie zawsze zamontowane w tej samej lokalizacji ttyUSBxxx (zwłaszcza jeśli masz wiele urządzeń). Na szczęście Linux automatycznie utworzy dowiązanie symboliczne do urządzenia w folderze / dev / serial / by-id. Plik w tym folderze zawsze będzie miał taką samą nazwę.

To jest krótkie podsumowanie, mam artykuł na blogu, który zawiera więcej szczegółów.

BadCanyon
źródło
2

Trudno nam powiązać określone urządzenie USB z kontenerem docker, który również jest specyficzny. Jak widać, zalecanym sposobem osiągnięcia tego celu jest:

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

Spowoduje to powiązanie wszystkich urządzeń z tym kontenerem. To niebezpieczne. Do obsługi wszystkich przyznano każdy kontener.

Innym sposobem jest wiązanie urządzeń przez devpath. Może to wyglądać tak:

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

lub --device(lepiej nie privileged):

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

Bezpieczniejsze. Ale tak naprawdę trudno jest wiedzieć, jaka jest ścieżka devpath konkretnego urządzenia.

Napisałem to repozytorium, aby rozwiązać ten problem.

https://github.com/williamfzc/usb2container

Po wdrożeniu tego serwera możesz łatwo uzyskać informacje o wszystkich podłączonych urządzeniach za pośrednictwem żądania HTTP:

curl 127.0.0.1:9410/api/device

i dostać:

{
    "/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"
    },
    ...
}

i przywiąż je do swoich pojemników. Na przykład możesz zobaczyć DEVNAME tego urządzenia /dev/bus/usb/001/120:

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

Może to pomoże.

williamfzc
źródło
0

Przy najnowszych wersjach dockera to wystarczy:

docker run -ti --privileged ubuntu bash

Zapewni dostęp do wszystkich zasobów systemowych (na przykład w / dev)

Thomas Grimault
źródło
2
uprzywilejowany to straszna opcja do wykorzystania ze względów bezpieczeństwa, mimo że tak, działa.
Marc Merlin
2
Jeśli jest używany do programowania rzeczy takich jak rzeczy związane z Arduino, to rozwiązanie jest dobre
Jose Cabrera Zuniga
0

Dodając do powyższych odpowiedzi, dla tych, którzy chcą w szybki sposób korzystać z zewnętrznego urządzenia USB (HDD, pendrive) pracującego wewnątrz dockera, a nie korzystających z trybu uprzywilejowanego:

Znajdź devpath do swojego urządzenia na hoście:

sudo fdisk -l

Możesz łatwo rozpoznać swój dysk po jego pojemności na liście. Skopiuj tę ścieżkę (w poniższym przykładzie jest to /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

Zamontuj tę devpath (najlepiej /media):

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

Możesz następnie użyć tego jako parametru do polubienia docker run:

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

lub w docker compose under volume:

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

A teraz, gdy uruchomisz i wejdziesz do kontenera, powinieneś mieć dostęp do dysku wewnątrz kontenera pod adresem /media/<mount folder name>

ZRZECZENIE SIĘ:

  1. To prawdopodobnie nie zadziała w przypadku urządzeń szeregowych, takich jak kamery internetowe itp. Przetestowałem to tylko dla dysków USB.
  2. Jeśli musisz regularnie podłączać i odłączać urządzenia, ta metoda byłaby denerwująca, a także nie działałaby, chyba że zresetujesz ścieżkę montowania i zrestartujesz kontener.
  3. Użyłem dockera 17.06 + zgodnie z zaleceniami w dokumentacji
toing_toing
źródło
0

Jeśli chcesz dynamicznie uzyskiwać dostęp do urządzeń USB, które można podłączyć, gdy kontener docker jest już uruchomiony, na przykład uzyskać dostęp do właśnie podłączonej kamery internetowej USB pod adresem / dev / video0, możesz dodać regułę cgroup podczas uruchamiania kontenera. Ta opcja nie wymaga --przywilejowanego kontenera i zezwala tylko na dostęp do określonych typów sprzętu.

Krok 1

Sprawdź numer główny urządzenia typu urządzenia, które chcesz dodać. Możesz to sprawdzić w dokumentacji jądra Linuksa . Lub możesz to sprawdzić dla swojego urządzenia. Na przykład, aby sprawdzić główny numer urządzenia dla kamery internetowej podłączonej do / dev / video0, możesz wykonać ls -la /dev/video0. Powoduje to coś takiego:

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

Gdzie pierwsza liczba (81) to główny numer urządzenia. Niektóre popularne numery główne urządzeń:

  • 81: kamery internetowe USB
  • 188: konwertery USB na szeregowe

Krok 2

Dodaj reguły po uruchomieniu kontenera Dockera:

  • Dodaj --device-cgroup-rule='c major_number:* rmw'regułę dla każdego typu urządzenia, do którego chcesz uzyskać dostęp
  • Dodaj dostęp do informacji udev, aby kontenery Docker mogły uzyskać więcej informacji o urządzeniach USB z -v /run/udev:/run/udev:ro
  • Zamapuj wolumin / dev na swój kontener Docker z -v /dev:/dev

Zakończyć

Aby więc dodać wszystkie kamery internetowe USB i urządzenia serial2usb do kontenera Docker, wykonaj:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
Wout_bb
źródło