DockerでGUIアプリを起動する(amd64)(GPU)(ROS2)

#やりたいこと
amd64のGPU付きノートPCで、Docker上でROS/ROS2を起動して、rvizやgazeboを動かしたい。
本体環境を保護して、別のマシンに環境をそのまま持っていくことが目的。
やり方が理解できないので、先人のやっていることを理解しながらのメモ。

**やりたいことをもう少し細分化**

+ GPU付きのノートPCのDocker環境でROSを動かす
+ GUDA10.2とOpenGLが動く
+ GUIが画面に出力されるようにする
+ ディレクトリをホストと共有したい

#参考にしている情報

シンプルに書いてある。参考になりそう。
[DockerでGUIアプリを実行する](http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/)

OpenGLが動くDockerイメージ
[nvidia/opengl
](https://hub.docker.com/r/nvidia/opengl)

CUDAとOpenGLが動くDockerイメージ
[nvidia/cudagl](https://hub.docker.com/r/nvidia/cudagl)

やりたいことは、この方と同じこと。
[ROSをdocker上で動かすサンプル．rvizなどのGUIアプリケーションも動作するはず．](https://github.com/shinsumicco/docker-ros)

ROS.org 公式の説明
[Using GUI's with Docker](http://wiki.ros.org/docker/Tutorials/GUI)

gazebo公式のインストールマニュアル
[Install Gazebo using Ubuntu packages](http://gazebosim.org/tutorials?tut=install_ubuntu&cat=install)

個人的に常々参考にさせていただいている。ARMコア用
[NVIDIA JetsonデバイスでROSを起動するDockerfile](https://github.com/atinfinity/jetson_ros_docker)

gazeboバイナリを取得するDockerfileの書き方が大変参考になりそう
[ROS / GAZEBOでのPriusのデモ](https://github.com/osrf/car_demo)

WEBブラウザ経由で試せるROS/ROS2環境。私も使ってます。
[ROS/ROS2のGUIをWebブラウザ経由でお手軽に試せるDockerfileを公開しました](https://memoteki.net/archives/2955)

ROS2をDockerで動かす貴重なサンプル。
[ROS2開発環境をDockerで構築する](https://tshell.hatenablog.com/entry/2020/02/13/174414)

すこし前の記事だけと、自分のやりたいことはほとんどこれ
[DockerでGUIのアプリ動かすためのメモ](https://qiita.com/tnarihi/items/275c009e9dec1306893f)

privileged オプションの詳細の参考
[Docker privileged オプションについて](https://qiita.com/muddydixon/items/d2982ab0846002bf3ea8)

同じことをやっている人がいましたｗ
[RealSense D435を使ってElasticFusion](https://qiita.com/eduidl/items/ebf40d79dc03de03abb3)

JetsonでROS2でRealSense
[Jetson Nano+ROS2上でのIntel RealSenseの動作と注意点](https://rb-station.com/article/jetson-nanoros2%E4%B8%8A%E3%81%A7%E3%81%AEintel-realsense%E3%81%AE%E5%8B%95%E4%BD%9C%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9)

#条件
CORE i7 GTX1660TiのノートPCで、ホストOSはUbuntu18.04
Docker19.xで実施する。
NVIDIA-dockerは使用しない。

#試してみること

+ GPUなしでGUIを起動し、FireFoxなど立ち上げてみる
+ GPUなしでOpenGLアプリを起動する
+ GPUありでGUIを起動し、FireFoxなど立ち上げてみる
+ GPUありでOpenGLアプリを起動する

#色々ためしてみる
ネット上のサンプルを元に、実施可能かどうかやってみることにします。

##1.ROSをdocker上で動かすサンプル(NG)

まずはこちらを参考に、rvizが立ち上がるかやってみます。

[ROSをdocker上で動かすサンプル．rvizなどのGUIアプリケーションも動作するはず．](https://github.com/shinsumicco/docker-ros)

```bash:docker_build
$ docker build --tag shinsumicco/ros:kinetic-desktop-standard --file Dockerfile.kinetic.standard .
$ docker network create rosnet
```

ターミナルを２つ開いて実行

```bash:terminal1
$ cd /path/to/docker-ros/
$ ./run.kinetic.standard --name roscore --net rosnet
# sudo chown -R $USER:$USER .
# roscore
... logging to /home/kuma/.ros/log/e940edd6-9dfa-11ea-ba03-0242ac120002/roslaunch-c2169f1a0bde-33.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://c2169f1a0bde:40163/
ros_comm version 1.12.14


SUMMARY
========

PARAMETERS
 * /rosdistro: kinetic
 * /rosversion: 1.12.14

NODES

auto-starting new master
process[master]: started with pid [43]
ROS_MASTER_URI=http://c2169f1a0bde:11311/

setting /run_id to e940edd6-9dfa-11ea-ba03-0242ac120002
process[rosout-1]: started with pid [56]
started core service [/rosout]

```

roscoreの実行で表示されるURLをターミナル2の起動引数にする。

```bash:terminal2
$ ./run.kinetic.standard --name rviz --net rosnet --env ROS_HOSTNAME=rviz --env ROS_MASTER_URI=roscoreの起動URLを使う
# sudo chown -R $USER:$USER .
# rosrun rviz rviz

[ INFO] [1590351256.773212816]: rviz version 1.12.17
[ INFO] [1590351256.773241018]: compiled against Qt version 5.5.1
[ INFO] [1590351256.773249629]: compiled against OGRE version 1.9.0 (Ghadamon)
libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
Could not initialize OpenGL for RasterGLSurface, reverting to RasterSurface.
libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
Segmentation fault (core dumped)

```

OpenGLのライブラリのエラーが表示されて、異常終了してしまいました。
GPU付きのPCだとうまく行かないのかな？

もう片方のGPU用はNVIDIA-Dockerを使用するということでやめておくことにします。

##2.X11ソケットをコンテナーと共有して直接使用する(OK)
[DockerでGUIアプリを実行する](http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/)

こちらを試してみます。

```dockerfile
FROM ubuntu:14.04

RUN apt-get update && apt-get install -y firefox

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox
```

Dockerfileをビルドして実行します。

```bash:terminal
$ docker build -t firefox .
$ docker run -ti --rm \
       -e DISPLAY=$DISPLAY \
       -v /tmp/.X11-unix:/tmp/.X11-unix \
       firefox
```

これはあっさり動作しました。
X11の設定が妥当になされていれば、実施できるようです。

![Screenshot from 2020-05-25 05-58-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328520/c69ae233-3c66-048e-f8e6-508be35ba98c.png)

#3.CUDAとOpenGLが動くコンテナを作り、画面に表示してみる
CUDA10.2+OpenGL(glvnd1.0) をpullしてみます。
タグはbase/runtime/develの3種類がありますが、どういう区分かわからないので、baseを選びます。（dockerhubに説明書いてほしい）

```bash:terminal
$ docker pull nvidia/cudagl:10.2-base-ubuntu18.04
$ docker run -it --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --name glvnd nvidia/cudagl:10.2-base-ubuntu18.04
```
テストのため、Dockerコンテナの中で、xeyesをインストールして実行してみます。

```bash:terminal
# apt-get install x11-apps:i386
# xeyes
No protocol specified
Error: Can't open display: :0

```

あら？開かない。

X11でうまく行ったDockerfileをそのまま使う。
完全にそのままだと、「/bin/sh: 1: cannot create /etc/sudoers.d/openwrt: Directory nonexistent」が発生する。
sudoがインストールされていないので、sudoをインストールするように修正すると動作する。

```dockerfile:sudoを追加
FROM nvidia/cudagl:10.2-base-ubuntu18.04

RUN apt-get update && apt-get install -y firefox sudo

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox
```
実行はこちら

```bash:terminal
$ docker run -it --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --name glvnd cuda_gui
```

これで、FirefoxはDockerのなかからGUIで表示されました。

##CUDAのバージョンをnvidia-smiで確認してみる
FireFoxを立ち上げたときはGPUが有向になっていませんでした。
--gpus all を引数に追加してみます。
FireFoxの起動の代わりにbashを起動して挙動を試します。

```bash:terminal
$ docker run -it --gpus all --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --name glvnd cuda_gui
$ nvidia-smi 

Tue May 26 09:37:22 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82       Driver Version: 440.82       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 166...  Off  | 00000000:01:00.0 Off |                  N/A |
| N/A   45C    P8     6W /  N/A |    458MiB /  5944MiB |      5%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

```

nvidia-smi を実行して、GPUが認識されていることが確認できました。
CUDA10.2もインストールされているようです。

##OpenGLのベンチマークソフトを実行させてみる
適当なベンチマークソフトをコンテナにインストールして実行します。

```
$ sudo apt-get install glmark2
$ glmark2
```
このように、デスクトップにGUIが表示されました。

![Screenshot from 2020-05-26 18-47-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328520/582b0811-237c-2a6a-347d-0151c46bc566.png)

#ROS2とgazeboを追加してみる

gazeboとROS2が動くように、色々インストールしてみます。
その過程でわかったことのメモ

`ENV DEBIAN_FRONTEND=noninteractive`
インストールのときに入力を求められたら、デフォルトで動くようにする。
Dockerをビルドしているときに入力求められると止まってしまうので、これで対策。

`apt-get install -yq `
セットアップのオプションで、-yは問い合わせ[Y/n]にYで答えてインストールを継続するオプション。
-qは標準出力に表示を出さずにインストールを行う。

私が適当に打ち込んだDockerfileですが、gazeboは動作しました。
しかし、ROS2は呼び出せなかったので、何か設定をミスっているようです。

![Screenshot from 2020-05-27 23-17-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328520/9c8f3d6f-9586-9f86-e6d4-575ab4d6ed66.png)

## ROS2のセットアップをするDockerfileを書く
ROS2のイメージをそのまま使うのが一番近道ですが、Dockerfileの大本はcudaとOpenGLが動くものを使ってしまいましたので、そのイメージにROS2のセットアップを手動で追加します。
が、安直に組み入れると、ROS2は動くのに、gazeboが動かない状態に陥ってしまいました。


そこで、今度は確実に動くだろう組み合わせから攻めていこうと思います。

# 4.cuda10.2+OpenGL+gazeboのDockerfileをとりあえず作る

```
# GPUとGUIが使えるDockerfileをamd64で動かす

FROM nvidia/cudagl:10.2-base-ubuntu18.04

# これを入れないとtzdataで設定入力を求められて停止する
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -yq wget curl git build-essential vim sudo lsb-release locales bash-completion tzdata

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

RUN curl -sSL http://get.gazebosim.org | sh

USER developer
ENV HOME /home/developer

```

実行してみると、あっさりgazeboが動きます。
これにROS2をインストールしてみます。

# 5.cuda10.2+OpneGL+gazebo+ROS2
日本語環境もいれて、使いやすく動くようにしてみます。

```dockerfile:Dockerfile

# GPUとGUIが使えるDockerfileをamd64で動かす

FROM nvidia/cudagl:10.2-base-ubuntu18.04

# これを入れないとtzdataで設定入力を求められて停止する
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -yq wget curl git build-essential vim sudo lsb-release locales bash-completion tzdata

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

RUN curl -sSL http://get.gazebosim.org | sh

ENV ROS_DISTRO dashing

# ロケールのセットアップ
RUN apt-get update && apt-get install -y locales && \
    dpkg-reconfigure locales && \
    locale-gen ja_JP ja_JP.UTF-8 && \
    update-locale LC_ALL=ja_JP.UTF-8 LANG=ja_JP.UTF-8
ENV LC_ALL   ja_JP.UTF-8
ENV LANG     ja_JP.UTF-8
ENV LANGUAGE ja_JP.UTF-8

# APTソースリストの設定
RUN apt-get update && \
    apt-get install -y curl gnupg2 lsb-release && \
    curl http://repo.ros2.org/repos.key | apt-key add - && \
    sh -c 'echo "deb [arch=amd64,arm64] http://packages.ros.org/ros2/ubuntu \
    `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-latest.list' && \
    apt-get update

# ROS2パッケージのインストール
RUN export ROS_DISTRO=dashing && \
    apt-get install -y ros-$ROS_DISTRO-desktop \
    python3-colcon-common-extensions python3-rosdep python3-argcomplete && \
    rosdep init && \
    rosdep update

USER developer
ENV HOME /home/developer

## 環境設定
RUN    echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc

```

これをビルドして、実行します。

```bash:terminal
$ docker build -t cuda_gui .
$ docker run -it --gpus all --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --name glvnd cuda_gui
```

コンソールで、gazebo,ros2ともに動きました。
rviz2を実行すると、こちらも動作します。
rviz2が動いたので、OpenGLも問題なく動いていると判断できます。
![Screenshot from 2020-05-28 18-11-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328520/7b03150e-2cf5-1544-3648-19f7562bda07.png)

```bash:terminal
[INFO] [rviz2]: Stereo is NOT SUPPORTED
[INFO] [rviz2]: OpenGl version: 4.6 (GLSL 4.6)
```

ちゃんと、OpenGL 4.6とコンソールに出力されていました。
これで、やりたいことはできましたが、ホストマシンのディレクトリと一致させて、保存の手間を省きたいところです。

次回の予定
[DockerからREALSENSE D435iを繋いで、ROS2で表示してみる](https://qiita.com/t_kumazawa/items/eb3a60f0ca1fbca70bee)