HEROZ Tech Blog

日本将棋連盟公認「将棋ウォーズ」や、AIを活用したシステム企画・開発を行う、AI企業HEROZの公式テックブログです。

IoTゲートウェイの「設定リモート注入」で難航した記録

ハードウェア制約のあるIoTゲートウェイで「設定リモート注入」をどう設計したか — Greengrass を見送り、S3 + MQTT に着地するまでの判断軸

想定読者: AWS IoT を使ってエッジゲートウェイを組もうとしている開発者の方、特にベンダー提供の制約が強いハードウェアを扱われる方

キーメッセージ: ベストプラクティスらしきもの(Greengrass、Device Shadow など)を一通り検討しましたが、どれも完全には当てはまりませんでした。初期構築の構成として S3 + MQTT + 自作中継 に落ち着くまでの、判断の記録です。Greengrass / Shadow / S3 + MQTT の優劣ではなく、「何が判断軸だったか」を共有することを目的としています。


はじめに

IoTゲートウェイの開発をやっていると、ある時期から決まって同じ問題に当たります。

「現地に置いた機器の 設定 を、どうやって遠隔から更新するのか」

ファームウェアの更新(OTA)はベンダー側の純正機構があることが多く、そこは比較的素直に解けます。本記事で扱うのは、OTAでは粒度が粗すぎる、もっと細かい「設定」の遠隔更新のほうです。

具体的には、センサー較正値・ポーリング間隔・接続先機器の認証情報・AWS の一時クレデンシャルといった、「OS の世代を上げるほどではないが、現地派遣せずに変えたい」情報 のことです。

AWS IoT の世界には、このユースケース向けに見える機能が一通り揃っています。Greengrass V2 の Component UpdateDevice ShadowSSM Parameter Store / AppConfig といったところです。Webで「IoT 設定 リモート 更新」を検索すれば、これらを使った構成例がたくさん見つかります。

ところが、いざ本プロジェクトのハードウェア(マイクロプロセッサ搭載のIoTゲートウェイ機)に当てはめようとすると、どれもピタッとはハマりませんでした

動かないわけではありません。動かせることは確認した上で、運用に乗せたときの歪みが看過できなかった、というのが実態に近いです。

なぜそんなことになるかというと、エッジ側のハードウェア制約とAWS側のサービス前提が、思ったより細かいレイヤーで噛み合わないから です。

具体的には、ベンダー純正OTAの更新粒度が ルートFS丸ごと差し替え だったり、ホストOSのルートFSが 揮発(再起動で初期化) だったり、RAM が潤沢でなかったり、といった事情です。これらは AWS のドキュメントを読んでいる側からは想像しにくく、現場でハードウェアを触ってはじめて見えてきます。

本記事は、その 「ベストプラクティスがそのままハマらなかった現場での、見送り判断と着地点の記録」 です。

Greengrass / Shadow / S3 + MQTT のどれが優れているかという話ではありません。「ハードウェア制約と運用制約のもとで、何を判断軸にして選んだか」 を共有することが目的です。同じような制約を持つ現場の方の判断材料になれば幸いです。


TL;DR

  • マイクロプロセッサ搭載のIoTゲートウェイ機を使った案件で、設定・認証情報のリモート更新をどう実現するかで試行錯誤しました
  • 本命候補だった AWS IoT Greengrass V2 は、ベンダー純正OTA(SWU)との役割重複・揮発ストレージとの相性・SWU という粒度の粗いイメージ媒体との噛み合わせの悪さで見送りました
  • Device Shadow / SSM Parameter Store / AppConfig も候補に挙がりましたが、要件と合わず他手法に流れました
  • 初期構築の構成としては、S3 にJSON設定ファイルを置く → IoT Core MQTT で更新通知 → デバイス側の中継エージェントがダウンロードして配置 → フロー実行基盤がリロード という自作構成に着地しました(フロー実行基盤の現状は Node-RED。次期開発で別基盤への移行を再検討する想定)
  • 決まったベストプラクティスがなく、ハードウェアとクラウドサービスの板挟みの中で組み合わせを探すのが、エッジ設計の現実でした

