Docker - uma maneira de dar acesso a um host USB ou dispositivo serial?


Respostas:


194

Há um par de opções. Você pode usar o --devicesinalizador usado para acessar dispositivos USB sem o --privilegedmodo:

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

Como alternativa, supondo que seu dispositivo USB esteja disponível com drivers funcionando, etc. no host /dev/bus/usb, você pode montá-lo no contêiner usando o modo privilegiado e a opção de volumes . Por exemplo:

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

Observe que, como o nome indica, --privilegedé inseguro e deve ser manuseado com cuidado.


4
Não precisa -v - privilegiada já significa acesso a todos os dispositivos
Art

12
Existe algum mecanismo como este para o cliente docker do Windows?
Pascal

Usando esta solução, não vejo dispositivos de um contêiner de docker ... Aqui estão os detalhes stackoverflow.com/questions/37213812 do meu problema. Aprecio qualquer ajuda! Obrigado.
Khsandr

1
Ainda não funcionará se o dispositivo USB estiver conectado depois que o Docker já estiver em execução.
Franklin Dattein 25/10

Quero dizer, ele não mapeia o dispositivo em / tty / USBX, apesar de o lsusb poder listá-lo.
Franklin Dattein

78

Com as versões atuais do Docker, você pode usar o --devicesinalizador para conseguir o que deseja, sem precisar conceder acesso a todos os dispositivos USB.

Por exemplo, se você deseja tornar /dev/ttyUSB0acessível apenas dentro do contêiner do Docker, pode fazer algo como:

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

3
apenas observe que o dispositivo não pode ser um link simbólico no momento. github.com/docker/docker/issues/13840
wligtenberg

6
usando o --devicesinalizador, como determino qual /dev/<device>é o dispositivo Android associado na máquina host, especialmente ao usar o Docker Quickstart Terminal (VirtualBox Host) para Windows ou Mac?
DanCat 24/11/2015

1
Isso funciona bem se o nome do seu dispositivo nunca mudar. Mas se você estiver usando algo dinâmico que usa dispositivos em / dev / bus / usb, isso não funcionará porque o nome do dispositivo muda quando você o conecta e desconecta. Você precisará da solução -v (volumes) acima.
Brad Grissom

1
As regras do @DanCat udev podem garantir que seu dispositivo seja montado em um caminho estático
C. Reed

1
Por que alguém estaria interessado em ter acesso a apenas um dispositivo usb? Os dispositivos USB devem ser conectados e desconectados, e isso precisa ser feito durante o tempo de execução dos aplicativos. USB não é SATA ou algo assim, você não pode esperar que algo sempre esteja lá ... E eu não acho que as pessoas simplesmente iniciem aplicativos via docker para execuções únicas e os encerram assim que os dispositivos USB são desconectados, certo? Eu imagino mais como aplicativos de tipo de serviço, não somente frascos correr ... Mas graças, fato que pode ajudar um pouco para que esse cenário muito limitado serviria
Arturas M

17

--devicefunciona até o seu dispositivo USB ser desconectado / reconectado e depois parar de funcionar. Você tem que usar cgroup devices.allow contornar isso.
Você pode simplesmente usar, -v /dev:/devmas isso não é seguro, pois mapeia todos os dispositivos do seu host para o contêiner, incluindo dispositivos de disco bruto e assim por diante. Basicamente, isso permite que o contêiner ganhe raiz no host, o que geralmente não é o que você deseja.
O uso da abordagem cgroups é melhor nesse aspecto e funciona em dispositivos que são adicionados após o contêiner como iniciado.

Veja detalhes aqui: Acessando dispositivos USB no Docker sem usar --privileged

É um pouco difícil de colar, mas, em poucas palavras, você precisa obter o número principal do seu dispositivo de personagem e enviá-lo ao cgroup:

189 é o número principal de / dev / ttyUSB *, que você pode obter com 'ls -l'. Pode ser diferente no seu sistema e no meu:

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

Então inicie seu contêiner assim:

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

sem fazer isso, qualquer dispositivo recém-conectado ou reiniciado após o contêiner iniciar, receberá um novo ID de barramento e não terá acesso permitido ao contêiner.


7
Para as pessoas que fizeram isso com -1, ajude e diga o que gostaria de melhorar. Escrevi esta página para ajudar outras pessoas que enfrentaram o problema que enfrentamos. Serei um pouco honesto em dizer que estou sendo impedido de tentar compartilhar novamente e ajudar as pessoas no stackoverflow também: - /
Marc Merlin

Se você ler minha resposta, verá que a adição do volume '-v / dev: / dev' dará acesso a dispositivos conectados dinamicamente.
Rrpilot

