← シリーズに戻る

コンテナと A/B: 2面で置き換える

Part 4 / 6 Raspberry Pi デプロイ5方式を7軸比較

ここまでの3方式は、どれも変わったものだけを配っていました。ファイルを置く、アーカイブを展開する、パッケージを入れ替える。粒度は違いますが、機械の上には更新されなかった部分がそのまま残ります。

この章で扱う2つは、その前提を捨てます。動くもの一式を丸ごと置き換える。

7軸の評価が両端に振り切ります。ロールバックは5方式で最強で、機械が単独で前の面に戻ります。常駐コストと帯域は5方式で最低で、設定を1行変えるために領域ぶんを転送して再起動します。共通課題に当てると過剰ですが、現地に人が行けないという条件が1つ入るだけで評価が反転します。

この方式の検証状態: 実機では動かしていません。A/B 型については RAUC の公式ドキュメントで確認した内容です(末尾に URL)。この方式だけは機材が足りず、パーティションを切り直せる機械がもう1台必要です。

2つの粒度

丸ごと置き換えると言っても、範囲が2段階あります。

置き換える範囲再起動追加で要る領域
イメージ型(コンテナ)アプリと依存ライブラリ不要(コンテナの作り直しのみ)イメージの保管分
A/B 型(A/B パーティション)システム領域まるごと必要もう1面ぶん

イメージ型は、レジストリに新しいイメージを置き、機械側でそれを取得してコンテナを作り直します。カーネルやブートローダーには触りません。取得の向きは pull 型と同じで、定期的にレジストリを見に行く道具を常駐させる形が一般的です。

A/B 型は組み込みの世界の標準解です。同じものが入る領域を2面持ち、更新は動いていないほうに書きます。

置き換える範囲の比較。イメージ型 (コンテナ) では、アプリと依存ライブラリがイメージごと入れ替わり、コンテナランタイムという常駐物が1つ増え、カーネルとブートローダーには触らない。再起動は不要 (コンテナの作り直しのみ) で、追加の領域はイメージの保管分。A/B 型 (A/B パーティション) では、カーネルを含むシステム領域が A 面と B 面の2つあり、いま動いている A 面はそのままで、B 面に丸ごと書く。ブートローダーは次にどちらの面で起動するかを持つ。再起動が必要で、追加の領域はもう1面ぶん。A/B 型ではカーネルの更新も同じ経路で配れる

A/B の動き方

RAUC の公式ドキュメントで確認できる範囲で、流れはこうです。

  1. いま動いている領域を判定し、動いていない領域だけを書き込み先に選ぶ
  2. 更新の中身を検証してから、その領域に書く
  3. 書き終えたら、次回起動でそちらを使うようブートローダーに指示する
  4. 再起動する
  5. 起動が成功したことを明示的に記録する
  6. 記録されないまま問題が起きたら、ブートローダーが前の領域に戻す

5 が肝です。「起動した」と「正しく動いている」は別なので、アプリケーションが正常に立ち上がったことを自分で申告させる設計になっていて、申告が来なければ機械は誰の指示も待たずに前の面へ帰ります。ハードウェアのウォッチドッグを併用すると、固まった状態も検出できます。

A/B パーティションの更新が機械の中で進む順序。動いていない面に書き、ブートローダーに次回起動を指示して再起動し、アプリが正常起動を自分で申告する。申告が来なければブートローダーが前の面へ戻す

配る側との通信は一切要りません。ネットワークが切れていようが、機械は単独で復帰します。push 型で手続きとして書いていたロールバックが、ここでは機械の性質になっています。

ただし、この申告は機械の中のブートローダー宛てで配る側には届かないので、戻ったことを配る側が知るには、更新エージェントがサーバーへ結果を報告する経路が別に要ります。Mender はこの報告をデバイス側の API に持っています。

起動成功の申告は機械の中で閉じ、配る側へは別の経路で報告する。機械の中では、アプリケーションが正常に立ち上がったことを自分でブートローダーへ申告し、ブートローダーは申告が来なければ誰の指示も待たずに前の面へ戻す。ハードウェアのウォッチドッグを併用すると固まった状態も検出できる。前の面に戻る判断に配る側との通信は要らないが、戻ったことも配る側には届かない。配る側のサーバーに知らせるのは更新エージェントで、結果をサーバーへ報告する (Mender はデバイス側 API に持つ)。報告の経路が無ければ、サーバーは機械が戻ったことを知らない

もう1つ、RAUC は署名を必須にしています。「バンドルに署名することは必須である」と設計判断として明記されており、署名なしの更新を流し込む経路がありません。RAUC を選べば、パッケージ型と同じく経路ではなく中身を信じる型になります。ただしこれは RAUC の設計判断で、A/B 型の実装すべてに当てはまるわけではありません。A/B 構成を組める SWUpdate は、ビルド時の設定(CONFIG_SIGNED_IMAGES)で有効にしたときに署名を検証する作りです。

