AWS-VPC内でROS2(FastDDS-Multicast-UDP)を使ったPub/Subを実行する【相性良い/悪い!?】

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/df13b248-ba33-2ccb-7857-358eaf5bf2b8.jpeg" width="720px">

## 要約
- デフォルトで、AWS-VPC（以下、VPCと呼称する。）内の通信手段としてブロードキャスト・マルチキャストの利用は制限されています。
- 「[AWS Transit Gateway](https://aws.amazon.com/jp/transit-gateway/)のマルチキャスト機能」を使うとこの制約の一部を回避することが可能です。
- ただし、「マルチキャスト・パケット転送」がカーネルビルド時に無効オプションがデフォルトとされており、それに起因して使いづらい部分もあること。
   - Dockerから使う場合などなど。

## はじめに
### ROS2元年！？
[ROSCon JP 2021](https://roscon.jp/)や[ROS World 2021](https://roscon.ros.org/world/2021/)の発表を見ると、その多くがROS2を使って実行されていることが分かります。[ROS Noetic NinjemysのEOLは2025年5月](https://wiki.ros.org/Distributions)と、移行の猶予時間はまだ残っている段階ですが、事例も増えてきておりROS2への理解も深めていくタイミングとしては2021年は良いタイミングと感じました。
そこで本記事では「ROS2のノードの一部（ROSbagとか!?）をクラウドサービス上で動かしたいなー」と考える方に向けて、選択肢の一つを提示できればと思います。

### VPC(Virtual Private Cloud)ではブロードキャスト・マルチキャストが制限されている
VPCは、パブリッククラウド内に論理的に分離された仮想ネットワーク空間を提供します。我々、利用者はこの中にEC2(仮想マシン)、RDS(データベースサービス)などの各リソースを配置して「目的に応じたインフラを自由に構築」していくわけです。
自由に〜と書きつつも、制約もあります。その一つとして、「VPC内の通信手段としてブロードキャスト・マルチキャストの利用が制限せれている点」が挙げられます。※1
一般的には通信量の抑制のために必要な措置とされています。論理的に同一サブネットを構成しつつも、物理的にはそうなっていないわけです。ブロードキャストやマルチキャストはパケットを1つ飛ばすとみんなに届くという、オンプレでは省エネな通信方式ですが、これを物理的にはバラバラな状況下で忠実に再現するのはあまり現実的な選択肢でないことも頷けるかと思います。(例：クラスBのローカルアドレスだと65534個に送信…!)

## Transit Gatewayのマルチキャスト機能
そんな中、登場したのが「[AWS Transit Gateway](https://aws.amazon.com/jp/transit-gateway/)のマルチキャスト機能」です。
「Transit Gateway」は、クラウドルーターと総称される機能群を提供します。
> Amazon VPC、AWS アカウント、オンプレミスネットワークを単一のゲートウェイに簡単に接続（引用：AWSのホームページのヘッダより）

その中で「マルチキャスト機能」は2019年の更新時に追加された比較的新しい機能です。当初は[IGMP](https://ja.wikipedia.org/wiki/Internet_Group_Management_Protocol)が使えず、マルチキャストグループに所属するEC2(それにアタッチしているENI)を、興味関心があるシステムとは別手動で管理せねばならず、使いづらいところがありました。
そんな中、2020年末にIGMPパケットへのサポートが発表され、2021年4月には、東京リージョンで使えるようになっていました。
[東京リージョンに2つのAWS Transit Gateway アップデート： AWS Transit Gateway Connect と Internet Group Management Protocol \(IGMP\) Multicast \| Amazon Web Services ブログ](https://aws.amazon.com/jp/blogs/news/transit-gateway-2updates-tokyo/)

## IGMP(Internet Group Management Protocol)
IGMPについての解説の詳細は割愛するが、ROS2との関わりという視点で、本記事と関連がある部分を少しだけ見ていきます。
ROS2で一般的なDDS実装である、[FastDDS](https://fast-dds.docs.eprosima.com/en/latest/index.html)及び、[Cyclone DDS
](https://github.com/eclipse-cyclonedds/cyclonedds)では、Multicast-UDPモードで動作する場合、IGMPパケットを送出してから通信を開始していることを確認できます。

![aws-tgw_Fig1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/d856c83c-7994-43a8-bdd0-1905d14b5ba3.jpeg)
（図1 `ros2 run demo_nodes_cpp talker`パケットキャプチャ結果 - FastDDS(Multicast-UDP)）

2021年現在売られているスイッチングハブでは「IGMPスヌーピング機能」が有効化されている機種が非常に多くあります。これらは、IGMPパケットを盗み見（スヌーピング）して、どの送信元がどのマルチキャストグループに属しているかを、スイッチングハブ側でも把握することで、マルチキャストパケットの送信先を絞っています。やはりこちらも、Transit Gateway同様に不要な通信で帯域を無駄遣いしないための工夫です。※2

## 構築手順
[公式のドキュメント](https://docs.aws.amazon.com/ja_jp/vpc/latest/tgw/tgw-multicast-overview.html)はこちら。AWS上で図2のような簡単な環境を作って、ROS2のPub/Subを試してみましょう。なお、制限については公式ドキュメントに詳しく載っていますが、特に、Amazon EC2は[ニトロシステム](https://docs.aws.amazon.com/ja_jp/vpc/latest/tgw/tgw-multicast-overview.html#limits)に対応したインスタンス（t3、c5、m5など）を利用しなければならない点にご注意下さい。※3

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/63d1099b-f2c1-3e86-7f8c-f115aa5f16d3.jpeg" width="480px">
（図2 AWS構成図）

これはいわゆる、「踏み台」を使った要塞構成と呼ばれるもので、AWSでもクイックスタートでおすすめしている風の構成です。詳しい構築方法はこちら。[AWS での Linux 踏み台ホスト \- クイックスタート](https://aws.amazon.com/jp/quickstart/architecture/linux-bastion/)
また、各EC2のOSがUbuntu20.04、ROS2 Foxyがインストールされているものとします。
ここでは、Transit Gatewayやセキュリティグループの設定を見ていきます。

### Transit Gatewayの設定
1. AWSの「コンソールのホーム」→「VPC」、左側メニュー「Transit Gateway」→「Transit Gateway」の順に進み、Transit Gatewayの設定画面を表示し、「Transit Gatewayを作成」ボタンをクリックします。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/551fce64-c415-25b9-0750-bc90524c627e.jpeg" width="380px">  
（図3 VPC画面の左側メニューのうちTransit Gateway部分を抜粋）
2. 「Transit Gatewayを作成」画面では、任意の名前タグ（ここではros2-mudp）を付与し、「Transit Gatewayを設定」→「マルチキャストサポート」チェックボックを有効にして、画面下部の「Transit Gatewayを作成」ボタンをクリックします。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/47f0c4cc-2aed-963f-8d34-81c91cef0603.jpeg" width="480px">  
（図4 「Transit Gatewayを作成」画面）  
3. 「Transit Gateway」の一覧画面に遷移するので、左側メニュー「Transit Gateway アタッチメント」→「Transit Gateway アタッチメントを作成」ボタンをクリックします。
4. 「Transit Gateway アタッチメントを作成」画面-詳細ブロックでは、任意の名前タグ（ここではros2-mudp）、「Transit Gateway ID」セレクトボックスに対して先に作った「Transit Gateway ID」を指定し、「アタッチメントタイプ」セレクトボックスがVPCとなっていることを確認してください。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/58418787-69b9-2cd5-28da-c77ae85d72bb.jpeg" width="480px">  
（図5 「Transit Gatewayアタッチメントを作成」画面）  
5. 「Transit Gateway アタッチメントを作成」画面-VPCアタッチメントブロックでは、今回のターゲットとなるROS2が載ったEC2が所属する、VPCやそのサブネットを指定して下さい。詳細・VPCアタッチメントの各ブロックの入力内容に問題ないことを確認して、画面下部の「Transit Gateway アタッチメントを作成」ボタンをクリックします。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/a4dc706c-9928-b982-9682-e737d1c8f930.jpeg" width="480px">   
（図6 「Transit Gatewayアタッチメントを作成」画面）  
6. 「Transit Gateway アタッチメント」の一覧画面に遷移するので、左側メニュー「Transit Gateway Multicast」→「Transit Gateway マルチキャストドメインを作成」ボタンをクリックします。
7. 「Transit Gateway マルチキャストドメインを作成」画面では、任意の名前タグ（ここではros2-mudp）を付与し、「Transit Gateway ID」セレクトボックスに対して先に作った「Transit Gateway ID」を指定し、「Transit Gateway マルチキャストドメインを設定」→「IGMPv2 のサポート」チェックボックスを有効にします。入力内容に問題ないことを確認して、画面下部の「Transit Gateway マルチキャストドメインを作成」ボタンをクリックします。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/296be061-6ae3-5851-4c5d-6fad857ae05d.jpeg" width="480px">  
（図7 「Transit Gateway マルチキャストドメインを作成」画面）  
8. 「Transit Gateway マルチキャストドメイン」の一覧画面に遷移するので、今回作成したマルチキャストドメイン（ros2-mudp）を選択し、下側メニュータブの「関連付け」タブをクリックして下さい。タブ画面が遷移した後に表示される「関連付けを作成」ボタンをクリックしてください。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/27e82a15-7785-2a51-ff70-86c23e7d935f.jpeg" width="480px">   
（図8 マルチキャストドメインの関連付けを作成する）  
9. 「関連付けを作成」画面では、「関連付けるアタッチメントを選択」セレクトボックスでは、先に作成したものを、「関連付けるサブネットを選択」セレクトボックスでは「Transit Gateway アタッチメント」と同様に予め作っておいたサブネットを関連付けます。入力内容に問題ないことを確認して、画面下部の「関連付けを」作成ボタンをクリックしてください。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/817b7f74-aa09-3f57-19cd-080a94c0fee0.jpeg" width="480px">  
（図9 「Transit Gateway マルチキャストドメイン - 関連付けを作成」画面）

### ネットワークACL、セキュリティグループ
[マルチキャストのルーティング \- Amazon Virtual Private Cloud](https://docs.aws.amazon.com/ja_jp/vpc/latest/tgw/how-multicast-works.html)を参考に、自身のセキュリティポリシーに応じて、適切なルールを宣言する必要があります。Transit Gatewayと同様に、AWSの「コンソールのホーム」→「VPC」、左側メニュー「セキュリティ」より設定可能です。
ひとつ、つまずきやすいポイントとしては、セキュリティグループの「ソース」はネットワークIPもしくはCIDRで指定する必要があるようです。（セキュリティグループ指定はできません。）


### EC2でIGMPの設定を変更する
[IGMP 設定を管理する](https://docs.aws.amazon.com/ja_jp/vpc/latest/tgw/working-with-multicast.html#multicast-configurations-igmp)には以下の通りの記載があります。
> EC2 のデフォルトの IGMP バージョンは IGMPv3 です。すべての IGMP グループメンバーのバージョンを変更する必要があります。以下のコマンドを実行できます。
> sudo sysctl net.ipv4.conf.eth0.force_igmp_version=2

ということで、コンソールで各EC2にログインし、上記コマンドを実行しておいて下さい。

## 動作確認
では、実際にROS2のデモおなじみの「demo_nodes_cpp」を動かしてみましょう。ROS-Baseとかでインストールした方は、おそらくインストールされていないので、お試し頂きたい場合は、以下コマンドで追加することも可能です。

```bash
sudo apt install ros-foxy-demo-nodes-cpp -y
```

では、こちらが実際に動かした様子です。ここでは`RMW_IMPLEMENTATION`は特に指定しません。（＝デフォルトのFastDDSを使う）
端末A

```bash
ros2 run demo_nodes_cpp talker
```

端末B

```bash
ros2 run demo_nodes_cpp listener
```

![2c1ab24cb2a94200ba0bd062ac773293.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/b70faae6-170b-f126-ba3e-c2fa3145d8d7.gif)
（図10 ROS2のデモプログラムによる動作確認）

この時、「Transit Gateway マルチキャストドメイン」の画面で、今回作成したマルチキャストドメイン（ros2-mudp）を選択し、下側メニュータブの「グループ」タブをクリックして下さい。すると、FastRTPSのデフォルトのマルチキャストグループである「239.255.0.1」によるグループが作成されていることが確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/b6a80943-e6fa-13e3-01bf-2cf00eaf3401.jpeg" width="640px"> 
（図11 AWSのコンソールでマルチキャストグループを確認する）

ちなみに、「239.0.0.0/8」に該当するアドレスはプライベートスコープとして定められたアドレスです。RFC2365によって組織内で使用するためのプライベートアドレスとして割り当てられています。

## ベンチマーク
ここでは、iperfを使って軽く実行したパフォーマンス測定結果を掲示します。頻度、データサイズ、インスタンスを変えた場合、同一AZ、オンプレとの比較をして考察を添えるべきかと思いますが、時間切れになってしまいました。。。。
データ無しで語る雰囲気としては、TransitGatewayを介しているからといってAWSのネットワーク性能が落ちている訳ではないように感じます。また、AZを跨いだ厳しい測定条件にも関わらずレイテンシーが3σで2〜5msで収まっているのはさすがだなというところ。
数字として今回は取れていませんが、Discoverが少し遅い点は少し気になる人もいるかもしれません。（実機でやっているときより2〜3秒遅いという肌感覚）

### 条件
- 東京リージョン（ap-northeast-1bと1c）
- EC2インスタンス：t3.medium
	- 2vCPU（Xeon Platinum 8000　 ブーストで@3.1GHz）
	- 4GiB
	- ネットワークバースト幅：5Gbps

### 1Hz - 20Mbpsを5並列
#### 基本統計量

|Latency_avg| |Latency_min| |Latency_max| |Latency_stdev| |Jitter| |
|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|
| | | | | | | | | | |
|平均|3.535268041|平均|3.491902062|平均|6.062958763|平均|0.135654639|平均|0.02357732|
|標準誤差|0.00964557|標準誤差|0.00933292|標準誤差|0.117311208|標準誤差|0.00733046|標準誤差|0.001129145|
|中央値 （メジアン）|3.5295|中央値 （メジアン）|3.4945|中央値 （メジアン）|6.322|中央値 （メジアン）|0.1205|中央値 （メジアン）|0.019|
|最頻値 （モード）|3.467|最頻値 （モード）|3.559|最頻値 （モード）|3.921|最頻値 （モード）|0.029|最頻値 （モード）|0.019|
|標準偏差|0.134347243|標準偏差|0.129992529|標準偏差|1.633956059|標準偏差|0.10210149|標準偏差|0.015727165|
|分散|0.018049182|分散|0.016898058|分散|2.669812402|分散|0.010424714|分散|0.000247344|
|尖度|-0.791325596|尖度|-0.770579324|尖度|-1.133331214|尖度|0.386623598|尖度|5.888190464|
|歪度|0.005190191|歪度|-0.063748029|歪度|0.173594446|歪度|0.931138478|歪度|2.262671108|
|範囲|0.527|範囲|0.527|範囲|5.808|範囲|0.432|範囲|0.084|
|最小|3.278|最小|3.222|最小|3.666|最小|0.019|最小|0.006|
|最大|3.805|最大|3.749|最大|9.474|最大|0.451|最大|0.09|
|合計|685.842|合計|677.429|合計|1176.214|合計|26.317|合計|4.574|
|標本数|194|標本数|194|標本数|194|標本数|194|標本数|194|
|最大値(1)|3.805|最大値(1)|3.749|最大値(1)|9.474|最大値(1)|0.451|最大値(1)|0.09|
|最小値(1)|3.278|最小値(1)|3.222|最小値(1)|3.666|最小値(1)|0.019|最小値(1)|0.006|
|信頼区間(99.0%)|0.02509336|信頼区間(99.0%)|0.024279987|信頼区間(99.0%)|0.305190097|信頼区間(99.0%)|0.019070503|信頼区間(99.0%)|0.002937518|


#### レイテンシー
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/6733f35c-b7f9-f94d-8bf8-4926753b45f4.png" width="640px"> 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/393b3eb4-5afb-ba9f-6460-2b87d47496ef.png" width="640px"> 

#### ジッター
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/654df910-e27d-a771-9c9f-a1822d386ef4.png" width="640px"> 


### おまけ：[iRobot ROS2 performance evaluation framework](https://github.com/irobot-ros/ros2-performance)
一応計測しましたが、同一マシン上でのパフォーマンステストではTransitGatewayを介さないっぽいですね。`FASTRTPS_DEFAULT_PROFILES_FILE`環境変数で指定したプロファイルXMLを使って[SHMを無効化](https://answers.ros.org/question/370595/ros2-foxy-nodes-cant-communicate-through-docker-container-border/)して測定したのですが、それにしては速すぎるような。（よく分かっていないのでオマケ扱い。この辺の詳しい情報をお持ちの方がおられましたら教えて頂ければ幸いです。）

```bash
$ FASTRTPS_DEFAULT_PROFILES_FILE=fastrtps-profile.xml ./irobot-benchmark topology/sierra_nevada.json --ipc off
〜略〜
node           topic          size[b]   received[#]    late[#]   too_late[#]    lost[#]   mean[us]  sd[us]    min[us]   max[us]   freq[hz]  duration[s]
lyon           amazon         36        501            0         0              0         86        63        46        902       100       5
hamburg        danube         8         501            1         0              0         92        181       45        3506      100       5
hamburg        ganges         16        501            0         0              0         62        49        38        935       100       5
hamburg        nile           16        501            0         0              0         71        46        43        851       100       5
hamburg        tigris         16        500            1         0              0         105       104       49        2074      100       5
osaka          parana         12        501            0         0              0         74        108       44        1662      100       5
mandalay       danube         8         501            1         0              0         113       187       63        3649      100       5
mandalay       salween        48        50             0         0              0         79        36        50        305       10        5
ponce          danube         8         501            1         0              0         124       188       68        3663      100       5
ponce          missouri       10000     50             0         0              0         113       198       63        1430      10        5
ponce          volga          8         10             0         0              0         78        59        43        245       2         5
barcelona      mekong         100       10             0         0              0         82        8         62        92        2         5
georgetown     lena           50        50             0         0              0         107       210       58        1556      10        5
geneva         congo          16        50             0         0              0         77        25        48        196       10        5
geneva         danube         8         501            1         0              0         132       189       74        3675      100       5
geneva         parana         12        501            0         0              0         92        112       55        1711      100       5
arequipa       arkansas       16        50             0         0              0         111       277       54        2043      10        5

Publishers stats:
node           topic          size[b]   received[#]    late[#]   too_late[#]    lost[#]   mean[us]  sd[us]    min[us]   max[us]   freq[hz]  duration[s]
montreal       amazon         36        0              0         0              0         19        12        6         107       100       5
montreal       danube         8         0              0         0              0         30        159       5         3419      100       5
montreal       ganges         16        0              0         0              0         13        9         3         98        100       5
montreal       nile           16        0              0         0              0         16        11        3         108       100       5
lyon           tigris         16        0              0         0              0         24        24        3         203       100       5
hamburg        parana         12        0              0         0              0         16        13        5         118       100       5
osaka          salween        48        0              0         0              0         16        11        5         64        10        5
mandalay       missouri       10000     0              0         0              0         21        13        13        92        10        5
ponce          congo          16        0              0         0              0         16        8         9         39        10        5
ponce          mekong         100       0              0         0              0         17        2         12        21        2         5
barcelona      lena           50        0              0         0              0         16        14        6         107       10        5
georgetown     volga          8         0              0         0              0         16        16        5         64        2         5
geneva         arkansas       16        0              0         0              0         14        7         6         53        10        5

System total:
received[#]    mean[us]  late[#]   late[%]   too_late[#]    too_late[%]    lost[#]   lost[%]
5279           95        5         0.09471   0              0              0         0
```


## 制約事項
要約で記した通り、「マルチキャスト・パケット転送」がカーネルビルド時に無効オプションがデフォルトとされており、それに起因してDockerなどの仮想ネットワークから本機能を使うことが基本的には出来ないようになっています。Dockerから使う場合は「hostネットワーク」を有効にして、ホスト側のネットワークに頼るというような対応を取る必要があります。
あくまでも、コンテナにこだわる場合はKubernetesでUDPマルチキャストに対応したクラスターネットワーク（例えば、[WeaveNet](https://www.weave.works/docs/net/latest/kubernetes/)）を使うという手もあります。ただし、現状でAWSのマネージドKubernetesサービスである[Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/jp/eks/)でWeaveNetを使うと、マネージドな有り難みを9割方スポイルしてしまうという問題もあります。
んー難しい。。。。

## おわりに
面倒・制約が残るものの、AWSのネイティブなネットワーク（トンネリング技術を使わないという意味で）でROS2(FastDDS-Multicast-UDP)を使ったPub/Subが行えることが分かりました。

- ユニットテストやシステムテストをAWS上で実行する
- ROSBagとかをデータ耐久性の高いクラウドストレージでバックアップする

などに使えないだろうか！？
また、EC2のARMプロセッサーのインスタンスである[Gravition2シリーズ](https://aws.amazon.com/jp/ec2/graviton/)のコスパが物凄いことになってきています。
> x86のインスタンスと比較して40％の価格性能比

先日のre:Invent2021ではGravition3も発表されており、クラウド高いなーとも言えない状況が出来つつある気がしました。ロボット系の皆様に於かれましては、クラウドも含めてオールARMプロセッサーっていうこともありうるのでは！？と思ったりもしています。

と妄想を膨らませつつ記事を締めたいと思います。

---
## 補足
※1 公式ドキュメントでその旨が「主題として」書かれているドキュメントを探すことが出来ませんでした。以下のような感じでさらっと書いてあるところとかは見つけましたが。この制限は暗黙の了解だったのでしょうか。
[VPC とサブネット \- Amazon Virtual Private Cloud](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/VPC_Subnets.html#vpc-subnet-basics)
> 10.0.0.255: ネットワークブロードキャストアドレスです。VPC ではブロードキャストがサポートされないため、このアドレスを予約します。

※2 [マルチキャスト \- IGMPスヌーピングとは](https://www.infraexpert.com/study/multicastz12.html)

※3 NITROについてはメチャクチャ詳しい記事がありました。
[AWS Nitro とは何かを理解する \| Powering next\-gen Amazon EC2: Deep dive on the Nitro System \#CMP301 \#reinvent \| DevelopersIO](https://dev.classmethod.jp/articles/reinvent-2020-report-cmp301/)!