Docker’da USB aygıt erişimi neden başarısız olur?
Düzenlendi · Kapsam: Linux, macOS ve Windows (WSL2) üzerinde Docker aygıt eşleme (device mapping), cgroup kuralları ve çekirdek modülü kısıtlamaları.
Gömülü sistemler, IoT, robotik veya donanım geliştirme alanında çalışan mühendisler; mikrodenetleyicileri (Arduino, STM32, ESP32) veya USB-Seri dönüştürücüleri (FTDI, CH340, CP2102) Docker konteyneri içinde programlamaya çalıştıklarında sık sık erişim hatalarıyla karşılaşırlar.
Bu hataların temelinde üç ana etken yatar:
- İşletim sistemi ve hipervizör katmanının USB veriyolunu izole etmesi (özellikle macOS ve Windows’ta).
- Linux çekirdeğinin cgroup (Control Group) aygıt denetleyicisi ve konteyner dosya sistemi tecridi.
- Donanımı süren çekirdek modüllerinin (kernel modules) konteyner içinde değil, ana makinede bulunması zorunluluğu.
1. Ana makine mimarisi kısıtlamaları: Linux vs macOS vs Windows
Docker konteynerleri sanal makine değildir; ana makinenin çekirdeğini paylaşırlar. Bu kural işletim sistemlerine göre büyük farklılıklar doğurur:
Yerel Linux Motoru
Doğrudan ana makinenin Linux çekirdeği çalışır. Çekirdeğin oluşturduğu aygıt düğümleri
(/dev/ttyUSB0, /dev/bus/usb/...), dosya sistemi bağlamaları ve cgroup izinleri ile
doğrudan konteynere aktarılabilir.
macOS Üzerinde Docker Desktop
macOS’un çekirdeği XNU’dur (Darwin). Docker Desktop, macOS üzerinde Apple’ın
Virtualization.framework altyapısını kullanarak hafif bir Linux Sanal Makinesi (VM)
çalıştırır.
- Sorun: macOS hipervizör katmanı, Mac’in USB-C/Thunderbolt girişine takılan fiziksel USB aygıtlarını Linux sanal makinesine doğrudan aktarmaz (passthrough yapmaz).
- macOS çekirdeği (IOKit) aygıtı kendisi sahiplenir. Linux VM’i ve dolayısıyla Docker konteyneri donanımı göremez.
- Çözüm: macOS üzerinde doğrudan
docker run --deviceçalışmaz. Seri portlar içinsocatile ağ köprüsü kurulmalı ya da bir Linux VM üzerinden USB/IP kullanılmalıdır.
Windows Üzerinde Docker Desktop (WSL2)
Docker Desktop Windows’ta WSL2 (Windows Subsystem for Linux 2) sanal makinesinde çalışır. Hyper-V mimarisi WSL2’ye varsayılan olarak USB iletimi sağlamaz.
- Çözüm: Microsoft’un resmi açık kaynaklı aracı
usbipd-winkullanılmalıdır. Windows tarafındausbipd bindveusbipd attach --wslkomutlarıyla USB aygıtı WSL2 çekirdeğine iliştirilir.
2. Aygıt eşleme ve güvenlik: --device vs --privileged vs cgroups
Linux üzerinde bir konteynerin aygıta erişebilmesi için çekirdek iki seviyede izin arar:
- Konteynerin
/devdizininde o aygıta ait bir düğüm (mknodile açılmış karakter/blok aygıtı). - Çekirdeğin cgroups (Control Groups) aygıt filtresinde o aygıtın
major:minornumarasına okuma/yazma izni.
--device=/dev/ttyUSB0 (Statik eşleme)
Konteyner başlatılırken belirtilen aygıtın major:minor numarasını okur, konteyner içinde
aynı isimle bir düğüm oluşturur ve cgroup listesine izin ekler.
- Kısıt: Aygıtın konteyner çalıştırıldığı anda bilgisayara takılı olması şarttır.
--privileged tehlikesi (Antipattern)
İnternetteki birçok rehber USB sorunlarını aşmak için --privileged bayrağını önerir.
Bu bayrak konteyner tecridini tamamen kaldırır:
- Konteynere
CAP_SYS_ADMINve tüm çekirdek yetkilerini verir. - cgroup filtresini
a *:* rwm(tüm disklere ve belleğe tam erişim) yapar. - AppArmor ve Seccomp profillerini kapatır.
[!CAUTION]
--privilegedile çalışan bir konteyner içindeki zararlı bir kod veya güvenlik açığı, ana makinenin sabit disklerine (/dev/sda), UEFI değişkenlerine ve ana belleğe (/dev/mem) doğrudan erişebilir. USB erişimi için asla--privilegedkullanılmamalıdır!
Modern ve güvenli çözüm: --device-cgroup-rule
Konteynere tüm yetkileri vermeden, sadece belirli USB aygıt sınıflarına joker karakterle (wildcard) izin verebilirsiniz:
docker run --rm -it \
--device-cgroup-rule='c 188:* rmw' \
--device-cgroup-rule='c 166:* rmw' \
--device-cgroup-rule='c 189:* rmw' \
-v /dev:/dev \
ubuntu:24.04
Önemli Linux karakter aygıtı sınıfları (Major Numbers):
188: USB-Seri dönüştürücüler (/dev/ttyUSB*)166: ACM modem ve geliştirme kartları (/dev/ttyACM*)189: Ham USB veriyolu (/dev/bus/usb/*/*- libusb/hidapi için)
3. Dinamik USB takıp çıkarma sorunu (udev Hotplug problemi)
Geliştiricilerin en çok şaşırdığı durum şudur: Konteyner ilk açıldığında cihaza bağlanır, ancak USB kablosu sökülüp tekrar takıldığında konteyner cihazı kaybeder ve yeniden başlatılana kadar bağlanamaz.
Sorunun kök nedeni:
- USB aygıtı çıkarılıp takıldığında Linux çekirdeği yeni bir düğüm açar ve aygıt numarası artar (örneğin
/dev/bus/usb/001/004iken001/005olur). Seri portlarda ise dosya sistemindeki inode tamamen silinip yeniden üretilir. - Konteynerin
/devdizini izole birtmpfsbağlamasıdır. Ana makinedekiudevdservisi yeni düğümü yalnızca ana makinenin/devdizininde açar; konteynerin mount namespace’i bu olaydan habersiz kalır. --deviceile bağlanan eski inode artık öksüz kalmıştır (No such file or directoryveyaInput/output error).
Hotplug çözümü:
Dinamik aygıtları desteklemek için ana makinenin aygıt ağacı bir cilt olarak bağlanmalı ve cgroup joker kuralı tanımlanmalıdır:
docker run --rm -it \
--device-cgroup-rule='c 188:* rmw' \
--device-cgroup-rule='c 166:* rmw' \
-v /dev:/dev \
my-embedded-app
4. Ana makine çekirdek sürücüsü (Kernel Module) eksikliği
Bir diğer yaygın yanılgı, aygıt sürücüsünün konteyner içine apt-get install ile
kurulabileceğinin sanılmasıdır.
- Konteynerler kendi çekirdeklerini çalıştırmazlar. Donanım sürücüleri (
.ko) doğrudan çekirdeğe yüklenir. - Konteyner içinden
modprobeveyainsmodçalıştırılamaz (CAP_SYS_MODULEkısıtlaması). - Donanımı tanıyıp
/dev/ttyUSB0düğümünü üreten sürücü ana makinenin Linux çekirdeğinde yüklü olmalıdır.
Sık kullanılan çipler ve çekirdek modülleri:
ch341: Yaygın Arduino klonları ve ESP8266 kartları (WCH CH340/CH341).cp210x: NodeMCU ve ESP32 geliştirme kartları (Silicon Labs CP2102).ftdi_sio: Endüstriyel seri dönüştürücüler ve JTAG programlayıcılar.cdc_acm: Orijinal Arduino Uno/Mega, Raspberry Pi Pico ve USB modemler.
Ana makinede sürücü denetimi:
Aygıtı taktığınızda ana makinede çekirdek mesajlarını izleyin:
dmesg | tail -n 20
Beklenen sağlıklı çıktı örneği:
usb 1-1: new full-speed USB device number 5 using xhci_hcd
ch341 1-1:1.0: ch341-uart converter detected
usb 1-1: ch341-uart converter now attached to ttyUSB0
Eğer yalnızca new USB device found yazıyor ancak attached to tty... satırı görünmüyorsa,
ana makinenizde ilgili sürücü modülü eksiktir (sudo modprobe ch341 çalıştırılmalıdır).
Sorun giderme matrisi
| Belirti / Hata | Kök Neden | Çözüm Yolu |
|---|---|---|
| macOS’ta aygıt hiç görünmüyor | Docker Desktop VM USB aktarımı yapmaz | USB/IP veya Linux VM köprüsü kullanın |
| Kablo sökülüp takılınca bağlantı kopuyor | udev hotplug konteynere yansımıyor | -v /dev:/dev ve --device-cgroup-rule kullanın |
Permission denied (/dev/ttyUSB0) |
Dialout grup izinleri eksik | --group-add dialout veya konteyneri root çalıştırın |
lsusb var ama /dev/ttyUSB0 yok |
Ana makinede çekirdek sürücüsü eksik | Ana makinede modprobe cp210x / ch341 yapın |
Kaynaklar ve doğrulama ortamı
- Linux Kernel Documentation - Devices and Cgroups
- Microsoft usbipd-win Deposu
- Docker Engine Device Specification
Bu kılavuz, Ubuntu 24.04 (Linux 6.8), macOS Sonoma (Docker Desktop 4.30) ve FTDI/CH340 donanımları üzerinde test edilerek doğrulanmıştır.