よくある話しかも知れませんが、OpenCVベースのシンプルなプログラムでいろいろ試してみて、新しい発見もあったので、まとめてみます。
はじめに
実験のために走らせたプログラムは、OpenCVベースの単純なもの。Docker containerをHostと同じユーザーで動作させる場合とroot権限で動作させる場合で検証してみました。
周辺事情によりやや古い環境です :
# cat /proc/version Linux version 4.15.0-54-generic (buildd@lgw01-amd64-014) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #58-Ubuntu SMP Mon Jun 24 10:55:24 UTC 2019 # docker --version Docker version 18.09.7, build 2d0083d
Python Program
実験のためにDocker container内で走らせるプログラムは、以下の超単純なもの :
import cv2 def main(): capture = cv2.VideoCapture(-1) while True: ret, frame = capture.read() if ret: cv2.imshow('frame', frame) key = cv2.waitKey() & 0xFF if key == ord('q'): break if __name__ == "__main__": main()
これを2種類のDocker contaierで走らせてみる。
Dockerfile
実験1 : Hostと同じユーザーで動作
まずひとつめの実験は、Docker containerをHostと同じユーザーで動作させるパターン。この場合Displayの共有は-eオプションで簡単にできるし、Cameraの共有も–deviceオプションで簡単なはず…
用意したDockerfileは以下の通り :
FROM ubuntu:18.04 RUN apt-get -y update && \ apt-get install -y build-essential g++-8 libopenblas-dev \ libgtk2.0-dev pkg-config python-dev python-numpy \ libcanberra-gtk-module sudo python3-pip v4l-utils acl RUN pip3 install opencv-python==4.1.1.26 ENV NO_AT_BRIDGE 1 ENV QT_X11_NO_MITSHM 1 ENV USER_ID=1000 ENV GROUP_ID=1000 ENV USER_NAME=usr1 RUN groupadd -g $GROUP_ID $USER_NAME && \ useradd -m -u $USER_ID -g $GROUP_ID $USER_NAME -G sudo && \ echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers WORKDIR /home/$USER_NAME COPY ./files/test.py . RUN chown ${USER_ID}:${GROUP_ID} -R /home/$USER_NAME USER $USER_NAME CMD python3 test.py
opencv-pythonはちょっと古いものを使ってるけど、最近のバージョンを使う場合はこちらの記事(Docker + opencv-pythonでImportError: libGL.so.1が出て調べたこと)を見て修正してください。
Docker imageのビルド :
# docker build -t test .
Docker containerの起動 :
# docker run --rm -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --device /dev/video0:/dev/video0 test VIDEOIO ERROR: V4L: can't find camera device
残念ながらエラー。
Docker container内で状況を確認してみる : (CMD行外して要再ビルド)
$ id uid=1000(usr1) gid=1000(usr1) groups=1000(usr1),27(sudo) $ sudo v4l2-cnt --list-device HD Pro Webcam C920 (usb-0000:00:14.0-6): /dev/video0 $ ls -la /dev/video0 crw-rw---- 1 root video 81, 0 Mar 2 08:17 /dev/video0 $ getfacl /dev/video0 getfacl: Removing leading '/' from absolute path names file: dev/video0 owner: root group: video user::rw- group::rw- other::---
一方Host側も見てみると、
# ls -la /dev/video0 crw-rw----+ 1 root video 81, 0 Mar 2 08:17 /dev/video0 # getfacl /dev/video0 getfacl: 絶対パス名から先頭の '/' を削除 file: dev/video0 owner: root group: video user::rw- user:usr1:rw- group::rw- mask::rw- other::---
明らかに違う。Host側のacl制御がDocker container側に引き継がれていない。
この問題に対して、以下の3つの手段が有効だった : (他にもあるかも)
- usr1をvideoグループに入れる
- Docker container起動後に、”$ sudo setfacl -m u:usr1:rw /dev/video0″ でacl制御する
- Host側で “# sudo chmod 666 /dev/video0” してから、Docker containerを起動する
1はDockerfileで “useradd -m -u $USER_ID -g $GROUP_ID $USER_NAME -G sudo,video” とすればよいので一番楽か。
2は “docker run” 時にデバイスの共有を指定するためDockerfileに記述しても意味がなく、起動後に設定する必要があるため、ENTRYPOINTに記述するか、container起動後に設定するか、あたりになる。
3はHost環境をいじるので、Docker containerを他人と共有する場合は嫌がられるかも知れない。
実験2 : root権限で動作
ふたつめの実験は、Docker containerをroot権限で動作させるパターン。この場合Cameraの共有は–deviceオプションで簡単にとおるが、Displayの共有でケアが必要になるはず。
用意したDockerfileは以下の通り :
FROM ubuntu:18.04 RUN apt-get -y update && \ apt-get install -y build-essential g++-8 libopenblas-dev \ libgtk2.0-dev pkg-config python-dev python-numpy \ libcanberra-gtk-module sudo python3-pip RUN pip3 install opencv-python==4.1.1.26 ENV NO_AT_BRIDGE 1 ENV QT_X11_NO_MITSHM 1 WORKDIR /root COPY ./files/test.py . CMD python3 test.py
Docker imageのビルド :
# docker build -t test .
Docker containerの起動 :
# docker run --rm -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --device /dev/video0:/dev/video0 test No protocol specified : cannot connect to X server :1
想定通りのエラー。xhostでlocal userにDisplayの使用を許可して、
# xhost +local: non-network local connections being added to access control list
再度 “docker run” すると、動作した !
おわりに
HostとDocker containerとのデバイスの共有を実験してみました。
グッディーは自作のDocker imageを他人と共有することがあるのだけど、”# xhost +” は大変嫌がられるので、”# xhost +local:” で許してちょ、という感じ。
“/dev/video*” のPermissionも環境によって微妙に違ったりするので、やり方によっては共有先の人がはまりまくることにもなるので、videoグループかroot権限が無難かな、とは思います。