工場長のブログ

日々思ったことを書いてます。

SORACOMで構成できるネットワークとNAT越えのさまざま

この記事では、SORACOM Air for セルラーやSORACOM Arcで接続されたデバイスクラウドアプリケーションの間の通信について考えてみます。

なお、わたしはこれを書いている2021/12/16現在、株式会社ソラコムで働いていますが、この記事内に出てくるソラコムの仕様についてはすべて外部から観測可能な話に基づいて書いています(のはず)。

ちなみにこの記事は株式会社ソラコム Advent Calendar 2021の19日目の記事です。

バイスクラウドの通信にはどんなものがある?

IoTアプリケーションにおける通信のユースケースとしては以下のようなものがあげられます。また、これらを組み合わせたアプリケーションも考えられるでしょう。

テレメトリ

バイスからクラウドに対してデータを送信する。スマートメーターのように数時間おきから数日おきに長いインターバルで定期送信するもの、ロボットの位置情報やステータスのように連続的に送信するもの、漏電監視装置のようにイベントドリブンで送信するものなど、流速や通信頻度は様々。情報の向きでいうとアップリンクです。

リモートアクセス

クラウドからデバイスに対してアクセスするパターンです。メンテナンスのためのSSHリモートデスクトップ、コマンドの発行などシグナリング的なものもこのユースケースに入ってきます。このユースケースは主体がクラウドアプリであり、クラウド(もしくはその向こう側にいるユーザー)のタイミングでデバイスにアクセスできる必要があり、ネットワーク的にはこれの実現方法がもっとも複雑になりがちです。(乱暴ではありますが)情報の向きで言うとダウンリンクです。

 

では、これらの通信が、SORACOMのネットワーク内で実際どのように実現されるか見ていきましょう。

SORACOM Air for セルラー/SORACOM Arcのネットワーク構成

f:id:imai-factory:20211216115952p:plain

まず、SORACOM AirのSIMを挿したりSORACOM Arcでそのまま通信すると、上のようなイメージで外部と通信することになります。ポイントはデバイスにはプライベートIPアドレスアサインされること、インターネットとの通信の際にNATされることというところでしょうか。

テレメトリのようにデバイスからインターネットに向けた通信(と、TCPの場合そのもどり)についてはなんの問題もないですね。逆に、リモートアクセスのほうは、どうやってNATを越えるかというのが議論になってきますね。中身の話をするまえにSORACOMの取りうる構成をもう少し見ていきます。

VPGを利用した場合に取りうる構成

f:id:imai-factory:20211216120008p:plain

SORACOMにはSORACOM CanalやSORACOM Door、SORACOM Directと呼ばれる、外部ネットワークとプライベート接続するためのサービスがあります。上記の絵はSORACOM Canalを使って利用者のVPCとピアリングをした際のイメージです。VPCとの通信の際にもNATされることに注意してください。

つまりクラウドからデバイスIPアドレスは見えない

このNATを挟む構成のため、クラウド側からソケットを開いて通信をすることはできません。ここでリモートアクセスに関してNAT越えの工夫が必要になってきます。

 

なお、こういう書き方すると「NATしないほうが便利じゃん」という見え方をするかもですが、この構成になっていることでデバイスは「デフォルトで外部からアクセスできない」というsecurity by defaultになっていることをお忘れなく!

でも、クラウドからデバイスにデータ送信したいよ!

もちろんです。わかります。以下のような方法が考えられます。

方法1: デバイスからポーリング

例えばデバイスから1時間に1回、クラウドにHTTP Getリクエストを送信して、そのレスポンスで必要な情報を渡すという方式です。もちろん、HTTP Postによるテレメトリの送信のレスポンスに、という形であわせ技にしてもよいでしょう。

クラウド側の実装としてはデバイスIDでルックアップ可能な「あるべき姿」テーブルを保持しておく形になりますね。AWS IoT Coreの「デバイスシャドウ」と同じコンセプトです。

