注意:仍处于 alpha 阶段。 API 可能会发生变化。
(此存储库镜像到 https://codeberg.org/flowerinthenight/zgroup)。
zgroup是一个 Zig 库,可以管理集群成员资格和成员故障检测。它结合了 SWIM 协议的八卦式信息传播和 Raft 的领导者选举算法(减去日志管理)来跟踪集群变化。
zgroup 的主要目标之一是能够以最小的依赖性和网络负载来跟踪大小可随时间动态变化的集群(例如 Kubernetes 部署、GCP 实例组、AWS 自动缩放组等)。到目前为止,我之前的所有相关工作都依赖于一些外部服务(参见spindle、hedge),使用传统的心跳来实现这一点。随着集群变大,这种心跳技术通常会受到负载大小(与集群大小成比例)增加的影响。但我想要一个不会受到这种副作用影响的系统。输入 SWIM 的感染式信息传播。无论集群大小如何,它都可以使用恒定的有效负载大小。 SWIM 使用PING
、 INDIRECT-PING
和ACK
的组合来检测成员故障,同时搭载这些相同的消息来传播成员更新(八卦协议)。目前,zgroup仅使用SWIM的直接探测协议;它尚未完全实现 Suspicion 子协议。
目前,zgroup 对所有消息使用单个 64 字节有效负载,包括领导者选举(见下文)。
我还想要某种不依赖外部锁定服务的领导者选举功能。目前,zgroup使用Raft的领导者选举算法子协议(没有日志管理)来实现这一点。需要注意的是,Raft 的领导者选举算法依赖于稳定的成员资格才能正常工作,因此 zgroup 的领导者选举只是尽力而为的基础;当簇大小仍在变化时,裂脑仍然可能发生。添加了额外的代码保护以最大限度地减少这些场景中的裂脑,但它并没有完全消除。在我的用例(和测试)中,逐渐的集群大小变化大多是稳定的,而具有巨大大小增量的突然变化则不然。例如,由于自动缩放,从三个节点(zgroup 的最小大小)突然大幅跳跃到一百个节点,会导致裂脑。然而,一旦达到目标规模,将始终选出一位领导者。
关于领导者选举期间 Raft 的随机超时范围的说明:zgroup 的领导者跟踪 ping 延迟平均值并尝试相应地调整超时范围以适应集群大小随时间的变化。
对于要加入现有集群的节点,它需要一个加入地址。虽然 zgroup 为此公开了join()
函数,但它还提供了回调机制,为调用者提供加入地址。然后可以将该地址存储到外部存储中以供其他节点使用。在内部,zgroup 使用组中具有最高 IP(v4) 地址的节点。
提供了一个示例二进制文件来展示使用该库的方法。有两种方法可以运行示例:
# 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 部署文件来在 Kubernetes 部署上尝试 zgroup。不过,在部署之前,请确保更新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。