AI

Docker containerからHostのCamera画像を取得してHostのDisplayに出力する

よくある話しかも知れませんが、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つの手段が有効だった : (他にもあるかも)

  1. usr1をvideoグループに入れる
  2. Docker container起動後に、”$ sudo setfacl -m u:usr1:rw /dev/video0″ でacl制御する
  3. 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権限が無難かな、とは思います。


   
関連記事
  • Docker buildでthe public key is not available: NO_PUBKEY A4B469963BF863CC への対処
  • OpenCV CLAHEのclip limitの意味
  • Docker + opencv-pythonでImportError: libGL.so.1が出て調べたこと
  • Workaround for the instable cv2.solvePnP with flags=cv2.SOLVEPNP_ITERATIVE
  • Staticなffmpegがとてもusefulだった話
  • Docker container内でsudoするとsudo: effective uid is not 0と言われる問題が解決 !

    コメントを残す

    *

    CAPTCHA