ПРИМЕЧАНИЕ . Все еще находится на стадии альфа-версии. 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 являются только максимальными усилиями; разделение мозга все еще может произойти, пока размер кластера все еще меняется. Дополнительные средства защиты кода добавляются для минимизации разделения мозга в этих сценариях, но полностью оно не устраняется. В моем варианте использования (и тестировании) постепенные изменения размера кластера в основном стабильны, а внезапные изменения с огромной разницей в размере — нет. Например, большой и внезапный скачок с трех узлов (минимальный размер zgroup) до, скажем, сотни из-за автомасштабирования может привести к разделению мозга. Однако, как только целевой размер будет достигнут, всегда будет избран единственный лидер.
Примечание о случайном диапазоне тайм-аута 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.
Предоставляется пример сценария запуска для тестирования zgroup на GCP MIG. Однако перед развертыванием обязательно обновите значение 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
Предоставляется пример сценария запуска для тестирования zgroup на AWS ASG. Однако перед развертыванием обязательно обновите значение 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 — настройка таймаутов для оптимизации распространения и конвергенции состояния. Текущая реализация тестировалась только в локальной сети.
Пиар приветствуется.