本記事はTIS Engineer Advent Calendar 2015の21日目の記事です。
#やったこと
Docker v1.9から、Dockerネイティブでマルチホストネットワーキングに対応しました。そこで、2台のホストでROSの入ったコンテナを用意して、ROS通信が可能かどうか検証しました。
ROSの通信プロトコルはXML-RPCとTCP(+UDP)なのでコンテナ間通信自体は問題ないはずなのですが、今後、ROSの分散処理にもDockerの新しいネットワーク機能が使える可能性があるため、その手順を最初からまとめてみようと思います。
検証の概要
概要は下図のようになります。
検証のために、AWS上に2つのインスタンスを使用します。
両方にDockerを入れ、片方はROSのみ、もう一方はROS+Caffeが入ったDockerコンテナを立ち上げます。
クライアントからサーバへ画像を送り、サーバではCaffeで画像識別(分類)し、識別候補をクライアントに返し、クライアント側で表示する、ということをやります。
ちなみにCaffeとは、主に画像分類でよく用いられるディープラーニングフレームワークです。今回はCaffenetという学習済みモデルを使用しています。学習データはこちらにあり、オブジェクトやシーンなど1000カテゴリに分類することができます。
#環境
各インスタンスは以下のように設定しました。説明のために、各インスタンスの役割も載せています。
|インスタンス名|役割|インスタンスタイプ|ストレージ(GB)|セキュリティグループ|プライベートIP(例)|
|-----|-----|-----|-----|-----|-----|-----|
|インスタンスA|Caffeで画像分類(サーバ)|t2.medium|12|すべてのトラフィック|172.31.2.38|
|インスタンスB|事前に録画しておいた動画の再生、結果表示(クライアント)|t2.micro|8|すべてのトラフィック|172.31.11.163|
#手順
大きく分けて、前後半に分かれます
- 前半:Dockerの環境構築~コンテナのオーバレイネットワークの作成
- 後半:二つのコンテナ起動と、コンテナ間の接続検証、画像分類
Docker v1.9のマルチホストネットワークについてはこちらが詳しいです。Docker1.9のマルチホストネットワーク
以下の手順は、こちらのページを参考にしました。
Docker : マルチホスト間での仮想ネットワーク
(前半)EC2インスタンスに接続
sshでインスタンスに接続します。インスタンスA,Bそれぞれ以下のようにして接続してください。
ssh -i "キー(.pem)のファイル名" ubuntu@接続先のパブリックIPアドレス
#Dockerのインストール
まずはDocker v1.9をそれぞれのインスタンス上にインストールします。Docker v1.9はカーネルが「3.16」以上が必要ですが、デフォルトでは3.13だったので、アップグレードします。
sudo apt-get update
# カーネルアップグレード
sudo apt-get install linux-image-3.19.0-39-generic
# 再起動
sudo shutdown -r now
再起動後、もう一度EC2インスタンスにsshで接続します。その後Dockerをインストールします。
# Docker v1.9のインストール
# 公式サイト:https://docs.docker.com/engine/installation/ubuntulinux/
# キーを追加する
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
# /etc/apt/sources.list.d/docker.listを作成・更新
sudo touch /etc/apt/sources.list.d/docker.list
sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" >> /etc/apt/sources.list.d/docker.list'
# パッケージリストの更新
sudo apt-get update
# Dockerのインストール
sudo apt-get install docker-engine
# Dockerデーモン起動
service docker start
# バージョンの確認
docker --version
# Dockerデーモン停止
service docker stop
#Consulのインストール
# wget, unzipのインストール
sudo apt-get install wget unzip
# インストーラのダウンロード
wget https://dl.bintray.com/mitchellh/consul/0.5.0_linux_amd64.zip
# 解凍
unzip 0.5.0_linux_amd64.zip
# インストール
sudo mv consul /usr/local/bin/
#コンテナ間のネットワーク設定
では、まずは両インスタンスでConsulを起動します。このとき、インスタンスA側をサーバ、インスタンスB側をクライアントとしてクラスタを組みます。
# コンサル起動(サーバ)
# 例)consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -bind 172.31.2.38
$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -bind サーバのeth0のIPアドレス
# コンサル起動(クライアント)
# 例)consul agent -data-dir /tmp/consul -bind 172.31.11.163 -join 172.31.2.38
$ consul agent -data-dir /tmp/consul -bind クライアントのeth0のIPアドレス -join サーバのeth0のIPアドレス
次に、Dockerデーモンをそれぞれ起動します。
# Dockerデーモン起動(サーバ)
# 例)docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=172.31.2.38:2376
$ docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=サーバのeth0のIPアドレス:2376
# Dockerデーモン起動(クライアント)
# 例)docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=172.31.11.163:2376
$ docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=クライアントのeth0のIPアドレス:2376
ではネットワークを作成する準備が整いました。Dockerネットワークを作成しましょう。TestNetworkという名前のオーバレイネットワークを作成します。
# Dockerネットワーク作成(サーバまたはクライアントどちらか一方でよい)
docker network create -d overlay TestNetwork
では作成されているか確認しましょう。
# サーバ、クライアント両方でネットワークが表示されることを確認する
docker network ls
NETWORK ID NAME DRIVER
81adcdfa8a2c TestNetwork overlay
1841a25b8ac4 docker_gwbridge bridge
e56f819e681e bridge bridge
a0bae70d0364 none null
97a57a078ed1 host host
(後半)サーバコンテナを起動する
サーバコンテナでは、画像をクライアントコンテナから受け取り、Caffeで画像識別して結果をターミナルに表示します。そのために、ROSとCaffe本体、CaffeのROSブリッジが入ったDockerイメージを使います。
では、コンテナを起動してゆきます。
# ROSフルパッケージ + Caffe(CPUビルド済み)+ CaffeのROSブリッジが入ったDockerイメージをダウンロード
docker pull ykoga/ros_caffe_cpu
# コンテナ起動(サーバコンテナ)
docker run -it --rm \
--net=TestNetwork \
--name caffe \
--hostname caffe \
--env ROS_HOSTNAME=caffe \
--env ROS_MASTER_URI=http://caffe:11311 \
ykoga/ros_caffe_cpu \
/bin/bash
中に入ったら、一応ifconfigでネットワーク情報を確認しておきます。
おそらく、"eth数字 10.0.0.x" というネットワークインターフェースが存在すると思います。これが、ネットワークオーバーレイで生成されたネットワークです。
ではROSマスターとCaffeノードを起動します。
# ROSマスターの起動
roscore &
# クライアントから画像を受け取ってCaffeで識別する
roslaunch ros_caffe ros_caffe.launch image:=/image_raw
##クライアントコンテナを起動する
クライアントコンテナでは、テスト用に事前にROS bagファイルにカメラ画像を録画しておいたファイルがあるので、それを使ってカメラ画像をPublishします。
では、コンテナを起動してゆきます。まずはROSの基本機能のみが入ったコンテナを起動します。
# ROSの基本イメージをダウンロード
docker pull ros:indigo-ros-base
# コンテナ起動(クライアント)
docker run -it --rm \
--net=TestNetwork \
--name image_pub \
--hostname image_pub \
--env ROS_HOSTNAME=image_pub \
--env ROS_MASTER_URI=http://caffe:11311 \
ros:indigo-ros-base \
/bin/bash
今回の検証で使うために、事前にROS bagファイルをダウンロードしておきます。
bagファイルには、事前にWebカメラで撮った動画が入っています。中身は下図のようなものです。(もし手元にROS環境がローカルPCにもある方ならrosrun rqt_image_view rqt_image_viewでビューアを起動すれば確認できます)
ではbagファイルをダウンロード、再生します。
apt-get update
apt-get install wget
# bagファイルのダウンロード
wget https://raw.githubusercontent.com/ykoga-kyutech/ros_caffe_cpu/master/test_webcam.bag
# 識別結果を表示
rostopic echo /ros_caffe/predictions &
# ROS bagファイル(録画した動画)の再生
rosbag play --loop test_webcam.bag
最後の行まで打ち込むと、Caffeの識別が始まり、クライアントコンテナ側に識別結果が表示されます。結果は例えば次のようになります。
結果の見方ですが、結果は上位5件ずつ候補が表示されます。スコアは0~1をとり、1のものほど確信度が高いことを示しています。この例だと、notebook, notebook computerが最も確信度の高い識別結果ということになります。
まとめ
Docker v1.9からの新機能であるマルチホストネットワーク機能を使って2台のインスタンス間で画像のやりとりを行うことで、ROS通信が問題なくできることを確認しました。
#わからなかったこと
- 上記のやり方でAWSインスタンス間での通信はOKだったが、AWSインスタンスと自宅PC間のROS通信はできなかった。というかConsulでクラスタを組む所からできなかった。VPNとかで両者を接続しないとだめなのかな。
- ROSの通信プロトコルはXML-RPCとTCP(+UDP)のはずなので、セキュリティグループを色々いじってみたが、結局、設定方法が「すべてのトラフィック」しか通信できなかった。ROS通信でのセキュリティグループの設定が不明。