図1: 検討した4候補の評価一覧です。Greengrass / Shadow / SSM はそれぞれ妥当な選択肢ながら、本機の制約(粗粒度OTA・揮発ルートFS)と噛み合いませんでした。本記事は左の3枚を順に外し、右の採用構成に着地するまでを追いかけます。


背景: なぜ「設定リモート注入」が要るのか

IoTゲートウェイは現場設置型ですので、一度設置したら簡単に触れません。ですが設定は変わります。

本プロジェクトで遠隔更新したい情報は次のとおりです。

  • センサー較正値(スケール下限/上限、換算係数)
  • ポーリング間隔(cron式)
  • 接続先デバイスの接続情報(多数のセンサー機器をぶら下げるため、IPアドレスや認証情報の集合が大きくなりがちです)
  • AWS一時クレデンシャル(S3アップロード用に定期更新が必要)
  • 機器種別のマッピング(同じGWでも接続される機器の構成が変わるケースがあります)

現地派遣なしでこれらを更新する仕組みが要件です。ここまでは普通の話で、問題はどう実装するかでした。


候補1: AWS IoT Greengrass V2

Greengrass V2 は、まさにこれをやるためのサービスに見えます。

  • Component Update: 設定やコードをクラウドから配信、段階的デプロイ
  • Token Exchange Service (TES): X.509証明書で一時AWS認証情報を取得
  • ローカル MQTT ブローカー: Component間のIPC
  • Shadow 連携: 状態同期
  • CloudWatch Logs 連携: ログ集約

設計書の初期版ではこれを前提に書いていました。「Component Update で OTA、TES で認証、Shadow で状態同期」という筋書きです。

見送りの決め手1: ベンダー純正 OTA との二重構造

結論から言いますと、ハードウェアベンダーが純正で提供しているOTA/監視サービスと Greengrass Component Update の役割が重なるのが大きな決め手でした。

具体的にはこうなります。

  • ベンダー純正: OS・コンテナ構成・アプリをA/Bパーティション方式の署名付きイメージで配信。失敗時は自動ロールバック。ベンダー提供の監視コンソールから状態が見える
  • Greengrass: Component 単位でクラウドから配信。バージョン管理・段階的デプロイ・ロールバックを Greengrass 側で持つ

この2つを併用すると、「OS更新はベンダー純正、アプリ更新は Greengrass」という役割分担を厳密に管理する必要があります。しかもどちらも管理コンソールが別物で、オペレーションが二重になってしまいます。

そもそもベンダー側の署名付きOTAが堅牢なため、Greengrass 側の段階的デプロイを使う動機が薄い、という事情もありました。

見送りの決め手2: 揮発ストレージとの相性

もう一つ大きかったのが、ホストOSのルートファイルシステムが揮発(read-only + tmpfs、再起動で初期化)である点との相性でした。

Greengrass は /greengrass/v2 配下にログ・デプロイ情報・アーティファクトを書き続ける前提で動作します。本機ではこれらを 再起動で消えない永続領域 にマッピングし続ける必要があり、Greengrass の想定するファイルレイアウトとはズレが生じます。

  • アーティファクト保管領域とログ書き込み先を別々に永続側へ逃がす設計が要る
  • 再起動のたびに揮発側から永続側へ復元するブートストラップを書く必要がある
  • Greengrass 自体の自動更新が、揮発側の差分を期待して挙動するケースで予測しづらい

見送りの決め手3: SWU という粒度の粗いイメージ媒体との噛み合わせ

ここまで「純正OTAとの重複」「揮発ストレージとの相性」を挙げましたが、ベンダー純正OTAの実体が SWU(SWUpdate 形式の署名付きアーカイブ)である点も、見送り判断に効いてきました。

SWU は OS・カーネル・ルートFS・コンテナイメージ・アプリケーションを 1つのアーカイブにまとめて A/B パーティションへ書き込む タイプの、配信単位がきわめて粗い更新媒体です。これは Greengrass の Component(クラウドから細粒度に配るアーティファクト)と、配信単位の粒度が根本的にズレます。