SORACOMの機能としてはメタデータサービスを活用することだけでこれが可能になります。デバイスから、取得済みの情報のバージョンやタイムスタンプを再度、メタデータ側に返させるようにしておくと、どこまでデータが同期されているかも管理できるようになりますね。

それ以外のやり方としてはSORACOM BeamSORACOM Funkを使うというやり方が考えられます。前者はSORACOM内部に配備されるリバースプロキシ、後者は同じくSORACOM内部に配備されるAWS Lambda等FaaSへのエントリポイントといった体のサービスたちです。

もちろん、外部サービスにデバイスから直接アクセスしにいってもらうのもOKなんですが、この場合デバイスをどうやって認証するのかというのが課題になってきます。SORACOM BeamやSORACOM Funkは、デバイスからのポーリングリクエストをSORACOM内で終端します。その際、回線情報をベースに端末を識別することができますので(これをもちろんアプリケーションで利用可能です)

これ、アナログなように見えますがIoTデバイス(とくにバッテリー駆動のもの)ではいちばんいい戦略だと思います。なぜなら、通信のタイミングをデバイス側で完全にコントロールできるので、それ以外の時間帯、モデムをスリープさせておくなどができるからです。

なお、SORACOMではこれをデバイスリードパターンと呼んでいます。

方法論2: TCPソケット、WebSocket、MQTT、WebRTCなど(いろいろレイヤは違うけど)プロトコル自体にNAT越えの能力が備わっている通信方式を使う

TCPのraw socketとかWebSocketとかMQTTとか話が散らかってるのでそれぞれ個別に解説してみます。

TCPソケット

ソケットプログラミング、お好きですか?(わたしは好きです)

バイスからクラウドアプリケーション上のエンドポイントに対してTCPセッションを確立し、それをソケットとして利用することによってデバイスから、クラウドからどちらからの通信も可能になります。昔からある手法ですが、実際いまも有効活用されている手法ですね。

ただ、もしいまから新しくアプリケーションの設計をするのであればこの方法はあまりおすすめしません。なぜなら、あまりにも「生」だから、といったらいいでしょうか。例えばメッセージの電文フォーマットの定義(STXとETXを使うとかみたいなところ。JSONでやろうとかそのもっと前段)を決めなければいけなかったり、セッション切断時の復旧方法なども自分で決めて実装をしていかなければなりません。既存の仕組みを利用しなければいけないようなケースでない限り、後述のプロトコルをつかったほうがよいでしょう。

WebSocket

これも比較的低レイヤなプロトコルなのと、Webとついてるくらいなのでブラウザ環境があるならこれを選ぶのもありかなと思うのですが、その前提をもたないIoT向けデバイスの場合、わざわざWebSocketを使わなくてもいいかなという気がします。選ぶべき場面としては、(厳密にはWebSocketとイコールではありませんが)socket.ioベースのサービスに接続したい、といったようなときでしょうか。

MQTT

IoTといえばMQTT。なぜなら軽量だから!

といった謳い文句を聞いたことがある方も多いと思います。そして、その宣伝ぽさや受け売り感に若干うんざりしてる方も多いかもしれません。ただ、わたしもIoT向けのメッセージングベースの双方向プロトコルという観点で言えば、MQTTがいちばん無難かなと思います。理由としては軽量だからAWS IoT CoreやGoogle IoT Coreなど、マネージドサービスとして提供されているMQTTブローカーの選択肢が豊富だからというところになります。また、TCPソケットやWebSocket(最近ぜんぜん触ってないのであんまりキャッチアップできてないですが)よりも高レイヤなプロトコルであり、メッセージのQoSやコネクションの維持(キープアライブや復旧)に関しての取り決め、そしてそれらの実装がそろっていることなども挙げられます。

なお、AWS IoT CoreやGoogle IoT CoreはMQTTブローカーを提供しますが、MQTTの機能だけを提供するものではなくMQTTをトランスポートレイヤに利用する、メッセージングやデバイス管理のためのサービス群と捉えるのが正しいかなと思います。つまり、生のMQTTクライアントで接続するだけだとその恩恵の一部しか享受できないのでご注意ください。これらのサービスを使うのであれば各プロバイダが提供しているSDKを利用するのがおすすめです。

