NOT A HOTEL のスマートホームのシステムを支えるインフラ
こんにちは。NOT A HOTEL でソフトウェアエンジニアをしているきんちゃん。(@wa_kinchan) です。
今回、この note では、2020年末から徐々に設計・開発、2022年10月に初めてリリースをした、NOT A HOTEL のスマートホームのネットワーク・インフラ・バックエンド目線でのシステム開発について、エンジニア向けに、お話できればと思います。
わたしの担当は、何でも係りで、拠点ネットワークから、バックエンド、当初は iOS までスマートホーム全般見ております。その目線から書きたいと思います。
前回の note がボリュームが大きめですが、スマートホームの過程と開発における裏側のについて、投稿しました。前記事から読むとより理解が進むかと思います。
エモい話になっていて、結構面白い内容になっていると思うのでぜひ読んで頂きたいです。
スマートホームの Cloud Run サービス群
本題に入る前に、Cloud Run サービスと、ホームコントローラーの流れを説明してから移ります。
NOT A HOTEL では、インフラは GCP で構成されており、Go でアプリケーションを実装し、Cloud Run にデプロイして API を提供しています。
スマートホーム向けのサービスが10個以上あり、主要ないくつかを 紹介します。
smarthome-app-api
ホームコントローラー for iPad アプリ向け
gRPC で API を提供するサービス
main-app-internal-api
メイン iOS・Web 予約アプリ用の Main API を呼ぶ内部向け API を提供するサービス
smarthome-receiver-apigw
現地の KNX ゲートウェイサーバーから状態を受け付けるサービス
gRPC で API を提供するサービス (また、そのサービスを HTTP でバインドしているゲートウェイのサービス)
smarthome-app-task
スマートホーム用の非同期に Cloud Task でタスクを実行するためのサービス
ホームコントローラーのまとめて操作や、アラーム時の非同期的に KNX ゲートウェイサーバーへリクエストを投げる際に利用する
ホームコントローラーの流れ
ホームコントローラーからの制御の流れの全体像は下記のようなイメージです。
便宜上ここでは、各設備・機器のことをデバイスと呼びます。
KNX は、デバイスの制御(コマンド)と、デバイスの状態監視(フィードバック)の2種類あります。
制御の流れについてです。
照明をつけるなどの制御を行う際は、iPad から Cloud Run 側 (smarthome-app-api) に VPN を利用してリクエストを投げます。
そのリクエストに応じて、デバイスを特定して、拠点の KNX ゲートウェイサーバーにリクエストを投げます。
KNX テレグラム(KNX の伝送情報) に変換して、物理 KNX ケーブル等の媒体を通して、各デバイスが動作します。
状態監視の流れについてです。
メーターの各デバイスが媒体を介して、状態が変わると KNX テレグラムが発火されて、拠点の KNX ゲートウェイサーバーが受け取ります。
KNX ゲートウェイサーバーは、値変化をトリガーに VPN を利用して変化した状態を Cloud Run (smarthome-receiver-apigw) に伝えます。
結果を加工して Cloud Firestore へ格納します。
ホームコントローラー iPad は Cloud Firestore SDK を利用してリアルタイムに現在状態の描画を行います。
第一形態: YAMAHA RTX1220 - HA VPN 構成
ネットワーク、第一形態です。
前述で「VPN を利用して」と、書いた部分の、ネットワーク部を掘り下げます。
拠点のオンプレ環境は極力少なくする方針で、原則クラウドに乗っける設計にしています。とはいえ、前回の note でも書いたような、現地のネットワークのインフラや、物理ゲートウェイの設計は必要です。
青島や那須等の初期の拠点の IPSec VPN 設計の話です。
拠点には、ヤマハの VPN ルーター RTX1220 が 2 台設置してあります。RTX と本番環境の GCP プロジェクトの Cloud VPN でピアリングしています。
また、アクセスポイントは、コスト面からも法人向けの BUFFALO 製品を選定しました。
ヤマハの VPN ルーターの IPSec VPN まわりの設定はシンプルで、公式のリファレンスほぼ同じ設定でワークします。
tunnel select 1
ipsec tunnel 1
ipsec sa policy 1 1 esp aes-cbc sha-hmac
ipsec ike version 1 2
ipsec ike always-on 1 on
ipsec ike encryption 1 aes-cbc
ipsec ike group 1 modp1024
ipsec ike hash 1 sha
ipsec ike keepalive log 1 on
ipsec ike keepalive use 1 on rfc4306
ipsec ike local address 1 {private-ip-v4}
ipsec ike local name 1 {global-ip-v4} ipv4-addr
ipsec ike nat-traversal 1 on
ipsec ike pfs 1 on
ipsec ike pre-shared-key 1 text {passphrase}
ipsec ike remote address 1 {gcp-cloud-vpn-gateway-ip}
ipsec ike remote name 1 {gcp-cloud-vpn-gateway-ip} ipv4-addr
ip tunnel address 169.254.0.2
ip tunnel remote address 169.254.0.1
ip tunnel tcp mss limit auto
tunnel enable 1
tunnel select 2
ipsec tunnel 2
...
VPN の接続方式についてです。
GCP には HA VPN 方式と、旧来の Classic VPN 方式があります。初期は、GCP が推奨している HA VPN トンネルの構成を取りました。
プロバイダの契約で IPoE 追加オプションを付けて、PPPoE と IPoE の2回線を固定のグローバル IP アドレスに対して、ピアリングを行っています。もしくは、IPoE を2回線契約しています。
IPSec SA Lifetime が 10800 秒(3時間)という寿命の制約があり、その都度、数秒程度の VPN 切断が起こるために、2 台のルーターを VRRP 構成で、それぞれ Master・Backup とし、VRRP トラッキングで即時フェイルオーバーさせる設計で実現しています。
HA VPN を使う構成は、現在も稼働していますが、第一形態としています。
HA VPN は推奨されているとはいえ、IPSec SA Lifetime の 10800 秒の寿命観点と、HAを組む上で、たとえば、IPoE の固定 IP を2回線用意する設計を行うとランニングコストが嵩みます。
また、IPoEとPPPoEで HA 構成にすると、10800 秒おきに PPPoE に変わったときに、速度が頭打ちになり、直接お客様のインターネット速度に影響します。そのような問題がある中、HAにするメリットとは?って、なりました。
第二形態: UniFi DM Pro - Classic VPN 構成
ネットワーク、第二形態です。
HA VPN 解消に向けた話をする前に、アクセスポイントの話をします。
前述で、第一形態では、法人向けの BUFFALO 製品のアクセスポイントを選定したとサラッと書きましたが、機能性 (Wi-Fi 5、通信速度に頭打ち) や、メンテナンス性 (WLS-ADT 等の監視システムが Windows 向けアプリケーションである)、意匠性 (筐体が大きい)、など、様々な問題点が出てきました。
ランニング等のライセンスフィーがかからず、コスパは良かったです。
そんなこともあり、ライセンスフィーが安価もしくは無料で、性能がよく、意匠性がある銀の弾丸的なアクセスポイントを探していました。Cisco や Aruba はもちろん性能も良いですが、イニシャル・ランニング両方が高く導入には至りませんでした。
そんな中、ネットワーク設計を行ってくれてる鬼フットワーク軽く、技術力高いネットワーク会社の方々が、UniFi を実際に試すと非常に良さそうと提案してくれました。
実際に試してみて、設計序盤から非常に感触もよく、アクセスポイント、スイッチ、ルータすべて UniFi の設計で組み直しました。その後、UniFi 製品設計で一拠点目いれたのですが、すべての課題面を払拭した形で導入ができました。
脱線しましたが、UniFi 構成では、Dream Machine Pro という、ルータを選定し、第一形態の懸念点の HA も払拭、Classic VPN で再設計することになりました。もちろん、IPSec SA Lifetime の制約も受けず、1つの IPoE の IP で VPN を貼り続けることになりました。IPoE が死んだときのフォールバックで PPPoE を切り替わる構成になっています。
これにて、アクセスポイント性能問題、HA VPN 問題が解消しました。
次になる気になるポイントは、拠点が増えていく連れて、拠点と Cloud VPN を貼ることになるため、線形でコストが増え続けることです。
下記の絵にあるように、最初は線形に増えたのはプレイスが一気に増え、現時点はプレイスの増加は止まっているのですが、今年(2023年)の末から、また、拠点が増えていきます。
この問題を解消するため、特定拠点からのみ Cloud VPN を貼るパターンの第三形態を考えています(プロキシパターン)。
それは、また実際に構築して、ワークして成功事例となったら第三形態のお話するとしましょう。
VPN 経由かつ閉域網で Cloud Run の接続の実現
次は、Cloud VPN の先の GCP 側の VPC の話をします。
弊社では、基本的に API は Cloud Run に建てています。GCE であれば、VPC ネットワークを指定すれば、簡単に双方向通信を実現できます。しかしながら、Cloud Run の場合 (App Engine Standard や Cloud Function 同様) は、サーバーレス VPC アクセスコネクタで、VPC に接続する必要があります。Cloud Run は VPC 外に実質いることになります。
スマートホームという悪意ある制御を防ぐためにも、サービス側の設定を Ingress を Internal に設定して、外界からのリクエストは受け付けず、VPC 内のみの限定アクセスを目指す形で進めました。
ただ、今回の構成の場合、単純に Cloud VPN に接続するだけでは、下り方向(サービス → 拠点ゲートウェイサーバー)は容易に VPN 経由で入れるのですが、上り方向(拠点ゲートウェイサーバー → サービス)の通信が VPN の経路を通らず、インターネットに出てから、サービスに到達してしまいます。そのため Ingres が Internal では動作せず、All だと HTTP Status 403 エラーが出ずに動作する状況でした。
その場合、Private Service Connect を利用して、Cloud VPC 経由で、オンプレミス環境から、Cloud Run を含む Google Cloud API を実行できる機能があり、利用しています。
手順としては以下のような流れです。
Subnet の限定公開の Google アクセスをオンに変更
Private Service Connect を対象の VPC を指定して作成
Private DNS の設定を、*.googleapis.com と *.run.app で作成 (ドメインオプション)
Cloud DNS のポリシーを対象の VPC を指定して、Inbound query forwarding を On で作成
Cloud Router の Advertised routes の Custom Ranges 設定で、Private Service Connect の IP アドレスを追加する
オンプレの YAMAHA ルーター側、もしくは、DNS サーバーで静的 DNS 解決を追加する
Run サービスの FQDN を指定して、ルーティングする URL 直指定で設定としました。今後、Run サービスが増えるたびに、既存プレイスに対しても、静的 DNS を追加する必要がありますが、そうそう追加するケースがないので、プレイスが多くなったら将来追加を自動化できるようにするなどで対応する方針です。
下記は YAMAHA の RTX1220 の設定 config です。Cloud Run の DNS 名と、Private Service Connect の IP アドレスを指定して、静的 DNS 解決しました。
dns static a https://{run-service-name}-{hash}-an.a.run {private-service-connect-ipv4}
その結果、Cloud VPN を介して、閉域網での Cloud Run のアクセスが実現できた。
Cloud Run Jobs を利用した実環境での検証
ネットワークの話が長くなったので、Run 側の機能を使ってます話にう取ります。
Cloud Run Jobs は、スマートホームの開発で相性が良く手動で実行する拠点の本番機器の動作チェックや、バッチ実行などで、プレビュー時代から利用しています。
用途は色々あるのですが、一番最初は、前述の VPN 経由での現地の機器を実際に動作確認する際に役に立ちました。
テレビのデバイス制御で、REST、SOAP、TCP の3種類の API を使い分けて実現しています。REST・SOAP は通常の API クライアントを作るだけだったので簡単でした。
TCP だけはコネクションを張った上で、コマンドを送信し、goroutine と channel で実装して特定のフィードバックが返ってきたら、切断する仕組みを実装する必要がありました。
ある程度機能を実装後、ローカルで、テレビのプライベート IP アドレスを直指定して、 go run で実行すれば確認はできたのですが、実拠点に設定したテレビでも Cloud Run を介して、動作チェックをする必要がありました。
サービスで一つ RPC を作り、動作チェックすることももちろんできましたが、本番の機器の動作チェックをするためには、本番環境の Cloud Run にデプロイが必須です。リアルなデモハウスみたいなものを作れればいいのですが、現時点は実利用される本番のハウスしか存在していないことも関係しています。
開発中のものを本番環境に反映してチェックしていくのは事故の可能性が高いため、本番のサービスとは別にサービスを作るか、本番環境の Jobs を作るかで、後者のほうが影響は範囲が少ないので、Cloud Run Jobs を採用しました。
現在は、Cloud Run Jobs 定常的に、シードの食わすバッチでも Jobs を利用しており、実行結果のログが残るため、前回の結果と、今回の結果を突き合わす必要があるなど、後から見直すときに非常に役に立っています。
今回は、ネットワーク・インフラよりの話になりましたが、多方面のソフトウェアエンジニア、様々な職種を募集中です。
また、2023/07/13に、「新時代のホテル運営システム(PMS)・スマートホームのゼロイチ開発舞台裏」と題した、イベントを開催しますしました。
話した内容はこちらです!
次回はもう少し、Go アプリケーション寄りの話をしたいなとも思います。
暮らしをつくるために、NOT A HOTEL のシステムたちは進化し続けます。一緒に作って行きた方募集中です!
NOT A HOTEL では、広く採用活動をしています。ご興味のある方は、ぜひ採用ページや Wantedly をチェックしてみてください。