図2: SWU は「OS〜アプリまで丸ごと積み重なった一枚岩」、Greengrass Component は「クラウドから細かく配られる多数のブロック」を表現しています。面積で粒度の違いを見ていただくのが意図です。両者を併走させると、下段の3つの不整合(消える / 二重ソース / 世代割れ)が運用負担として効いてきます。

具体的に何が困るかと言いますと、

  • 更新粒度が「アプリ部品単位」と「ルートFS丸ごと」で 2系統になる Greengrass は Component 単位の差分配信を前提としますが、SWU は実質「全部入りイメージを差し替える」運用です。「軽い変更は Greengrass、重い変更は SWU」という二系統運用を許容する必要があり、どちらの経路でいまの状態に至ったかが追いづらくなります
  • SWU 更新で Greengrass のローカル状態が吹き飛ぶ/古いまま固まる ルートFSを丸ごと差し替える性質上、/greengrass/v2 配下を永続ボリュームへ逃がしておかないと、SWU 適用のたびに Greengrass のデプロイ状態が消えることになります。逆に永続側へ逃がした場合は、SWU 側で更新したはずの Greengrass 本体・nucleus バージョンが、永続側の古いデータと不整合になる懸念が出てきます
  • Greengrass 本体やデプロイ済み Component を SWU に焼き込むかどうかの判断が要る SWU に焼き込めばオフラインでも初期状態が再現されますが、Greengrass のクラウド側デプロイ管理と二重ソースになります。焼き込まずクラウドからの再取得に任せれば、SWU 適用直後はネットワーク復旧 + Greengrass 同期完了までフルに機能しない期間が発生します。どちらを選んでも歪みが出ます
  • ロールバックの主導権が SWU 側にある A/B パーティションの自動ロールバックが効くため、「SWU は前世代に戻ったが、Greengrass のクラウド側デプロイは新世代のまま」という状態が容易に起こり得ます。ロールバック時の整合性は SWU 側だけで完結させたい運用要件にとって、Greengrass のクラウド側状態を別途巻き戻す手間は割に合いませんでした

つまり、SWU は「粗いがその分シンプルで、署名・A/B・ロールバックまで一体化した強い更新媒体」であり、ここに Component 単位の細かい配信レイヤー(Greengrass)を重ねると、二つの異なる更新粒度を同期させるオーバーヘッドが支配的になります。

動かせることは確認した上で、次の3点を総合して見送りました。

  1. 純正OTA(SWU)と役割が重複し、運用が二重化する
  2. 揮発ルートFS という前提と Greengrass の想定ファイルレイアウトが合わず、永続側へのマッピング設計が窮屈になる
  3. 更新媒体の粒度が SWU(ルートFS丸ごと)と Greengrass(Component単位)で二重化し、状態管理のオーバーヘッドが運用負担として効いてくる

副次的に効いた要因として Java ランタイム(12〜60 MB 規模の常駐プロセス)が増えることも、RAM が潤沢ではない環境では無視できませんでした。

なお、机上判断ではなく実機での nucleus インストール・最小Component デプロイまで動作確認した上での、運用面での見送りです。


候補2: AWS IoT Device Shadow

Shadow は desired / reported で状態同期する仕組みで、設定リモート注入のユースケースに使われる代表例です。本プロジェクトでも、設計初期にかなり真剣に載せ替えを検討しました。

検討の過程で見えてきた、外す決め手になった点を順に挙げます。

  • secrets を載せていいものか問題 対象データに secrets(パスワード)や接続先機器の認証情報が含まれます。Shadow ドキュメントは IAM ポリシーで保護されるとはいえ、「状態同期」のための領域に認証情報が常駐するモデル自体に心理的抵抗がありました
  • ペイロード上限の頭打ち Shadow のペイロード上限は Classic で 8KB、Named でも 30KB です。本プロジェクトは 1台のGWに多数のセンサー機器をぶら下げる前提のため、機器ごとの接続情報・較正値・マッピングを束ねると、近い将来に上限に当たるリスクが見えました
  • Node-RED 連携の現実 Shadow を Node-RED から扱うには node-red-contrib-aws 系か自作ノードが必要です。既存の汎用ノードは更新頻度・メンテナ状況にバラツキがあり、長期運用で塩漬けになるリスクがありました

