Docker: ROS1-noeticの環境イメージ作成（Ubuntu・Windows）

# Docker: ROS1-noeticの環境イメージ作成

## はじめに
Dockerのイメージ作成の練習として，
Ubuntu20.04上にROSを構築して，ワークスペース作成までのDockerイメージを作成してみる．

流れは以下のとおりである．

1. Ubuntu20.04のイメージをベース
1. ROSのインストールコマンドをDockerfile内で
1. ワークスペースをDockerfile内で作成

注意したいところは，上記のものだけを適用しても，RvizなどのGUIは動かないこと．

前回の記事を参考にそのGUIも使えるようにしたROS環境の構築について詳しくまとめる．
また，ホストOSがUbuntuかWindowsかによって少し手順やDockerfileも異なるため，分けて記述する．

## Ubuntu上での構築
```Dockerfile:Dockerfile
# osrfが提供するrosイメージ（タグがnoetic-desktop-full）をベースとしてダウンロード
FROM osrf/ros:noetic-desktop-full

# Docker実行してシェルに入ったときの初期ディレクトリ（ワークディレクトリ）の設定
WORKDIR /root/

# nvidia-container-runtime（描画するための環境変数の設定）
ENV NVIDIA_VISIBLE_DEVICES \
    ${NVIDIA_VISIBLE_DEVICES:-all}
ENV NVIDIA_DRIVER_CAPABILITIES \
    ${NVIDIA_DRIVER_CAPABILITIES:+$NVIDIA_DRIVER_CAPABILITIES,}graphics

# ROSの環境整理
# ROSのセットアップシェルスクリプトを.bashrcファイルに追記
RUN echo "source /opt/ros/noetic/setup.sh" >> .bashrc
# 自分のワークスペース作成のためにフォルダを作成
RUN mkdir -p catkin_ws/src
# srcディレクトリまで移動して，catkin_init_workspaceを実行．
# ただし，Dockerfileでは，.bashrcに追記した分はRUNごとに反映されないため，
# source /opt/ros/noetic/setup.shを実行しておかないと，catkin_init_workspaceを実行できない
RUN cd catkin_ws/src && . /opt/ros/noetic/setup.sh && catkin_init_workspace
# ~/に移動してから，catkin_wsディレクトリに移動して，上と同様にしてcatkin_makeを実行．
RUN cd && cd catkin_ws && . /opt/ros/noetic/setup.sh && catkin_make
# 自分のワークスペースが反映されるように，.bashrcファイルに追記．
RUN echo "source ./catkin_ws/devel/setup.bash" >> .bashrc
```

1行ずつ何をしているかについては，コメントアウトで示すとおりである．


### GUI有効にするための工夫
```bash:run.sh
# If not working, first do: sudo rm -rf /tmp/.docker.xauth
# It still not working, try running the script as root.
	
xhost +
XAUTH=/tmp/.docker.xauth
if [ ! -f $XAUTH ]
then
    xauth_list=$(xauth nlist :0 | sed -e 's/^..../ffff/')
    if [ ! -z "$xauth_list" ]
    then
        echo $xauth_list | xauth -f $XAUTH nmerge -
    else
        touch $XAUTH
    fi
    chmod a+r $XAUTH
fi

docker run -it \
    --env="DISPLAY=$DISPLAY" \
    --env="QT_X11_NO_MITSHM=1" \
    --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
    --env="XAUTHORITY=$XAUTH" \
    --volume="$XAUTH:$XAUTH" \
    --runtime=nvidia \
    --name=ros1_noetic \
    ros1_noetic \
    bash
echo "done"
```

```run.sh```の中に```docker run```を含めているため，以下のようにするだけでよい．
肝としては，```xhost +```により，xserverへのアクセス権限を解放している．そのため，使用後には，```xhost -```にして，もとに戻すのが良いと思われる．docker側からもxserverにアクセスすべく，xserver　に関わるx11についてマウントし，XAUTHについてもマウントしていることが分かる．


```terminal:docker run
bash run.sh
```