5
rrpilot: -v / dev: / dev fornece a você todo o / dev, incluindo / dev / sda e outras coisas que você realmente não deseja expor a um usuário root no contêiner. Em outras palavras, sua solução funciona, mas é insegura. A minha contorna esse problema. Vou editar minha resposta para apontar isso.
Marc Merlin

1
A resposta pode ser melhorada, mostrando como obter o número principal e esclarecendo que 189deve ser substituído. Uma descrição do que para enviar devices.allowpodem ser encontrados aqui: kernel.org/doc/Documentation/cgroup-v1/devices.txt
Craig Younkins

1
Há um novo recurso do Docker que torna isso um pouco mais simples: "--device-cgroup-rule" ( docs.docker.com/engine/reference/commandline/create/… )
tianon

14

Eu queria estender as respostas já fornecidas para incluir suporte para dispositivos conectados dinamicamente que não são capturados /dev/bus/usbe como fazê-lo funcionar ao usar um host do Windows junto com a VM do boot2docker.

Se você estiver trabalhando com o Windows, precisará adicionar regras de USB para dispositivos que você deseja que o Docker acesse no gerenciador do VirtualBox. Para fazer isso, você pode parar a VM executando:

host:~$ docker-machine stop default

Abra o VirtualBox Manager e adicione suporte USB com filtros, conforme necessário.

Inicie a VM boot2docker:

host:~$ docker-machine start default

Como os dispositivos USB estão conectados à VM boot2docker, os comandos precisam ser executados a partir dessa máquina. Abra um terminal com a VM e execute o comando docker run:

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

Observe que, quando o comando é executado dessa maneira, somente os dispositivos USB conectados anteriormente serão capturados. O sinalizador de volumes é necessário apenas se você desejar que isso funcione com dispositivos conectados após o contêiner ser iniciado. Nesse caso, você pode usar:

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

Note, eu tive que usar em /devvez de /dev/bus/usbem alguns casos para capturar um dispositivo como /dev/sg2. Só posso supor que o mesmo seria verdadeiro para dispositivos como /dev/ttyACM0ou /dev/ttyUSB0.

Os comandos de execução do docker também funcionarão com um host Linux.


Bom ponto de montagem / dev: / dev. Isso oferece mais flexibilidade em termos de captura de outros dispositivos e também ajuda no elemento dinâmico.
Kotakotakota

e também compromete a segurança e o isolamento da sua máquina host.
Exadra37 7/07

@ Exadra37 Sim ... e se isso importa no seu aplicativo, você não deve usá-lo. No entanto, é importante observar que existem alguns aplicativos em que você não se importa e não está usando o docker para seu isolamento. No meu caso específico, você pode executar um aplicativo Linux empacotado no Windows.
rrpilot 8/07

3

Outra opção é ajustar o udev, que controla como os dispositivos são montados e com quais privilégios. Útil para permitir acesso não raiz a dispositivos seriais. Se você tiver dispositivos permanentemente ligado, a --deviceopção é a melhor maneira de ir. Se você tem dispositivos efêmeros, aqui está o que eu tenho usado:

1. Defina a regra do udev

Por padrão, os dispositivos seriais são montados para que apenas usuários raiz possam acessar o dispositivo. Precisamos adicionar uma regra do udev para torná-los legíveis por usuários não raiz.

Crie um arquivo chamado /etc/udev/rules.d/99-serial.rules. Adicione a seguinte linha a esse arquivo:

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

MODE = "0666" dará a todos os usuários permissões de leitura / gravação (mas não execução) nos seus dispositivos ttyUSB. Essa é a opção mais permissiva e você pode restringir ainda mais isso, dependendo dos seus requisitos de segurança. Você pode ler no udev para saber mais sobre como controlar o que acontece quando um dispositivo é conectado a um gateway Linux.

2. Monte na pasta / dev do host para o contêiner

Os dispositivos seriais geralmente são efêmeros (podem ser conectados e desconectados a qualquer momento). Por esse motivo, não podemos montar no dispositivo direto ou na pasta / dev / serial, porque eles podem desaparecer quando as coisas são desconectadas. Mesmo se você conectá-los novamente e o dispositivo aparecer novamente, é tecnicamente um arquivo diferente do que foi montado, para que o Docker não o veja. Por esse motivo, montamos toda a pasta / dev do host para o contêiner. Você pode fazer isso adicionando o seguinte comando de volume ao comando de execução do Docker:

-v /dev:/dev

Se o seu dispositivo estiver permanentemente conectado, usar a opção --device ou uma montagem de volume mais específica provavelmente será uma opção melhor do ponto de vista da segurança.

3. Execute o contêiner no modo privilegiado