総じて「Shadow でやれる部分もありますが、Shadow だけで完結しないし、Shadow を入れる動機が決定打にならない」という結論でした。

全体を一つのパターン(後述の S3 + MQTT)で統一したほうが、運用も実装もシンプルにまとまる、というのが最終的な判断です。


候補3: SSM Parameter Store / AppConfig

一般論として挙がりますが、本プロジェクトでは軽く検討して外れました。

  • SSM Parameter Store:
    • エッジからの読み取りに AWS SDK + IAM認証が要り、結局 Greengrass TES 相当の仕組みが必要になります
    • 加えて、本プロジェクトでは 拠点ごとに複数のGWが存在する前提のため、少なくとも /{site}/{gw_id}/...二階層は欲しくなります。SSM Parameter Store の / 区切りでも階層は表現できますが、サイト×GW×設定種別の3次元を一覧・差分管理しようとすると IAM ポリシーやネーミングが膨らみがちで、設定全体を扱うストアとしては窮屈に感じました
  • AppConfig: 設定の段階的配信に強い反面、IoTデバイスからのネイティブサポートがないため、HTTPで取りに行くコードを自前で書く必要があります。S3 直読みと手間が変わりません

採用(初期構築): S3 + MQTT + 自作中継エージェント

結局のところ、既存マネージドサービスの部品を最小限に組み合わせる形に落ち着きました。これは初期構築フェーズで採用した構成です。

フロー実行基盤としては今のところ Node-RED を使っていますが、ここは次期開発で再検討する余地があると考えています(後述「次にやるなら」参照)。

全体フロー

図3: 採用構成の全体フローです。クラウド層(青)と現場層(橙)を上下に物理分離し、5ステップで「アップロード → 通知 → 受信 → 配置 → 反映」の一本道を表現しています。永続領域(🔒)に着地することで、再起動を跨いで設定が生き残ります。詳細なトピック名・パス・補助経路(ack / クレデンシャル取得)は本文を参照ください。

各ステップで使っているのは S3 / IoT Core / MQTT という標準的な部品だけです。マネージドサービスの固有機能に依存していません。

フロー実行基盤の差し替えが起きても、上流(S3 / IoT Core / 中継エージェント)はそのまま流用できる切り分けにしてあります。

設計上の山場1: S3 → MQTT → 中継エージェント → フロー実行基盤 の伝搬経路

「どこからどこまでを一つの"設定更新"として扱うか」が設計の肝でした。

  • S3 だけ更新して通知しない → デバイスは気づきません。ポーリングするとクラウド通信が無駄になります
  • MQTT だけ通知して S3 を更新しない → 何を取りに行けばよいか分かりません
  • 両方を同期的にやる仕組みはAWSにない → ユーザー側で順序を守る必要があります

ここは割り切って、「S3 にアップロードした後、管理者が MQTT 通知を投げる」を手順として明示化しました。

将来的には Lambda で S3 PutObject トリガーから MQTT publish する自動化も選択肢にありますが、初期構成では手動で十分でした。

通知トピックは GW単位(config/update/{site_id}/{gw_id})で切っています。全デバイス一斉更新は避け、GW 単位で個別に当てていける構造にしてあります。

本格的なステージング配信(カナリアロールアウト等)を組むなら、Lambda + 配信スケジューラを別途立てる前提です。

設計上の山場2: IoT Credential Provider を直叩きする認証情報管理

Greengrass TES を使わないということは、デバイスが AWS サービスに直接アクセスするための一時認証情報を自分で取りに行く必要があります。

