Kubernetes: WeaveによるPodへのIPアドレス割り当て(4)

前回の記事(3)の続きです。
今回はWeaveのドキュメントを見てみます。

以下のURLに掲載されているWeave IPAMについてのドキュメントipam.mdから主要な箇所を抜粋して訳(抄訳/意訳)しています。適宜、補足説明を加えて訳しています。
https://github.com/weaveworks/weave/blob/master/docs/ipam.md

Weaveはクラスタに対してネットワークアドレス( 例:10.32.0.0/12 )を割り当て、それを各クラスタノード(ピア:peer)毎に分割します。各ノードはそのアドレス範囲から自身のノードのPodに対してIPを割り当てます。Podが削除された場合はそのIPを解放します。

CRDT(Convergent Replicated Data Type。後述)を使用し、アドレス空間の情報をWeave Gossipメカニズムによって共有します。あるノードのIPの個数が不足した場合、ポイントツーポイントメッセージにより他のノードのアドレス空間からの自身のノードへの追加をリクエストします。

各ピアで実行されているアロケータ(allocator)は、コンテナインフラ(WeaveスクリプトやDockerプラグインなど)からのIPアドレス要求を受け付けるhttpインターフェースを持っています。

[補足説明1]
アロケータはweaveコンテナ上で稼働しているweaverデーモンの中にIPAMの機能として組み込まれています。
weaverデーモンは6782番、6783番、6784番のポートでHTTPリクエストを待ち受けています。
[補足説明2]
Weaveは元々はDockerをマルチノードで利用するためのソフトウェアなので、このドキュメントでは「IPアドレスのコンテナに対しての割り当て」として説明されていますが、KubernetesではコンテナではなくPodになります。この箇所については「コンテナ」を「Pod」に置き換えて訳しています。

●コマンド
httpインターフェースを介してアロケータがサポートするコマンドは以下の通りです。

[補足説明]
それぞれ、関数Allocate()、Lookup()、Free()、Claim()が処理を行います。

・Allocate: Podのための1個のIPアドレスを要求する
・Lookup: Podのための以前割り当てられたIPアドレスを取得する
・Free: 現在割り当てられているIPアドレスを返す
・Claim: Podのための特定のIPアドレスを要求する(例:Podが既にそのアドレスを使用している場合、など)

http の各リクエストは、サブネットを指定するか、サブネットが指定されていない場合は、あらかじめ定義されたデフォルトのサブネットへの割り当てリクエストとみなされます。

アロケータはイベントメカニズムを介して監視も行います。Podが死んだ場合、そのPodに割り当てられていたすべてのIPアドレスは解放されます。

●定義
・ Allocations(割り当て): 特定の1個のIPアドレスをPodに割り当てること。
・ Range(範囲): ほとんどの場合、個々のIPアドレスを扱うのではなく、連続したグループで操作することが多く、これをRangeと呼びます。
・ Ring(リング、環): アドレス空間をリング(環)として扱い、そのRange(範囲)は最上位のアドレスが最下位のアドレスに続く形で回り込んでいます。
・ Peer(ピア): ピアはWeaveネットワーク上のノードです。ピアは0個以上のRangeを所有することができます。

●アドレス割り当てのプロセス
ピアがあるRangeを所有している場合、そのRange内であれば、同じピア上のPodに自由にアドレスを割り当てることができます。サブネット内のアドレス空間が足りなくなった場合(所有するすべてのRangeがPodで一杯になった場合)、他のピアにアドレス空間を要求します:

・サブネット内の各ピアが所有するアドレス空間の量で加重して、依頼の対象となるピアをランダムに選びます。
・対象ピアがアドレス空間を提供することを決定した場合、要求ピアに対して、新たに更新されたRingを含むメッセージをユニキャストで返します。
・ 対象ピアが空きアドレス空間を持っていない場合、要求ピアが古い情報に基づいて要求したと判断し、現在のRingのコピーを含むメッセージを要求ピアにユニキャストで返します。
・ 要求ピアはアドレス空間を受け取るか、サブネット内のすべてのピアが満杯であることをRingのコピーが伝えるまで、要求し続けます。

●データ永続性
IPAMの主要データは、各ノードのweavedb(/weavedb/weave-netdata.db)というBoltDBファイルに保存されます。
このDBファイル以下のデータを含み、様々なWeave Netコンポーネントによって利用されます。
・ ピア間のIPアドレス割り当て範囲の分割
・ローカルピア上のPodへのIPアドレス割り当て