なんていう話をして舌の根も乾いてないところですが、実際にはAWS IoT CoreをMQTTブローカーとして使えればいいというケースも多くあると思います。実際、AWS IoT Coreってマネージドで可用性高くて、どこからでもセキュアにメッセージをPublishしたりできるし、ConsumerとしてLambdaをぶら下げたりできる、ひじょーーーうに優秀なMQTTをブローカーですよね。SORACOMの場合、SORACOM Beamを使うと簡単かつセキュアにAWS IoT Coreにデバイスを接続できますよ!

WebRTC

すごい強引ですがここでWebRTCも。というかこの記事、本当はUDPのNAT越えについて書きたかった(だけ)なんですが、DeNAさんのアドベントカレンダーゲームでよくある「NATタイプ」はどう判定しているの?という素晴らしい記事が公開されているので、少し話をずらした結果がこの記事なんですw

WebRTCは主に音声や映像のP2P転送のための技術(文脈によってはサービス)として注目されていますが、ほかにもデータチャネルというメッセージ送受信のための仕組みもその仕様の中に含まれています。

ちなみに、SORACOM Air for セルラー上でWebRTCを使った映像の送信については2年前のSORACOMアドベントカレンダーで、WebRTCでロボット操縦しようぜ!というタイトルでAmazon Kinesis Video Steamを使って試してみています。なお、SORACOM Air for セルラーはSTUNによる通信はできず、TURNが利用されていました(つまりAddress Dependant NATもしくはAddress And Port Dependant NATを利用している)。

また上記の記事内にて言及がありますが、Amazon Kinesis Video StreamsのサービスにはSTUNとTURNも含まれていますので、MQTTと同じ用にマネージドなブローカーが出揃ってきているという状況になっていることも言及しておきます。

(ググっていたら見つけた、ejabberdの会社が開発をリードしているerlangで書かれたstun/turn、余裕があればコードリーディングしようと思っていましたがちょっと時間切れなのでまた今度)

方法論2についてのまとめ:

いろいろ紹介してきましたが、いまのところプロトコルの理解の容易さとマネージドサービスを介して利用できること、またSDKやライブラリの出揃い具合の観点からMQTTが一番扱いやすいと言えると思います。

方法論3: IPレイヤで双方向にルーティング可能にする

これ、見た目上シンプルに映ることも多いのですが、実際にはいちばん面倒です。

IPレイヤでルーティング: L2

今回の例ではカスタマーVPCの中にEC2があって、そこからデバイスに対して通信をしたいとしましょう。NATがあるのでそのままでは直接通信はできません。

f:id:imai-factory:20211216161913p:plain

ここでSORACOM Gateというサービスが登場してきます。このサービスはオーバーレイネットワークを使ってデバイスサブネットをEC2のところまで延伸してくれます。絵にするとこんな感じです。SORACOM内に配備されているゲートウェイ(これをVPG、Virutal Private Gatewayと呼んでいます)からEC2までVXLANというプロトコルをトンネルとして使ってオーバーレイネットワークを作っています。(IPSecによるVPNに似てるとお考えいただいても大丈夫です)

f:id:imai-factory:20211216162433p:plain

なお、Gate Peerを冗長化するために下記のようにGate Peerを複数デプロイすることができます。アプリケーションレイヤの冗長化であればこれらをDNSラウンドロビンしてやるのが手っ取り早いでしょう(それでOKなアプリケーションプロトコルを利用していれば)。ネットワークレイヤでやるのであれば、VRRP over VXLANなんてのもありかもしれません(いまぱっと考えて思いついただけですが、生のEC2ネットワークではなく、VXLAN内なのでVRRPいけるかも、と思ってます。あとで試してみよう。)

f:id:imai-factory:20211216162622p:plain

