はじめに
Ubuntuを搭載したEdge PCを24時間365日、無人で運用する場合、
OSが起動する
Dockerが起動する
Applicationが起動する
Cloudへ接続できる
だけでは十分ではありません。
実際の長期運用では、
時刻がずれる
DNSの状態が変わる
Routeが戻らない
Firewall / NATの状態が崩れる
NICが正常に復帰しない
Update後にRebootが必要になる
Container Runtimeの状態が変わる
電源断後に一部だけ正常に戻らない
といった問題も考える必要があります。
そこで今回のEdge PCでは、
異常を一度も発生させないことを目指すのではなく、異常が発生しても正常状態へ戻りやすい構成にする
という方針にしました。
その中心として使用したのが、
systemd service
systemd timer
です。
この記事では、Ubuntu Edge PCを24/365で無人運用するために実際に採用した、
- 定期Maintenance
- 処理時間の分散
- 定期Reboot
- Boot後の自己復旧
- NTP / DNS / Route / NAT / Firewall確認
- UpdateとRebootの分離
- NIC復旧
- systemd timerの監視
- 外部監視との役割分担
について整理します。
結論:無人Edge端末は「壊れない」より「戻れる」ようにする
今回の構成を先にまとめると、
① 壊れにくくする
↓
② 壊れたら正常状態へ戻す
↓
③ 戻せなければ外部から検知する
という3段階です。
具体的には、
予防
├─ Version管理
├─ Security Update
├─ 定期Maintenance
└─ Maintenance時間の分散
自己復旧
├─ 定刻Reboot
├─ Boot後のRoute確認
├─ Firewall / NAT確認
├─ DNS確認
├─ NTP同期
├─ NIC復帰
└─ 必要時Reboot
外部検知
├─ Ping
├─ Heartbeat
├─ Telemetry
├─ rsyslog
└─ netconsole
という構成にしました。
ポイントは、
一つの強力な仕組みで安定させようとしない
ことです。
1. 「正常に起動した」だけでは24/365運用にならない
開発時には、
Power ON
↓
Ubuntu起動
↓
Docker起動
↓
Azure IoT Edge起動
↓
Application起動
↓
Cloud接続
まで確認できれば、正常に見えます。
しかし、無人運用で重要なのはその瞬間ではありません。
1日後
1週間後
1か月後
Update後
Network断後
Power Failure後
にも、同じ正常状態へ戻れる必要があります。
そのため今回の設計では、
正常状態を最初に作る
だけでなく、
正常状態を定期的に作り直す
ことを重視しました。
例えば、
一時的なNetwork異常
Router / DHCP側の変化
時刻ずれ
Package Update
Process停止
予期しないReboot
Power Failure
などは、Edge PCだけでは完全には防げません。
そこで、
異常を完全に防ぐ
という考え方から、
現在状態を確認
↓
正常なら何もしない
↓
異常なら修復
↓
Normal Stateへ戻す
という考え方へ変更しました。
2. Maintenance処理を巨大な1本のScriptにしない
最初に避けたのが、
毎晩01:00
NTP確認
DNS確認
Network再設定
Package Update
Container Restart
Reboot
のように、すべてを1本のScriptで処理する構成です。
確かにFile数は少なくできます。
しかし障害時に、
どこで失敗したのか分からない
一つ失敗すると後続処理まで止まる
Network処理とUpdateが衝突する
Rebootされると原因を追いにくい
という問題があります。
そこで役割ごとに、
NTP
NAT
DNS
IoT Edge Update
OS Reboot判定
IoT Edge Reboot判定
NIC復帰
Route修復
Firewall確認
Diagnostic
を分離しました。
3. systemd serviceとtimerで「実行時刻」と「処理」を分ける
Ubuntuでは、
systemd service
+
systemd timer
を使っています。
基本構成は、
.timer
↓
いつ実行するか
.service
↓
何を実行するか
です。
例えばNTP同期なら、
mdlinkage-ntp-sync.timer
↓
mdlinkage-ntp-sync.service
↓
NTP同期処理
という形です。
Timer自体の状態は、
systemctl status mdlinkage-ntp-sync.timer
で確認できます。
実際に実行された処理は、
journalctl -u mdlinkage-ntp-sync.service
で確認できます。
この構成のメリットは、
「いつ動かすか」と「何をするか」を分離できる
ことです。
4. 実際のMaintenance Schedule
今回のEdge PCでは、主なMaintenanceを次のように分散しました。
00:15
定刻Reboot
00:45
NTP Sync
NAT確認
01:00
DNS確認・再設定
01:30
IoT Edge / Moby Patch確認
02:30
OS Updateによる
reboot-required確認
03:30
IoT Edge / Moby Update後の
Reboot Flag確認
04:30
NIC状態確認・復帰
さらにBoot後にも、
Route
NAT
Firewall
NIC
Network関連設定
NTP
を再確認します。
重要なのは、
すべてを同時に実行しないこと
です。
5. Maintenance時間をずらす理由
例えば同じ時刻に、
NTP同期
Package Update
DNS変更
Container Restart
Network再設定
Reboot
を実行したとします。
一つ一つは正常でも、組み合わせによって、
Networkが一時的に切れる
Update途中でRebootする
DNS確認中にNICがRestartする
Container確認中にRuntimeが更新される
といった問題が発生する可能性があります。
また障害が起きても、
NTP?
DNS?
Update?
Network?
Reboot?
と原因を切り分けにくくなります。
そこで、
処理A
↓
時間を空ける
↓
処理B
↓
時間を空ける
↓
処理C
としています。
Maintenance処理同士も障害原因になり得る
という前提です。
6. 定期Rebootを入れるなら「Reboot後に戻れること」が前提
今回の構成では、
00:15
に定刻Rebootを実行しています。
目的は、
長時間動作による一時的な状態蓄積
Application / Runtimeの状態変化
Network Stackの一時的な異常
Processの想定外状態
などを一度CleanなBoot状態へ戻すことです。
ただし、
定期Rebootを入れれば安定する
という意味ではありません。
例えばReboot後に、
Networkが戻らない
Routeがない
Firewallが戻らない
NATがない
NICがDown
IoT Edgeが起動しない
となれば、Rebootそのものが障害原因になります。
そのため、
Reboot
↓
Ubuntu Boot
↓
Network Ready
↓
Route確認
↓
Firewall / NAT確認
↓
NIC確認
↓
NTP同期
↓
Application復旧
までを一つのRecovery設計として考えます。
7. Recovery Scriptは「正常なら何もしない」
Boot後のRoute確認では、
現在のRoute確認
↓
正常
↓
OKとして終了
とします。
異常な場合だけ、
現在のRoute確認
↓
Route不足
↓
期待状態へ修復
します。
FirewallやNATも同様です。
現在のRule確認
↓
正常
↓
何もしない
または、
現在のRule確認
↓
不足・異常
↓
期待状態へ戻す
です。
つまり、
Timer起動
↓
毎回設定を書き直す
のではありません。
Timer起動
↓
現在状態を確認
↓
正常なら終了
↓
異常なら修復
としています。
Maintenance処理は、
何度実行しても正常状態を壊しにくいこと
が重要です。
8. NTP / DNSも「最初に設定したからOK」ではない
NTP
Edge Gatewayでは時刻も重要です。
時刻がずれると、
TLS Certificate
Log時刻
Cloud通信
障害解析
複数端末間のLog比較
などへ影響します。
特にTroubleshootingでは、
Edge PC Log
Network Log
Cloud Log
を時系列で比較するため、各機器の時刻がずれていると解析が難しくなります。
今回の構成では、
00:45
NTP Sync
に加え、
Boot
↓
Network Ready
↓
NTP Sync
も行っています。
DNS
Cloud接続ではDNSも重要です。
例えば、
IP通信
↓
OK
DNS Resolve
↓
NG
なら、Network Interface自体が正常でもAzure IoT Hubへ接続できません。
利用者から見ると、
Networkはつながっている
でも
CloudへDataが送れない
という状態です。
そのためDNSも一度設定して終わりではなく、定期的に確認します。
切り分けも、
NIC
↓
Network
↓
Route
↓
DNS
↓
Cloud
のようにLayerごとに分けて考えます。
9. Update処理とReboot処理を分離する
無人運用では、
Update
↓
即Reboot
という構成にはしていません。
例えばUbuntu側でKernelや重要Packageが更新されると、
/var/run/reboot-required
が生成される場合があります。
今回の構成では、
Update完了
↓
時間を空ける
↓
02:30
↓
reboot-required確認
↓
あり → Reboot
なし → 何もしない
としています。
つまり、
Updateする処理とRebootする判断を分離する
構成です。
10. IoT Edge / Container RuntimeのUpdateも専用処理にする
Azure IoT Edge / Container Runtimeについても、OS Maintenanceとは分離しました。
01:30
に、
Installed Version確認
↓
Candidate Version確認
↓
同一世代か確認
↓
Dry-run
↓
依存関係確認
↓
条件を満たせばPatch
↓
Health Check
という専用の処理を通します。
UpdateによってRebootが必要になった場合は、
IoT Edge / Moby Update
↓
pending-iotedge-reboot
のようにFlagを残します。
その後、
03:30
↓
Flag確認
↓
条件確認
↓
必要ならReboot
します。
これにより、
Update
↓
即Reboot
ではなく、
Update
↓
Flag
↓
Maintenance Window
↓
Reboot
という制御ができます。
また複数台のEdge PCを想定し、
RandomizedDelaySec
も利用しています。
これにより全端末が完全に同じ瞬間にMaintenanceを開始することを避けます。
11. Maintenanceの最後にNICも確認する
ApplicationやContainerが正常でも、
NIC Down
であればGatewayとしては動作できません。
そのため、
04:30
NIC状態確認
も行います。
今回の設計では、
Application
Container
OS
Network Interface
を別々のLayerとして考えています。
Application正常
+
Container正常
+
OS正常
+
NIC異常
であれば、
Gatewayとしては異常
です。
12. 定期MaintenanceだけでなくBootをRecovery Triggerにする
Timerを毎日設定していても、
Power Failureが、
00:30
に発生した場合、次の定期Maintenanceまで待つわけにはいきません。
そこで一部の処理では、
OnBootSec
を使っています。
対象は例えば、
NTP
NAT
Route
NIC
Firewall
です。
つまり、
Power Failure
↓
Power Restore
↓
Ubuntu Boot
↓
Recovery処理開始
として、
BootそのものをRecovery Triggerにする
設計です。
13. Persistent=trueは処理によって使い分ける
systemd timerには、
Persistent=true
があります。
例えば01:00に予定していた処理の時間にPCが停止していた場合、
01:00
PC OFF
↓
処理できない
↓
次回Boot
↓
未実行処理を実行
とできます。
ただし、すべてのTimerで有効にするわけではありません。
例えば毎日00:15の定刻Rebootについて、
昨日00:15
PC OFF
↓
今日Boot
↓
昨日Rebootできなかったので今Reboot
とする必要はありません。
そのため、
実行漏れをBoot後に補う処理
と、
実行漏れを補わない処理
を分けます。
重要なのは、
Persistent=trueを使うかどうかではなく、その処理の目的
です。
14. Timerを作っただけではなく「実際に動いた証拠」を確認する
無人端末では、人が画面を見て処理成功を確認できません。
そのためLogを残します。
Service単位のLogは、
journalctl -u mdlinkage-ntp-sync.service
で確認できます。
Timer全体は、
systemctl list-timers --all
で確認できます。
Maintenance用Timerだけ確認するなら例えば、
systemctl list-timers --all | grep -E \
'mdlinkage|mdl-|reboot-if'
のように確認できます。
見るべきなのは、
Timer Fileが存在する
だけではありません。
enabledか
activeか
期待する時刻になっているか
実際にServiceが実行されたか
Exit Codeが正常か
まで確認します。
今回のAcceptance Checkでは、
00:15 定刻Reboot
00:45 NTP
00:45 NAT
01:00 DNS
01:30 IoT Edge Patch
02:30 OS Reboot Check
03:30 IoT Edge Reboot Check
04:30 NIC Wakeup
が期待どおり登録されているか確認します。
さらにBoot後には、
Route
NTP
NAT
Firewall
NIC
などが、
現在のBoot Sessionで実際に実行された証拠
まで確認します。
設定Fileが存在することと、実際に動作したことは別です。
15. 自己復旧だけでは検知できない障害もある
ここまでの仕組みによって、
時刻
DNS
Route
Firewall
NAT
NIC
IoT Edge
Container Runtime
Reboot Requirement
など、多くの状態をEdge PC自身で確認できます。
しかし、Edge PCそのものが完全にFreezeした場合は別です。
Edge PC
↓
Freeze
↓
CPU / Kernel / Network停止
となれば、
systemd timer
Health Check
Log保存
Recovery Script
自体が動きません。
そこで、
Edge PC自身
↓
正常状態を維持
異常なら自己復旧
と、
External Monitor
↓
Edge PCが本当に生きているか
外側から確認
を分けました。
外部監視側では、
Ping
Heartbeat
Telemetry
rsyslog
netconsole
など複数の経路を使います。
これによって、
Applicationだけ停止
Networkだけ停止
OS全体がFreeze
Kernel Crash
などの切り分けをしやすくします。
16. 24/365無人Edge端末の設計を整理する
今回の設計をまとめると、3つのLayerになります。
① 予防
────────────────
Version管理
Security Update
定刻Maintenance
Maintenance時間の分散
② 自己復旧
────────────────
定刻Reboot
Boot後のRoute確認
Firewall / NAT確認
DNS確認
NTP同期
NIC復帰
必要時Reboot
③ 外部検知
────────────────
Ping
Heartbeat
Telemetry
rsyslog
netconsole
つまり、
壊れにくくする
↓
壊れたら戻す
↓
戻せなければ外から検知する
という構成です。
まとめ
24/365の無人Edge端末を安定稼働させるために、特別なTechnologyを一つ導入したわけではありません。
使用しているのは、
systemd
systemd timer
journal
nftables
Shell Script
Health Check
など、Linuxでは一般的な仕組みです。
重要だったのは、
どの異常に対して
どのLayerで
いつ確認して
どこまで自動復旧し
どこから外部監視へ任せるか
をSystem全体として設計することでした。
そしてRecovery処理についても、
毎回設定を書き直す
のではなく、
現在状態を確認
↓
正常なら何もしない
↓
異常なら期待状態へ戻す
ようにしています。
24時間365日の無人運用では、「止まらないこと」だけではなく、「正常状態へ戻れること」と「戻れなかったことを検知できること」が重要です。
次回は、Edge PC自身が完全にFreezeした場合に備えて、
監視対象とは別のLinux端末からEdge PCを監視する構成
について整理します。
Edge PC
↓
Ping
Heartbeat
Telemetry
rsyslog
netconsole
↓
External Monitor
自己復旧できない障害を、どのように外側から検知し、Freeze直前の情報を残すかを紹介します。