ピアの再起動時に直ちに参照されて、ピアを再起動前の状態に復元できます。

[補足説明]
このDBファイルはPodの生成、削除のたびに更新されます。
BoltDB(Pure Go key/value database)はキーバリューストア(kvs)形式で、Go言語のデータベースとしてよく使われているようです。
Weaveの場合、値はgobというバイナリ形式にエンコードされて保存されています。
https://github.com/boltdb/bolt
https://pkg.go.dev/encoding/gob

●Podに割り当てるIPアドレスの要求
Weave Podが再起動された場合は、以下のように処理が行われます。

weavedbが存在する場合、前のセクションで説明したように、そこから永続的なIPAMデータをロードします。
weavedbが存在しない場合、他のピアから、かつて所有していたRange(範囲)の情報を得ます。

Weave Netはその後、Podに割り当てられた個々のIPアドレスの情報をアロケータから取得し、以降の割り当て要求に対して同じアドレスを与えないようにします。
(※ 以降の訳は省略。)

●Ring CRDT
Convergent Replicated Data Typ( CRDT ) を使用することで、中央での調整なしにピアが同時に変更を行い、それを伝達できるようにしています。これを実現するために、ピアは自分が所有するRangeのデータ構造にのみ変更を加えるようにしています。

データ構造はトークン(Token)の集合で、それぞれが所有するピアの名前を含んでいます。1つのピアは複数のRangeを所有することができます。各トークンはRangeの開始アドレスに配置され、各Rangeが1つのトークンから次のトークンへと続くようにセットは順序付けられています。リング上の各Rangeには、開始アドレスは含まれますが、終了アドレスは含まれません。Rangeは折り返しているので、最後のトークンの「次」のトークンが最初のトークンとなります。

●初期化
これまでのセクションでは、ピアがリングの変更を調整する方法を説明しました。
では、リングの初期状態はどのようにして確立されるのでしょうか?
長く稼働しているクラスタに新しいピアが加わった場合、そのピアは他のピアからリングの状態を知ることができます。しかし、新規のクラスタではリングの初期状態を白紙の状態から確立する必要があります。そして、それはすべてのピア間で一貫していなければなりません。これは分散型コンセンサス問題であり、Paxosアルゴリズムを用いて解決します。
参考URL: https://ja.wikipedia.org/wiki/Paxosアルゴリズム
(※ 以降の訳は省略。)

●ノードのシャットダウン
ピアが離脱するとき(weave resetコマンド)、ピアは自分のトークンをすべて別のピアに付与し、更新されたリングをブロードキャストします。
メッセージを送信した後、ピアは終了し、応答を待ちません。
(※ 以降の訳は省略。)

参考:Weaveソースコードのディレクトリ構成
(本ページの説明に出て来る内容に関係した箇所は赤字にしてあります。)

$ ls -F
CHANGELOG.md     Makefile          bin/                go.sum       proxy/             vendor/
CONTRIBUTING.md  NOTICE            build/              ipam/        router/            weave*
COPYING.LGPL-3   README.md         code-of-conduct.md  nameserver/  site/
DEPENDENCIES     SECURITY.md       common/             net/         test/
Gopkg.lock       VENDORED_CODE.md  db/                 npc/         testing/
Gopkg.toml       Vagrantfile       docs/               plugin/      tools/
LICENSE          api/              go.mod              prog/        vagrant-common.rb

$ head -1 CHANGELOG.md
## Release 2.8.1

$ ls -FR ipam/
ipam/:
allocate.go   allocator_test.go  http.go       paxos/    ring/   status.go          tracker/
allocator.go  claim.go           http_test.go  prime.go  space/  testutils_test.go

ipam/paxos:
participant.go  paxos.go  paxos_test.go

ipam/ring:
entry.go  ring.go  ring_test.go

ipam/space:
space.go  space_test.go

ipam/tracker:
awsvpc.go  tracker.go

$ ls -F prog/
kube-utils/  sigproxy/    weave-npc/  weaveexec/  weaveutil/
net-plugin/  weave-kube/  weavedb/    weaver/     weavewait/

$ ls -F db/
boltdb.go

$ ls -FR prog/weaver/
prog/weaver/:
Dockerfile.template  discovery.go  main.go       metrics.go  weavedata.db
checkpoint.go        http.go       main_test.go  reclaim.go  weaver*

$ ls -FR prog/kube-utils/
prog/kube-utils/:
annotations.go  kube_test.go  main.go  nodestatus.go  peerlist.go  peerlist_test.g