注: まだアルファ段階です。 API は変更される可能性があります。
(このリポジトリは https://codeberg.org/flowerinthenight/zgroup にミラーリングされています)。
zgroup は、クラスターのメンバーシップとメンバーの障害検出を管理できる Zig ライブラリです。 SWIM プロトコルのゴシップ形式の情報配布と、Raft のリーダー選出アルゴリズム (ログ管理を除く) を組み合わせてクラスターの変更を追跡します。
zgroup の主な目標の 1 つは、時間の経過とともに動的に変化するサイズのクラスター (Kubernetes デプロイメント、GCP インスタンス グループ、AWS 自動スケーリング グループなど) を最小限の依存関係とネットワーク負荷で追跡できるようにすることです。これまでの私の関連作業はすべて、これを実現するために従来のハートビートを使用する外部サービス (スピンドル、ヘッジを参照) に依存しています。このハートビート手法では通常、クラスターが大きくなるにつれてペイロード サイズが (クラスター サイズに比例して) 増加するという問題が発生します。しかし、私はその副作用に悩まされないシステムを望んでいました。 SWIMの感染型情報発信に入ります。クラスターのサイズに関係なく、一定のペイロード サイズを使用できます。 SWIM は、 PING
、 INDIRECT-PING
、およびACK
の組み合わせを使用してメンバーの障害を検出し、これらの同じメッセージに便乗してメンバーシップの更新を伝播します (ゴシップ プロトコル)。現在、zgroup は SWIM の直接プローブ プロトコルのみを使用します。 Suspicion サブプロトコルは (まだ) 完全には実装されていません。
現時点では、zgroup は、リーダーの選出を含むすべてのメッセージに単一の 64 バイトのペイロードを使用します (以下を参照)。
また、外部のロック サービスに依存せずに、ある種のリーダー選出機能も必要でした。現時点では、zgroup はこれを実現するために Raft のリーダー選出アルゴリズム サブプロトコル (ログ管理なし) を使用しています。 Raft のリーダー選出アルゴリズムは適切に機能するために安定したメンバーシップに依存しているため、zgroup のリーダー選出はベストエフォート型のみであることに注意してください。クラスターのサイズがまだ変化している間も、スプリット ブレインが発生する可能性があります。このようなシナリオではスプリット ブレインを最小限に抑えるためにコード ガードが追加されますが、完全に排除されるわけではありません。私のユースケース (およびテスト) では、クラスター サイズの段階的な変更はほとんど安定していますが、巨大なサイズのデルタによる突然の変更は安定しません。たとえば、自動スケーリングにより 3 つのノード (zgroup の最小サイズ) から、たとえば 100 ノードに大きく突然変化すると、スプリット ブレインが発生します。ただし、目標規模が達成されると、常に 1 人のリーダーが選出されます。
リーダー選出中の Raft のランダムなタイムアウト範囲に関するメモ: zgroup のリーダーは、ping 遅延の平均を追跡し、時間の経過によるクラスター サイズの変化に対応するために、それに応じてタイムアウト範囲を調整しようとします。
ノードが既存のクラスターに参加するには、参加アドレスが必要です。 zgroup はこのためにjoin()
関数を公開していますが、コールバック メカニズムも提供し、呼び出し元に結合アドレスを提供します。このアドレスは、他のノードが使用できるように外部ストアに保存できます。内部的には、zgroup はグループ内で最も高い IP(v4) アドレスを持つノードを使用します。
ライブラリの使用方法を示すサンプル バイナリが提供されています。サンプルを実行するには 2 つの方法があります。
# Build the sample binary:
$ zig build --summary all
# Run the 1st process. The expected args look like:
#
# ./zgroup groupname member_ip:port [join_ip:port]
#
# Run the first process (join to self).
$ ./zig-out/bin/zgroup group1 0.0.0.0:8080 0.0.0.0:8080
# Then you can run additional instances.
# Join through the 1st process/node (different terminal):
$ ./zig-out/bin/zgroup group1 0.0.0.0:8081 0.0.0.0:8080
# Join through the 2nd process/node (different terminal):
$ ./zig-out/bin/zgroup group1 0.0.0.0:8082 0.0.0.0:8081
# Join through the 1st process/node (different terminal):
$ ./zig-out/bin/zgroup group1 0.0.0.0:8083 0.0.0.0:8080
# and so on...
構成されている場合、サンプル バイナリは無料サービス https://keyvalue.immanuel.co/ を結合アドレスのストアとして使用します。
# Build the sample binary:
$ zig build --summary all
# Generate UUID:
$ uuidgen
{output}
# Run the 1st process. The expected args look like:
#
# ./zgroup groupname member_ip:port
#
# Run the first process:
$ ZGROUP_JOIN_PREFIX={output} ./zig-out/bin/zgroup group1 0.0.0.0:8080
# Add a second node (different terminal):
$ ZGROUP_JOIN_PREFIX={output} ./zig-out/bin/zgroup group1 0.0.0.0:8081
# Add a third node (different terminal):
$ ZGROUP_JOIN_PREFIX={output} ./zig-out/bin/zgroup group1 0.0.0.0:8082
# Add a fourth node (different terminal):
$ ZGROUP_JOIN_PREFIX={output} ./zig-out/bin/zgroup group1 0.0.0.0:8083
# and so on...
Kubernetes デプロイメントで zgroup を試すためのサンプル Kubernetes デプロイメント ファイルが提供されています。ただし、デプロイする前に、次のようにZGROUP_JOIN_PREFIX
環境変数を更新してください。
# Generate UUID:
$ uuidgen
{output}
# Update the 'value' part with your output.
...
- name: ZGROUP_JOIN_PREFIX
value: " {output} "
...
# Deploy to Kubernetes:
$ kubectl create -f k8s.yaml
# You will notice some initial errors in the logs.
# Wait for a while before the K/V store is updated.
GCP MIG で zgroup を試すためのサンプル起動スクリプトが提供されています。ただし、デプロイする前に、スクリプト内のZGROUP_JOIN_PREFIX
値を次のように更新してください。
# Generate UUID:
$ uuidgen
{output}
# Update the 'value' part of ZGROUP_JOIN_PREFIX with your output.
...
ZGROUP_JOIN_PREFIX={output} ./zgroup group1 ...
# Create an instance template:
$ gcloud compute instance-templates create zgroup-tmpl
--machine-type e2-micro
--metadata=startup-script= ' ' " $( cat startup-gcp-mig.sh ) " ' '
# Create a regional MIG:
$ gcloud compute instance-groups managed create rmig
--template zgroup-tmpl --size 3 --region {your-region}
# You can view the logs through:
$ tail -f /var/log/messages
AWS ASG で zgroup を試すためのサンプル起動スクリプトが提供されています。ただし、デプロイする前に、スクリプト内のZGROUP_JOIN_PREFIX
値を次のように更新してください。
# Generate UUID:
$ uuidgen
{output}
# Update the 'value' part of ZGROUP_JOIN_PREFIX with your output.
...
ZGROUP_JOIN_PREFIX={output} ./zgroup group1 ...
# Create a launch template. ImageId here is Amazon Linux, default VPC.
# (Added newlines for readability. Might not run when copied as is.)
$ aws ec2 create-launch-template
--launch-template-name zgroup-lt
--version-description version1
--launch-template-data '
{
"UserData":" ' " $( cat startup-aws-asg.sh | base64 -w 0 ) " ' ",
"ImageId":"ami-0f75d1a8c9141bd00",
"InstanceType":"t2.micro"
} '
# Create the ASG:
$ aws autoscaling create-auto-scaling-group
--auto-scaling-group-name zgroup-asg
--launch-template LaunchTemplateName=zgroup-lt,Version= ' 1 '
--min-size 3
--max-size 3
--availability-zones {target-zone}
# You can view the logs through:
$ [sudo] journalctl -f
グループの現在のメンバーを取得するには、次のようなことを試してください。
const members = try fleet . getMembers ( gpa . allocator ());
defer members . deinit ();
for ( members . items , 0 .. ) | v , i | {
defer gpa . allocator (). free ( v );
log . info ( "member[{d}]: {s}" , .{ i , v });
}
zgroup を使用する際の注意が必要な部分は、状態の伝達と収束を最適化するためにタイムアウトを構成することです。現在の実装はローカル ネットワーク内でのみテストされました。
PRの方も大歓迎です。