はじめに
Azure IoT Edgeを使ったシステムで、Edge PCからCloudへの通信断に備えてStore-and-Forwardを構成していても、データが失われる可能性は残ります。
それは、Edge PCにデータが届く前の通信断です。
今回扱うシステムは、次の構成です。
Modality(医療機器)
↓ TCP
Edge PC(Ubuntu / Azure IoT Edge)
↓
Azure IoT Hub
Edge PCからCloudへの通信断については、ローカルでデータを保持し、復旧後に再送する仕組みを検討してきました。
しかし、ModalityからEdge PCへのTCP接続が失敗した場合、データはEdge PCに到達しません。
つまり、Edge PC側のStore-and-Forwardだけでは不十分です。
この記事では、実際に遭遇したTCP接続トラブルを例に、次の点を整理します。
- TCPのACKとApplicationのACKの違い
- SYNが届いてもTCP接続できない原因
- 送信失敗時のデータ保持と再送
- ACKが返らなかった場合の処理
- 再送によるDuplicate(重複データ)への対応
- 無人運用でのTimeoutとErrorの扱い
1. 環境と通信構成
今回の環境は以下のとおりです。
| 項目 | 構成 |
|---|---|
| 送信元 | Modality(医療機器) |
| 受信先 | Edge PC |
| Edge OS | Ubuntu 24.04 |
| Edge Runtime | Azure IoT Edge 1.6 |
| Container | Docker / Moby |
| 通信Protocol | TCP |
| 使用Port | TCP 57545 |
| Cloud | Azure IoT Hub |
Edge PC内では、複数のContainerによってデータを処理しています。
Modality
↓ TCP 57545
Edge PC
│
├─ TCPServer
│ ↓
├─ RelayConversion
│ ↓
├─ SendAzure
│ ↓
└─ edgeHub
↓
Azure IoT Hub
この構成で通信断が発生する箇所は、大きく2つあります。
① Modality → Edge PC
② Edge PC → Cloud
②については、Edge側にデータを保持して再送するStore-and-Forwardが有効です。
しかし、①で通信が失敗した場合には、Modality側にも対策が必要になります。
2. TCP通信でもApplicationのデータ保持は保証されない
TCPには次のような仕組みがあります。
- ACK(受信確認)
- Retransmission(再送)
- Sequence Number(順序管理)
- Flow Control(フロー制御)
これらによって、TCPは信頼性の高い通信を実現しています。
ただし、TCPが保証することと、Applicationが保証することは異なります。
TCP ACKとApplication ACKの違い
TCP ACKは、相手側のTCPスタックが送信済みデータを受信したことを確認する仕組みです。
しかし、次の処理までは保証しません。
TCPでDataを受信
↓
ApplicationがDataを読み取る
↓
Dataの内容を検証する
↓
Dataを保存する
↓
後続処理へ渡す
例えば、Edge PCがTCPデータを受信した直後にApplicationが異常終了した場合、受信済みデータがApplicationとして保存されていない可能性があります。
そのため、重要なデータを扱う場合は、Application側でもACKやResponseを設計する必要があります。
Modality Edge PC
│ │
├──── Data送信 ────────────>│
│ │
│ Data受信
│ │
│ Data検証
│ │
│ Data保存
│ │
│<──── Application ACK ────┤
│ │
送信完了
このACKが何を保証するかは、システム側で定義します。
例えば、
「Edge PCがデータを安全に保存したこと」
をACKの成功条件にする設計です。
単にTCPで受信した時点でACKを返す設計と、永続化が完了してからACKを返す設計では、障害時のデータ損失リスクが異なります。
なお、このACKはEdge PCへの保存完了を意味するものであり、Azure IoT Hubへの送信完了まで保証するものではありません。
3. 実際に遭遇した「SYNは届くのにTCP接続できない」障害
過去に、ModalityからEdge PCへTCP接続できない問題が発生しました。
Packet Captureを確認したところ、ModalityからのSYNはEdge PCに届いていました。
Modality
│
│ SYN
↓
Edge PC
さらに、Docker Container側ではSYN/ACKが生成されていました。
しかし、TCP Connectionは成立しません。
調査すると、Edge PCのRoutingが原因で、応答PacketがModalityのNetworkではなく、別のInterfaceへ送られようとしていました。
Modality
│
│ SYN
↓
Edge PC
│
│ SYN/ACK
↓
誤ったNetwork Interface
つまり、
- Modalityからの通信は届いている
- Containerも応答を生成している
- しかし、戻りの通信経路が正しくない
という状態です。
受信方向のPacketが確認できても、戻りの経路が正しいとは限りません。
Linuxでの確認コマンド
同様の問題が発生した場合、まずRouting Tableを確認します。
ip route
次に、ModalityのIPアドレスへの経路を確認します。
ip route get <Modality_IP>
例えば、Modalityへ到達するためのInterfaceが想定と異なっていないかを確認します。
Packetを確認する場合は、tcpdumpを使用します。
sudo tcpdump -ni any \
"host <Modality_IP> and tcp port 57545"
ここでは以下を確認します。
① ModalityからSYNが届いているか
② Edge PCからSYN/ACKが返っているか
③ ModalityからACKが戻ってくるか
Linux Host側のRoutingだけでなく、DockerのNetwork Namespace、Port Mapping、NATなども関係します。
そのため、必要に応じて各Network InterfaceでCaptureし、どのInterfaceからPacketが出ているかを確認します。
今回の経験から、Applicationが正常に動いていても、Network Pathが成立しているとは限らないことが分かりました。
4. 送信できなかったデータをModality側で保持する
TCP Connectionが成立しない場合、Edge PC側にはデータが届きません。
そのため、Edge PC側にどれだけStore-and-Forwardを構成していても、このデータを救うことはできません。
必要なのは、Modality側にも未送信データを保持する仕組みを持たせることです。
正常時は次のようになります。
Modality
↓
Data生成
↓
Edge PCへ送信
↓
Application ACK受信
↓
送信完了
一方、通信失敗時は次のようにします。
Modality
↓
Data生成
↓
DataをLocal保存
↓
Edge PCへ送信
↓
Connection Error / Timeout
↓
Dataを保持
↓
一定時間後にRetry
↓
通信復旧
↓
再送成功
↓
ACK受信
↓
送信完了
重要なのは、送信失敗を理由に元データを削除しないことです。
また、データ消失を防ぐためには、送信前のLocal保存や保存領域の永続性も考慮する必要があります。
メモリ上のQueueだけでは、Modalityが再起動した場合に未送信データが失われる可能性があります。
5. ACKが返らない場合は「受信失敗」とは限らない
再送設計で注意したいのが、ACKだけが失われるケースです。
例えば、次のような状況です。
Modality Edge PC
│ │
├──── Data送信 ───────────>│
│ │
│ Data保存成功
│ │
│<──── ACK送信 ────────────┤
│
× ACKが途中で失われる
│
Timeout
│
├──── Data再送 ───────────>│
Modalityから見るとACKが届いていないため、送信が成功したかどうか判断できません。
しかし、Edge PC側ではすでにデータを保存している可能性があります。
ここで再送すると、同じデータが2回届きます。
そのため、次の2つはセットで考える必要があります。
ACKが届かない
↓
再送する
↓
重複受信を検出する
6. Duplicate(重複データ)を防ぐ設計
再送を許可する場合、Duplicateの対策も必要です。
例えば、Modalityがデータを送る際に、Message IDを付与します。
{
"messageId": "device001-000123",
"deviceId": "device001",
"timestamp": "2026-10-01T10:00:00Z",
"data": {
"status": "completed"
}
}
Edge PCでは、Message IDを確認します。
Data受信
↓
Message ID確認
↓
保存済み?
│
├─ NO
│ ↓
│ Data保存
│ ↓
│ ACK返却
│
└─ YES
↓
二重保存しない
↓
ACK返却
この仕組みによって、同じMessage IDのデータが再送された場合でも、二重処理を防げます。
ここで重要なのは、Retry時も同じMessage IDを使うことです。
再送するたびに新しいIDを発行すると、Edge PC側で同一データと判断できません。
また、実装時には次の点も考慮する必要があります。
- Message IDが一意になる範囲
- 重複チェック履歴の保持期間
- Edge PC再起動後も履歴が残ること
- 重複チェックと保存処理の整合性
Message IDの確認だけでは、並行処理による二重保存を完全に防げない場合があります。
例えば、保存処理に一意制約を設けるなど、冪等性(Idempotency)を確保する設計が必要です。
7. TimeoutとRetryの設計
一時的な通信断が起こるたびにApplicationを停止させると、24時間365日の無人運用が難しくなります。
例えば、次のような設計です。
Data送信
↓
Responseなし
↓
ERROR
↓
Application停止
↓
Operatorによる復旧待ち
これでは、一時的なNetwork Troubleでも現地対応が必要になります。
そこで、通信失敗と致命的なApplication Errorを区別します。
通信失敗を段階的に扱う
以下は設計例です。実際の時間や回数は、業務要件に合わせて決定します。
| 状態 | 処理 |
|---|---|
| 一時的な通信失敗 | データ保持・自動Retry |
| 通信失敗が一定時間継続 | Warningを記録 |
| 長時間復旧しない | Operatorへ通知 |
| 保存領域が逼迫 | 優先度の高いAlert |
| データ保存自体に失敗 | 別途定義した重大障害処理 |
Retry間隔についても、一定間隔で再送する方法に加え、失敗が続く場合に間隔を広げるExponential Backoffなどがあります。
例えば、
Retry 1回目 → 5秒後
Retry 2回目 → 10秒後
Retry 3回目 → 20秒後
以降 → 上限時間内でRetry
という考え方です。
複数のModalityが同時に復旧する環境では、Retryタイミングにランダムな揺らぎ(Jitter)を加えることも有効です。
ただし、無制限のRetryで障害を隠してはいけません。
データを保持してRetryを継続することと、異常をOperatorへ通知することは両立させる必要があります。
8. ModalityからCloudまでを通した設計
ここまで整理すると、データ保持を考えるべき場所は1か所ではありません。
┌────────────────────┐
│ Modality │
│ │
│ Data生成 │
│ Local保存 │
│ 送信 / Retry │
│ ACK確認 │
└─────────┬──────────┘
│
│ TCP
↓
┌────────────────────┐
│ Edge PC │
│ │
│ TCPServer │
│ Data受信・重複判定 │
│ Data永続化 │
│ ACK返却 │
│ │
│ Store-and-Forward │
└─────────┬──────────┘
│
│ AMQP over WebSocket
↓
┌────────────────────┐
│ Azure IoT Hub │
└────────────────────┘
この場合、通信断への対策は2段階になります。
| 通信区間 | 主な対策 |
|---|---|
| Modality → Edge PC | Modality側保存、Retry、Application ACK、Duplicate対策 |
| Edge PC → Cloud | Edge側Store-and-Forward、再送、保存容量管理 |
ここでは、ACKの意味も区間ごとに区別する必要があります。
例えば、Modalityが受信するACKを「Edge PCへの永続化完了」と定義した場合、それはCloudへの到達保証とは異なります。
また、Cloud側も含めてデータの二重処理を避けたい場合には、後続の処理でもMessage IDを活用する必要があります。
システム全体で考えると、基本的にはAt-least-once Delivery(少なくとも1回の配信)と冪等な処理を組み合わせる設計が現実的です。
9. 設計時に確認しておきたいこと
今回のようなシステムでは、以下の項目を事前に確認しておくと、通信断が発生した際のData Lossを防ぎやすくなります。
| 確認項目 | 確認内容 |
|---|---|
| 送信前保存 | Data送信前に元データを保存しているか |
| Connection失敗 | TCP接続失敗時にDataが残るか |
| ACK Timeout | ACK未受信時に再送できるか |
| ACKの意味 | 受信完了か、永続化完了か |
| 再起動 | Modality再起動後も未送信Dataが残るか |
| Duplicate | 同じMessage IDの二重処理を防げるか |
| Retry | 間隔やBackoffが適切か |
| Monitoring | 通信断が長時間続いた場合に検知できるか |
| 保存容量 | 長時間の通信断に耐えられるか |
特に重要なのは、正常系のテストだけで完了させないことです。
TCP Connection失敗
送信途中の切断
ACKだけが失われる
Edge PCの再起動
Modalityの再起動
通信断が長時間継続
復旧後の一斉再送
これらの状態でもデータが保持され、復旧後に正しく処理されるかを確認する必要があります。
なお、実際の医療機器への適用では、データの重要度や業務上の安全要件に応じて、処理停止・警告・自動復旧の条件を個別に定義する必要があります。
まとめ
今回のポイントは、TCPの信頼性とApplicationのデータ保証を分けて考えることです。
TCPにはACKや再送の仕組みがありますが、それだけでApplicationによるデータ保存や後続処理まで保証されるわけではありません。
また、実際の障害では、ModalityからのSYNがEdge PCに届いていても、Routingの問題によってTCP接続が成立しないケースがありました。
そのため、Modality側から次の仕組みを考える必要があります。
- 送信前にDataを保持する
- 送信失敗やTimeout後にRetryする
- Application ACKの成功条件を明確にする
- ACKが失われても安全に再送できるようにする
- Message IDによってDuplicateを防ぐ
- 一時的な通信断と重大障害を区別する
重要なのは、通信断を完全になくすことではありません。
通信が切れてもデータを失わず、復旧後に処理を継続できる構成にすることです。
これはEdge PCからCloudへの通信だけではなく、データを生成するModality側から設計する必要があります。
次回
これまでは、Edge PCの安定化、通信断検知、Store-and-Forward、Modality側の再送設計など、主に技術的な対策を整理してきました。
次回は少し視点を変えて、これらのReliability・Security・Monitoring対策が、Downtime削減やRemote Support、保守工数削減といった顧客価値にどうつながるのかを整理します。