はじめに
DockerでGUIを表示したいとき、何が起きているのか理解したかったのでメモを作りました。参考にしたのはROSのチュートリアルですが、それ以外のコンテナでも同様だと思います。
ちなみにVNCとブラウザでGUIを表示する方法がお手軽なのでおすすめです。が、ここではX window systemを使う場合の挙動について記します。
X window system
UNIXライクなOSでよく使われているウィンドウシステム。サーバーとクライアントから構成され、Xプロトコルを用いて通信する。1987年からプロトコルのバージョンが11なのでシステム指してをX11、あるいは単にXとも呼ぶ。Xの実装がXorgで、多くのLinuxディストリビューションで用いられている。
Xサーバーはディスプレイ、マウス、キーボードなどの入出力装置を制御する。もともとX自体、ネットワーク越しに動くプログラムのウィンドウを手元のディスプレイで表示・操作することを意図して作られており、Xサーバーがローカル、Xクライアントがリモートのイメージ。もちろんXクライアントがローカルにあっても動く。
DockerでGUIを表示する仕組み
-
ソケット
DockerでGUIを表示するには、Dockerコンテナ内のXクライアントがホストのXサーバーと通信する必要がある。XサーバとXクライアントが同じコンピュータにある場合、これには通常UNIX-domain socketが使われる。ソケットファイルは/tmp/.X11-unix
以下にあり、アクセスすることでXサーバーとの通信が可能になる。Dockerコンテナ起動時に/tmp/.X11-unix
をマウントすればコンテナ内からアクセスできる。 -
環境変数DISPLAY
DISPLAY
でXクライアントが接続するXサーバーを指定する。フォーマットはhostname:displaynumber.screennumber
。例えばlocalhost:0.0
など。ただしhostnameとscreennumberは省略できるのでこの場合:0
でも良い。docker runに--env="DISPLAY"
を追加すればホストの環境変数がコンテナ内に反映される。
簡単な方法
ではdocker runで/tmp/.X11-unix
とDISPLAY
を設定すればいいのかというとそれだけではなく、Xサーバーと通信するには認証が必要。通常rootユーザーからXサーバーにアクセスはできないが、以下をホストで実行すると可能になる。ただしこれはセキュリティ的に推奨されない。
$ xhost +local:root
もう少し安全な方法
DockerコンテナのUIDとGIDをホストと同じにすればアクセスできる。例えば次のように--userオプションを設定する。
$ docker run -it --rm \
--user=$(id -u $USER):$(id -g $USER) \
--env="DISPLAY" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
osrf/ros:melodic-desktop-full
ただし、root以外のユーザーが設定されていないDockerイメージではI have no name!
になってしまう。以下のように必要なディレクトリをマウントすると、コンテナ内でホストと同じユーザー名にできる。
$ docker run -it \
--user=$(id -u $USER):$(id -g $USER) \
--env="DISPLAY" \
--volume="/etc/group:/etc/group:ro" \
--volume="/etc/passwd:/etc/passwd:ro" \
--volume="/etc/shadow:/etc/shadow:ro" \
--volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
osrf/ros:melodic-desktop-full
隔離する方法
Dockerイメージにroot以外のユーザーがある場合、UIDとGIDがホストと同じに設定してあれば良いらしい。例えば次のようなDockerfileを作る。
FROM osrf/ros:melodic-desktop-full
#Add new sudo user
ENV USERNAME myNewUserName
RUN useradd -m $USERNAME && \
echo "$USERNAME:$USERNAME" | chpasswd && \
usermod --shell /bin/bash $USERNAME && \
usermod -aG sudo $USERNAME && \
echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$USERNAME && \
chmod 0440 /etc/sudoers.d/$USERNAME && \
# Replace 1000 with your user/group id
usermod --uid 1000 $USERNAME && \
groupmod --gid 1000 $USERNAME
次のコマンドでDockerfileをビルドし、起動すればGUIが表示できるコンテナになる。(参考にしたチュートリアルではX authentication fileを作ってマウントする必要があるとのことだったが、私の環境ではなしで動いた)
$ docker build -t hoge .
$ docker run -it --rm \
--env="DISPLAY" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
-user="myNewUserName" \
hoge
参考
おわりに
dockerとGUIについての理解が少し深まりました。ただ、X authentication周りは触らずに動いてしまったので少しモヤッとしていますが...