참고 : 아직은 알파 단계입니다. API는 변경될 수 있습니다.
(이 저장소는 https://codeberg.org/flowerinthenight/zgroup에 미러링되어 있습니다).
zgroup 은 클러스터 멤버십 및 멤버 오류 감지를 관리할 수 있는 Zig 라이브러리입니다. SWIM 프로토콜의 가십 스타일 정보 전파와 Raft의 리더 선택 알고리즘(로그 관리 제외)의 조합을 사용하여 클러스터 변경 사항을 추적합니다.
zgroup의 주요 목표 중 하나는 종속성과 네트워크 부하를 최소화하면서 시간이 지남에 따라 동적으로 변경될 수 있는 크기의 클러스터(예: Kubernetes 배포, GCP 인스턴스 그룹, AWS 자동 확장 그룹 등)를 추적할 수 있는 것입니다. 지금까지 나와 관련된 모든 이전 작업은 이를 달성하기 위해 전통적인 하트비트를 사용하는 일부 외부 서비스(스핀들, 헤지 참조)에 의존합니다. 이 하트비트 기술은 일반적으로 클러스터가 커짐에 따라 페이로드 크기(클러스터 크기에 비례)가 증가하는 문제를 겪습니다. 하지만 저는 그런 부작용을 겪지 않는 시스템을 원했습니다. SWIM의 감염형 정보 전파에 들어갑니다. 클러스터 크기에 관계없이 일정한 페이로드 크기를 사용할 수 있습니다. SWIM은 PING
, INDIRECT-PING
및 ACK
의 조합을 사용하여 회원 실패를 감지하는 동시에 동일한 메시지를 편승하여 회원 업데이트(가십 프로토콜)를 전파합니다. 현재 zgroup은 SWIM의 직접 검색 프로토콜만 사용합니다. (아직) Suspicion 하위 프로토콜을 완전히 구현하지 않았습니다.
현재 zgroup은 리더 선택(아래 참조)을 포함하여 모든 메시지에 대해 단일 64바이트 페이로드를 사용합니다.
또한 외부 잠금 서비스에 의존하지 않고 일종의 리더 선택 기능도 원했습니다. 현재 zgroup은 이를 달성하기 위해 Raft의 리더 선택 알고리즘 하위 프로토콜(로그 관리 없이)을 사용합니다. Raft의 리더 선택 알고리즘은 제대로 작동하려면 안정적인 멤버십에 의존하므로 zgroup의 리더 선택은 최선의 노력을 기반으로 합니다. 클러스터 크기가 계속 변경되는 동안에도 분할 브레인이 발생할 수 있습니다. 이러한 시나리오에서는 분할 브레인을 최소화하기 위해 추가 코드 가드가 추가되지만 완전히 제거되지는 않습니다. 내 사용 사례(및 테스트)에서 점진적인 클러스터 크기 변경은 대부분 안정적이지만 대규모 델타의 갑작스러운 변경은 그렇지 않습니다. 예를 들어, 자동 크기 조정으로 인해 노드 3개(zgroup의 최소 크기)에서 노드 100개로 갑자기 크게 점프하면 분할 브레인이 발생합니다. 그러나 목표 규모가 달성되면 항상 단일 리더가 선출됩니다.
리더 선택 중 Raft의 임의 시간 초과 범위에 대한 참고 사항: zgroup의 리더는 핑 대기 시간 평균을 추적하고 시간에 따른 클러스터 크기 변경을 수용하기 위해 그에 따라 시간 초과 범위를 조정하려고 시도합니다.
노드가 기존 클러스터에 가입하려면 가입 주소가 필요합니다. 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 배포에서 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을 환영합니다.