また、すでに古文書の領域になりつつあるAWSデザインパターン(ほめてる)にあるFloating IP Addressデザインパターンも利用可能です。下記のイメージで、仮想IFを別ホストにハンドオーバーする感じです。具体的な手順としては、1. 仮想IFのダウンを検知 2. SORACOMのAPIを使って既存仮想IFをホストしているGate Peerを削除 3. 新しいGate Peerを登録 4. 新しいGate Peer上でVXLANトンネルを作成、という形です。Active/Standby方式なので多少のダウンタイムは発生しますが、確実に冗長性を確保できるひとつの方法です。

f:id:imai-factory:20211219153647p:plain

 

さて、ここまでお読み頂いていてお気づきの方もいらっしゃるかも知れませんが、Gate Peerはデバイスサブネット内に配置されているので同一L2ネットワーク内での通信をしています。既存のネットワークと接続する場合、このGate Peerをルーター的に利用して、その向こう側にあるサーバーたちと通信させたくなります・・・よね?

実はこれには技術的なハードルがあります。これについては次のパートで解説します。

IPレイヤでルーティング: L3

L3でのルーティングとは、言い換えるとNATなしで外部ネットワークと通信したい、ということだと捉えてもらえればと思います。(伝わるかは自信ありませんが・・・)絵にすると以下のようなイメージです。強い意志をもってNATを乗り越えるのです!

f:id:imai-factory:20211219160507p:plain

技術的なハードルについてはこちらのSORACOM公式ブログのポスト「SORACOMで拡張する企業ネットワークの構築例」 に解説がありますので、そちらを併せて呼んでみていただければと思いますが、デバイスから送信されたパケットは一旦必ずSORACOMのゲートウェイ(VPG)に吸い込まれます。ここで宛先がデバイスサブネットローカル(この例では10.0.0.0/9)だった場合のみそのままL2のままフォワーディングされますが、それ以外の宛先の場合L3でルーティングされます。以下のような感じ。

f:id:imai-factory:20211219160103p:plain

この制約をうまく解決してくれるのがSORACOM Junctionのリダイレクションです。この機能は、予め設定した転送先のIPアドレスに対して「すべての」パケットを「フォワード」してくれます。すべての、ということはインターネット向きのパケットも対象になることに注意しましょう。またL2でのフォワードであって、L3でのルーティングではありませんので、フォワード先に指定できるのはあくまでデバイスサブネットの空間内のIPアドレスに対して、ということになります。今回の場合、下記のようにSORACOM GateのGate Peer(10.0.0.100)にフォワード先を設定することによって、ついに念願のNATなしのルーティングが実現しました!

f:id:imai-factory:20211219162632p:plain

なお、この構成を冗長化するには、先程のFloating IP Addressデザインパターンを使うのがよいでしょう。Gate Peerをハンドオーバーする際、SORACOM Junctionのフォワード先の設定も一緒に変えてやるかんじです。

方法論3についてのまとめ

と、こんなかんじにIPレイヤで冗長性をもったルーティング構成を構築することもできるようになっています。

まとめと所感

ということでSORACOMで取りうるアプリケーションとデバイスを接続するためのネットワーク構成のパターンを思いつくまま(たぶんだいたい網羅してる)に紹介してきました。

じゃあどう選べばいいのよ?という声が聞こえてきそうですがわたしの意見としては、IoTの文脈においては可能な限りデバイスから生成したソケットのなかで双方向通信を行うHTTPポーリングやMQTTを使うべきと考えます。理由は2点

  1. ネットワークインフラのレイヤでIPリーチャビリティ(今回の方法論3)を確保しようとすると、まさに「インフラ」をマネージする必要が出てくること。費用面だけでなくて、運用の手間も増える。
  2. クラウド側の都合のいいタイミングでソケットを開ける状態にしておくということは、デバイス側は常にIP通信が行える状態を保持するため、3G/LTEを常にアクティブな状態にしておく必要があり、(バッテリーで稼働するデバイスの場合、)消費電力に大きな影響を与えること。

この辺の、通信プロトコルやルーティングの選択がデバイスのバッテリーに効いてくる、みたいなところはIoTっぽさがありますね。