ここは AWS IoT の Credential Provider + Role Alias を直接叩く実装にしました。流れは次のとおりです。

  1. 事前準備: AWS IoT で Role Alias を作成し、IAM Role をアタッチします(S3 アップロード権限など)
  2. デバイス側: Thing 証明書を使って Credential Provider エンドポイントに HTTPS GET します
    • URL: https://{account-specific-prefix}.credentials.iot.{region}.amazonaws.com/role-aliases/{role-alias}/credentials
    • ヘッダ: x-amzn-iot-thingname: {thing-name}
    • クライアント証明書: 秘密鍵 + デバイス証明書 + AmazonRootCA1
  3. レスポンス: アクセスキー / シークレット / セッショントークン / 有効期限
  4. デバイス側: 10分の安全マージンを取って、期限切れ前にリフレッシュします

取得した認証情報はフロー実行基盤側のメモリ上(現状は Node-RED の global 変数)に保持し、S3 アップロードなど AWS SDK 的な処理をするときに取り出して使います。

この実装は小さなユーティリティですが、Greengrass を見送った以上、すべてのコンポーネントがここを通るため、落ちると連鎖的に影響します。

単体テストを厚めに書きました(credentials の有効性判定、期限切れ判定、リフレッシュ要否判定)。

設計上の山場3: 設定ファイルの配置とロードタイミングの設計

設定リモート注入を「現地で壊さず」「想定どおりのタイミングで反映させる」ためには、結局のところ 「設定ファイルをデバイス上のどこに置き、いつ・どう読み直すか」 という、デバイス側の設定ライフサイクル設計で勝負がつきます。

S3 / MQTT / 中継エージェントといった経路の話は前の山場1〜2で整理しましたが、経路がいくら正しくても、着地後のファイル取り扱いが雑だと、リロードのたびにランタイム状態が壊れたり、古い処理と新しい設定が混ざったりします

ここはエッジ側の品質をそのまま規定する論点でした。

図A: 3つのトリガー(起動・reload通知・障害復旧)はすべて Loading に集約され、Waiting(走行中ジョブの完了待ち) → Swap(入替) を経てはじめて Active に戻ります。設定が動的に差し替わる系で必ず効いてくる骨格です。

どこに置くか(配置)

  • 配置先: 再起動で消えない永続領域配下に、環境別サブディレクトリで置きます
    • 例: {永続領域}/config/{env}/設定ファイル{env} は dev / stg / prod 等)
  • 揮発するルートFS側には置きません(再起動で消えます)
  • 書き換えはアトミック書き込みを徹底します。*.tmp に書いてから rename(2) で差し替える、というよくある手順です
    • 中途半端な状態の JSON を読み手側が読みに行ってパースエラーを起こすと、リロードのたびにプロセスが unhealthy になるため、ここはケチりませんでした
  • 中継エージェントとフロー実行基盤の両方が同じファイルを参照します。書く責務は中継エージェント側だけ、フロー実行基盤は読み専として、書き手を一本化しました

どのタイミングでロードするか(タイミング)

ロードのトリガーは次の3つに整理しました。

  1. 起動時の初期ロード: 起動時に最新の設定ファイルを読み、ランタイムに展開します。ファイルが無い場合は ack を返さずに安全側(既存設定で起動しない)に倒します
  2. reload 通知契機の再ロード: 中継エージェントから flow/config/reload を受信した時点で、ファイルを読み直してランタイムを入れ替えます。OS のファイル監視機能には頼りません。理由は、書き手と読み手が別プロセスである以上、書き込み中の中間状態をファイル監視側が拾ってしまうリスクがあるためです。「書き手が書き終わった」ことを MQTT 通知の発火点で表明する方が、責務として素直です
  3. 障害復旧時のフォールバック: MQTT 切断中に設定が更新されていた場合に備え、再接続直後に最新版の取得確認を行います

どう反映するか(反映の二段構え)