Se você não usou a opção --device e montou toda a pasta / dev, será necessário executar o contêiner no modo privilegiado (vou verificar o material do cgroup mencionado acima para ver se isso pode ser removido ) Você pode fazer isso adicionando o seguinte ao comando de execução do Docker:

--privileged

4. Acesse o dispositivo na pasta / dev / serial / by-id

Se o seu dispositivo puder ser conectado e desconectado, o Linux não garante que ele seja sempre montado no mesmo local ttyUSBxxx (especialmente se você tiver vários dispositivos). Felizmente, o Linux fará um link simbólico automaticamente para o dispositivo na pasta / dev / serial / by-id. O arquivo nesta pasta sempre terá o mesmo nome.

Este é o resumo rápido, eu tenho um artigo de blog que entra em mais detalhes.


2

É difícil vincular um dispositivo USB específico a um contêiner de docker, que também é específico. Como você pode ver, a maneira recomendada de obter é:

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

Ele vinculará todos os dispositivos a esse contêiner. Não é seguro. Todos os contêineres foram concedidos para operar todos eles.

Outra maneira é ligar dispositivos pelo devpath. Pode parecer com:

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

ou --device(melhor, não privileged):

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

Muito mais seguro. Mas, na verdade, é difícil saber qual é o caminho de desenvolvimento de um dispositivo específico.

Eu escrevi este repositório para resolver este problema.

https://github.com/williamfzc/usb2container

Após a implantação deste servidor, você pode obter facilmente todas as informações dos dispositivos conectados via solicitação HTTP:

curl 127.0.0.1:9410/api/device

e pegue:

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

e vinculá-los aos seus contêineres. Por exemplo, você pode ver que DEVNAME deste dispositivo é /dev/bus/usb/001/120:

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

Talvez isso ajude.


0

Com as versões mais recentes do docker, isso é suficiente:

docker run -ti --privileged ubuntu bash

Dará acesso a todos os recursos do sistema (em / dev, por exemplo)


2
priviledged é uma opção terrível a ser usada para segurança, mesmo que sim, funcione.
Marc Merlin #

2
Se for usado para programar coisas como Arduino coisas relacionadas esta solução é boa
José Cabrera Zuniga

0

Além das respostas acima, para aqueles que desejam uma maneira rápida de usar um dispositivo USB externo (HDD, unidade flash) trabalhando dentro da janela de encaixe e não usando o modo privilegiado:

Encontre o devpath para o seu dispositivo no host:

sudo fdisk -l

Você pode reconhecer sua unidade por sua capacidade facilmente na lista. Copie este caminho (para o seguinte exemplo /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

Monte este devpath (preferível /media):

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

Você pode usar isso como um parâmetro para docker rungostar:

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

ou na janela de encaixe compor sob volumes:

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

E agora, quando você executar e entrar no seu contêiner, poderá acessar a unidade dentro do contêiner em /media/<mount folder name>

AVISO LEGAL:

  1. Provavelmente isso não funcionará para dispositivos seriais, como webcams, etc. Só testei isso para unidades de armazenamento USB.
  2. Se você precisar reconectar e desconectar dispositivos regularmente, esse método seria irritante e também não funcionaria, a menos que você redefinisse o caminho de montagem e reiniciasse o contêiner.
  3. Usei o docker 17.06 +, conforme prescrito na documentação

0

Se você deseja acessar dinamicamente dispositivos USB que podem ser conectados enquanto o contêiner do docker já está em execução, por exemplo, acessar uma webcam usb recém-conectada em / dev / video0, você pode adicionar uma regra cgroup ao iniciar o contêiner. Esta opção não precisa de um contêiner --privileged e permite apenas o acesso a tipos específicos de hardware.

Passo 1

Verifique o número principal do dispositivo que você deseja adicionar. Você pode procurar na documentação do kernel do linux . Ou você pode verificá-lo para o seu dispositivo. Por exemplo, para verificar o número principal do dispositivo de uma webcam conectada a / dev / video0, você pode fazer a ls -la /dev/video0. Isso resulta em algo como:

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

Onde o primeiro número (81) é o número principal do dispositivo. Alguns números principais de dispositivos comuns:

  • 81: webcams usb
  • 188: conversores usb para serial

Passo 2

Adicione regras ao iniciar o contêiner do docker:

  • Adicione uma --device-cgroup-rule='c major_number:* rmw'regra para cada tipo de dispositivo ao qual você deseja acessar
  • Adicione acesso às informações do udev para que os contêineres do docker possam obter mais informações sobre seus dispositivos USB com -v /run/udev:/run/udev:ro
  • Mapeie o volume / dev para o contêiner do docker com -v /dev:/dev

Embrulhar

Para adicionar todas as webcams USB e dispositivos serial2usb ao seu contêiner de docker, faça:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.