何を買っているのか

ロールバックの軸だけを見ると圧勝に見えますし、実際現地に人が行けない機械ではこれ以外の選択肢が事実上ありません。壁の中の機械、遠隔地の機械、顧客先に納めた機械。文鎮化が許されない状況では、A/B の2面ぶんの領域は保険料として安く付きます。

代償は3つあります。

常駐コストが5方式で最大です。更新エージェントが常駐し、ブートローダー側にも状態を持たせ、そのうえで起動成功の申告を出す仕組みをアプリケーション本体に組み込むことになるので、「入れて終わり」の道具ではありません。

帯域も最大です。設定ファイル1行の変更で、システム領域ぶんが転送されます。差分更新の仕組みを持つ実装もありますが、基本は全体です。

再起動が要ります。数秒であっても、動いている機械を止めることになります。24時間動き続ける前提のものだと、これが制約になります。

A/B 型で買っているのは、現地に行かずに前の面へ戻れること。買うものは、壊れても機械が単独で前の面に戻ること。壁の中の機械、遠隔地の機械、顧客先に納めた機械のように文鎮化が許されないなら、事実上これ以外の選択肢がない。払う代償は3つ。常駐コストは最大で、更新エージェントが常駐し、ブートローダーにも状態を持たせ、起動成功の申告をアプリ本体に組み込む。帯域も最大で、設定1行の変更でもシステム領域ぶんを転送する (差分更新を持つ実装もあるが、基本は全体)。再起動が要り、数秒でも動いている機械を止めるので、24時間動き続ける前提なら制約になる。比べる相手は、文鎮化した機械を直しに行く交通費と、2面ぶんのストレージの値段

イメージ型の位置

A/B とファイル配布の中間にいます。

再起動が要らず、A/B ほどの領域も要りません。イメージという単位で丸ごと置き換わるので、「機械の上でだけ壊れる」種類の事故が減ります。手元で動いたイメージが、そのまま機械の上でも動く。

ただしロールバックは A/B ほど強くありません。前のイメージに戻すこと自体は簡単ですが、問題は戻す判断を誰がするかで、新しいコンテナが起動してしばらく動いてから壊れた場合、健全性の確認と自動復帰を自分で組み込んでいなければ、結局は push 型と同じ「手続きとして書く」領域へ戻ります。

イメージ型で問われるのは、戻す判断を誰がするか。前のイメージに戻すこと自体は簡単。新しいイメージでコンテナを作り直し、起動してしばらく動いたあとで壊れたとき、前のイメージに戻すのは誰か。健全性の確認と自動復帰を組み込んであれば、仕組みが判断して前のイメージへ戻す。組み込んでいなければ人が手順で戻すことになり、push 型と同じ「手続きとして書く」領域へ戻る。A/B 型では、起動成功の申告が来なければブートローダーが前の面へ戻す。判断役が最初から仕組みに入っているかどうかが、ロールバックの強さの差

そしてコンテナランタイムという常駐物が1つ増えます。設定ファイルを配りたいだけの機械に、そのために Docker を入れる判断は、素直とは言いにくいところです。

7軸で見ると

A/B 型で書きます。イメージ型はこの表と pull 型の中間だと考えてください。

軸評価補足
到達性◎機械側から取りに行く形にできます
常駐コスト✗5方式で最大。エージェント・ブートローダー連携・起動申告
権限の粒度△更新エージェントは強い権限を持ちます
ロールバック◎5方式で最強。機械が単独で前の面に戻ります
帯域✗領域ぶん転送。設定1行でも同じ
改ざん耐性◎(RAUC の場合)実装によります。RAUC は署名が必須、SWUpdate は有効にしたときだけ検証します
観測性○更新エージェントが配る側のサーバーへ結果を報告する実装があります(Mender など)

両端に振り切った表になります。ロールバックと改ざん耐性(RAUC の場合)で最高、常駐コストと帯域で最低。

このシリーズの共通課題(設定ファイル数十 KB を1台に、週数回)に当てると、明らかに過剰です。数十 KB のために数百 MB を転送し、再起動することになります。

ただし条件を1つ動かすだけで評価が反転します。現地に人が行けない。この条件が入った瞬間にロールバックの◎以外はすべて誤差になり、文鎮化した機械を直しに行く交通費と2面ぶんのストレージの値段を並べて比べる、という話に変わります。

次の章では、ここまで4方式で何度も出てきた署名の話を、まとめて扱います。

参考

  • RAUC — Basic Concepts — 非アクティブ領域への書き込み、ブートローダーとの分担、起動成功の記録とフォールバック、署名必須の設計判断
  • SWUpdate — Updates with signed images — 署名の検証は CONFIG_SIGNED_IMAGES で有効にしたときに働き、有効にすると実行時には外せないこと
  • Mender API — デバイス側 API の用途に配布状況の報告(Report deployment status)が含まれること