ロードした設定の中身は、性質の異なる2種類に分けて扱いました。

  • データとしての設定値(接続先IP、認証情報、較正値、機器マッピング、AWS一時クレデンシャル など)
    • これはランタイムのデータ領域に上書きするだけで反映されます
    • 次に値を参照する処理から自然に新しい値が使われます
  • ランタイム状態に紐づく設定(スケジュール、コネクションセッション、購読中のトピック など)
    • これは値を上書きしただけでは反映されません。いま走っている状態を一度落として、新しい設定で立て直す必要があります
    • 例として、スケジュールの差し替えは「既存ジョブを止める → 新しい cron 式で再登録する」という二段階処理になります
    • この種の差し替えは順序を間違えると状態が消えっぱなしになるため、リロード手順を関数として一本化し、単体テストで担保しました
    • さらに、reload 通知を受けた瞬間に前のポーリング・送信処理がまだ走っている可能性があります。リロード処理の入口でフラグを立て、走行中のジョブが完了するのを待ってからランタイムを入れ替える仕組みを入れています。ここは「Node-RED だから」というより、設定を動的に差し替える系一般で必ず効いてくる論点です

結果: 動いたけれど

落ち着いたところを振り返ると、次のような構造になっていました。

本来 Greengrass が提供する機能 本プロジェクトの代替
Component Update による OTA SWU(A/B パーティションの署名付きイメージ)
Token Exchange Service IoT Credential Provider + Role Alias を自前で直叩き
Component 間 IPC ローカル Mosquitto (MQTT)
Shadow 連携(状態同期) 不採用(候補2 参照)。設定リモート注入は S3 + MQTT で代替
ローカルデバッグコンソール フロー実行基盤のエディタ(現状: Node-RED)
設定配信(OTA は別行で SWU がカバー) S3 + IoT Core MQTT publish + ack(GW 単位での個別配信)
設定ライフサイクル管理 中継エージェントによるアトミック書き込み + 通知駆動リロード
ログ集約 podman logs + ベンダー純正監視コンソール
バージョン管理・ロールバック S3 オブジェクトバージョニング(設定)/SWU の世代管理(OS・アプリ)

機能単位で見れば、だいたいカバーできています。ただし、Greengrass が提供する「統一された管理レイヤー」は失われています。

トラブルシュートは各層(S3 / IoT Core / 中継エージェント / フロー実行基盤 / OS)で個別にログを見る必要があります。

これが正解だとは思っていません。ただ、ハードウェア制約と運用制約(粒度の粗い純正OTAとの共存・揮発ストレージ)を満たすのがこの形だった、という事例として残しておきたいと考えています。


学び(テクニカルな反省)

  • 「AWS IoTのベストプラクティス」はハードウェアを選びます。Greengrass V2 は aarch64 + 1GB RAM 以上の環境なら素直に効きます。armv7 + 512MB + ベンダー純正OTAあり + 揮発ルートFS、という組み合わせでは合わないことがあります
  • ベンダー側が提供している機構(OTA、監視)と AWS 側のサービスで機能重複が起きるとき、AWS 側を入れるコストのほうが大きくなる場合があります
  • 更新媒体の「粒度」が合うかどうかは、機能の有無よりも実は支配的です。SWU のようなルートFS丸ごと差し替え型の更新と、Greengrass の Component 単位の細粒度配信を併走させると、状態管理のオーバーヘッドが運用負担として現れます
  • 設定リモート注入は「経路」より「着地後のライフサイクル」で勝負がつくことがあります。S3 / MQTT / 中継エージェントの経路設計だけでなく、書き込みのアトミック性・ロードのトリガー設計・ランタイム状態の差し替え順序まで含めて初めて、現地で壊れない仕組みになります
  • 「設定をJSONファイルとしてS3に置く」は原始的ですが、関与するレイヤーが少なく、各層で独立にテスト可能です。スケールしない規模では十分強い選択肢でした
  • IoT Credential Provider は地味ですが便利です。Greengrass を使わない場合でも、これを直接叩けば AWS SDK 的な処理は回せます
  • Device Shadow は万能ではありません。サイズ制限・連携ノードの品質を考えると、候補から外れるケースがあります
  • Node-RED は立ち上げ初期に強い反面、長期運用での限界(フロー JSON の差分レビュー困難・水平スケーラビリティ・観測性・contrib 品質)が顕在化しやすいです。初期構築では割り切って使い、規模が大きくなる前に別基盤への移行を検討する余地はあります