## Windows上での構築

```Dockerfile:Dockerfile
FROM osrf/ros:noetic-desktop-full

WORKDIR /root/

# xserverへアクセスする際の環境変数の設定
ENV DISPLAY host.docker.internal:0.0

# apt-getのアップデートとアップグレード
RUN apt-get update -y && apt-get upgrade -y
# x11-apps（xserverにアクセスするために必要なパッケージ）をインストール
RUN apt-get install x11-apps -y
RUN echo "source /opt/ros/noetic/setup.sh" >> .bashrc
RUN mkdir -p catkin_ws/src
RUN cd catkin_ws/src && . /opt/ros/noetic/setup.sh && catkin_init_workspace
RUN cd && cd catkin_ws && . /opt/ros/noetic/setup.sh && catkin_make
RUN echo "source ./catkin_ws/devel/setup.bash" >> .bashrc
```

Ubuntu側で説明した部分は説明を省いている．Widnwos側で追記した部分についてはコメントアウトで説明している．

```terminal:docker run
docker run -it -v ${path}/share:/root/docker_share --name ros1_noetic ros1_noetic:latest /bin/bash
```

私の環境では，このように実行している．```-v ${path}/share:/root/docker_share```により，Dockerfileと同じディレクトリにあるshareフォルダをマウントすることで，dockerとホスト間でファイルのやり取りを可能にしている．これはdocker内で，docker_shareというディレクトリにつなげているが，もちろん，ROSのワークスペースと直接つないでもよいと考える．

## おまけ（Makefileの利用）
最後にDockerを使う中で，非常にMakefileの便利さに気づかされたため，一例を示す．

まず，フォルダの配置．

```terminal:フォルダの配置
docker_ros1_noetic_on_windows
  |- share
     |- README.md
  |- Dockerfile
  |- Makefile
  |- README.md
```

```makefile:Makefile
path=D:/Docker/docker_ros1_noetic_on_windows

build:
	docker build -t ros1_noetic:latest .
run:
	docker run -it -v ${path}/share:/root/docker_share --name ros1_noetic ros1_noetic:latest /bin/bash
start:
	docker start ros1_noetic
shell:
	docker exec -it ros1_noetic bash
stop:
	docker stop ros1_noetic
rm:
	docker rm ros1_noetic
git:
	git add ./share/README.md
	git add ./Dockerfile
	git add ./Makefile
	git add ./README.md
	git commit -m "modified"
	git push -u origin main
```
このように，Makefileをつくれば，makeコマンドだけで，すべて扱えるようになる．

- **コンテナのイメージファイル作成**
```
make build
```

- **コンテナ生成+起動**
```
make run
```

- **コンテナ起動（コンテナ停止しているものを起動）**
```
make start
```
※make runではすでにあるコンテナは起動できない

- **コンテナに入る（インタラクティブシェル）**
```
make shell
```

- **コンテナ停止（停止しないと削除できない）**
```
make stop
```

- **コンテナ削除**
```
make rm
```

- **githubへプッシュ**
```
make git
```

私の場合は，まだDocker Hubを活用できていないため，gitへのプッシュのみとしている．

## 感想
Ubuntu上での環境構築は比較的簡単だったが，Rvizの動作確認まで含めると，```xhost +```による解決を見つけるまでには少し時間がかかった．Windowsについても同様にできるかなと思ってやってみたら，そもそも```xhost +```とは何かというような形でエラー．xserverへの知識がゼロだったために，それを準備してあげることにも気づかず，丸1日2日ほど，時間を費やしてしまった．なんとかできたため良いとする．Dockerを学んでいく中で，Dockerが使えるようになっていくのは当たり前だが，Makefileやシェルスクリプトのありがたみを改めて知り，これは使っていかなければと思えたのは非常に大きい．これからも，理解を深めて活用していきたいと思う．ちなみに，すでにROS2（foxy）やPyTorchに関しても同様に，環境構築ができているが，共有する余裕があるかは分からない．できるだけ早めにまとめてはおきたいと考えてはいる．