特筆すべき T&E 事例を一つ挙げておきます。Greengrass 検証中の一時期、ローカル開発環境のビルドに非常に時間がかかったことがありました。

クロスアーキのマルチステージビルド、gdk CLI による Component 作成、Docker-in-Docker の検証を並行すると、ビルド環境自体のメンテナンスが主業務化する現象が発生します。これも「Greengrass 寄りの開発は軽くない」と実感した瞬間でした。


次にやるなら(次期開発に向けて)

初期構築で割り切った部分のうち、次期開発で再検討したい論点を並べておきます。

運用面の改善(短中期)

  • S3 PutObject → Lambda → IoT MQTT publish の自動化(手動通知の撤廃)
  • 設定スキーマの JSON Schema 化と CI 検証(不正な設定を配布して現地で壊さないため)
  • AWS IoT Jobs の再検討。Shadow より大きなペイロードを扱えるジョブキュー型で、OTA 以外の用途(設定更新)でも使える可能性があります

基盤の差し替え(中長期)

  • フロー実行基盤の差し替え検討。Node-RED から、コード表現可能で観測性の高いランタイムへ。候補としては、Python asyncio / Rust / Go 製の小型ワーカ、メッセージング層の NATS / Redis Streams 化、変換ロジックのクラウド側ストリーム処理(Kinesis / MSK + Flink)への寄せ、などです
  • 段階的移行の前提: 初期構築で動いている Node-RED フローはそのまま「仕様」として活かし、新基盤に対する受け入れテストの参照とする。一斉切り替えではなく、役割を切り分けて段階的に移行する

おわりに

ここまで、Greengrass / Device Shadow / SSM Parameter Store / AppConfig という4つの候補を順に外し、S3 + MQTT + 自作中継エージェントに着地するまでをたどってきました。最後に、本記事から持ち帰っていただきたいことを3点に絞ります。

1. ベストプラクティスの優劣ではなく、「制約と噛み合うか」が判断軸です

Greengrass V2 は優れたサービスですし、Device Shadow も SSM Parameter Store も、それぞれ素直にハマる現場では強力です。本記事はこれらを否定するものではありません。

重要なのは、「自分の現場のハードウェア制約・運用制約と、サービス側の前提が噛み合うか」を、ドキュメント上ではなく実機で確かめることです。

Greengrass を見送った最大の理由は機能の不足ではなく、SWU との更新粒度のズレでした。これはAWSのベストプラクティス資料からは見えてきません。

2. エッジ設計では「機能があるか」より「更新粒度・永続化・運用責務が噛み合うか」が支配的です

クラウド単独のシステムなら「必要な機能があるか」が最初の問いになります。

一方、エッジを含むシステムでは、機能の有無は満たせても、それを支える前提(ファイルレイアウト、永続化方針、更新粒度、運用責務の分担)が噛み合わないと、運用に乗せた瞬間に歪むことが多いです。

設計初期の評価軸に、「更新の粒度」「永続化境界の位置」「責務分担の重複」を必ず入れることをおすすめします。

3. 同じような制約を持つ現場では、今回の試行錯誤がそのまま判断材料になります

「ベンダー純正OTA + 揮発ルートFS + RAM 制約」という組み合わせは、本プロジェクト固有の話ではありません。産業用エッジ機器ではむしろ普通の構成です。

同じ制約に当たった方が、「Greengrass を載せようとしてうまくいかなかったが、なぜダメだったか言語化できない」という状態から一歩進むための、判断材料になれば嬉しいです。

S3 + MQTT という素朴な答えに着地したこと自体が結論ではなく、「制約を直視して、足りる範囲で組む」というスタンスのほうが、本記事の本当の中身だと思っています。

決まったベストプラクティスがない領域で、ハードウェアとクラウドの板挟みの中から組み合わせを選び取るのが、エッジ設計の現実です。本記事がその実例の一つとして、読んでいただいた方の次の一手に少しでも役立てば幸いです。


参考資料