156Часть II. Паттерны проектирования обслуживающих систем Есть два способа реализовать выбор владельца. Первый — создать распределенный алгоритм согласования вроде Paxos или RAFT. Сложность этих алгоритмов выводит их за рамки книги и делает их дальнейшее рассмотрение нецелесообраз-ным. Реализация какого-нибудь из них сравнима с реали-зацией блокировок с помощью ассемблерных инструкций сравнения и обмена. Из нее выйдет хорошая задачка для уни-верситетского курса информатики, но на практике так делать не стоит.
К счастью, есть ряд распределенных хранилищ типа «ключ — значение», в которых эти алгоритмы уже реализованы. В общем и целом эти системы предоставляют реплицированные, на-дежные хранилища, а также примитивы, необходимые для по-строения сложных механизмов выбора и блокировки. К таким хранилищам относятся, к примеру, etcd, ZooKeeper и consul. К базовым примитивам, предоставляемым подобными система-ми, относится операция сравнения с заменой для конкретного ключа. Если вы ранее не сталкивались с операцией сравнения с заменой, то знайте: она представляет собой атомарную опера-цию следующего вида:
var lock = sync.Mutex{}
var store = map[string]string{}
func compareAndSwap(key, nextValue, currentValue string) (bool, error) {
lock.Lock()
defer lock.Unlock()
if _, found := store[key]; !found {
if len(currentValue) == 0 {
store[key] = nextValue
return true, nil
}
return false, fmt.Errorf("Для ключа %s ожидалось значение %s, но было обнаружено пустое значение", key, currentValue) Глава 9. Выбор владельца 157
if store[key] == currentValue {
store[key] = nextValue
return true, nil
}
return false, nil
}
Операция сравнения с заменой атомарно записывает новое значение ключа, если его текущее значение совпадает с ожи-даемым. В противном случае она возвращает false . Если зна-чения не существует, а currentValue не равно null , она воз-вращает ошибку.
Вдобавок к операции сравнения с заменой хранилища типа «ключ — значение» позволяют устанавливать для ключа срок действия (TTL, time-to-live). Как только он истекает, значение ключа обнуляется.
Этих двух функций достаточно, чтобы реализовать множество примитивов синхронизации.
Практикум. Развертывание etcd Etcd ( https://coreos.com/etcd/docs/latest/ ) — распределенный сервис блокировок, разработанный в рамках проекта CoreOS. Он до-вольно устойчив и хорошо себя зарекомендовал в различных проектах, в том числе в Kubernetes.
Развертывание etcd существенно упростилось благодаря двум проектам с открытым исходным кодом:
helm ( https://helm.sh/ ) — менеджеру пакетов Kubernetes, под-держиваемому Microsoft Azure;
оператору etcd ( https://coreos.com/blog/introducing-the-etcd-operator.html), разработанному в рамках проекта CoreOS.
158Часть II. Паттерны проектирования обслуживающих систем
приложений. Оператор отвечает за создание, масштаби-
рование и поддержку функционирования приложения. Пользователи настраивают приложение, задавая его жела-емое состояние с помощью API. К примеру, оператор etcd отвечает за сервис etcd. Операторы — относительно новая задумка, но они представляют собой важное направление
Чтобы развернуть оператор etcd в CoreOS, воспользуемся ме-неджером пакетов helm. Helm — менеджер пакетов, являющий-ся частью Kubernetes и разработанный Deis. Microsoft Azure поглотила Deis в 2017 году, и Microsoft с тех пор продолжает поддерживать open-source-разработку данного проекта. Если вы впервые используете helm, вам понадобится установить его согласно инструкции по адресу https://github.com/kubernetes/ helm/releases .
После запуска helm в своей среде вы можете установить опера-тор etcd следующим образом:
# Инициализация helm
helm init
# Установка оператора etcd
helm install stable/etcd-operator
После установки оператор etcd создает собственный класс Kubernetes-ресурсов, представляющих собой etcd-кластеры. Оператор запущен, но кластеры etcd еще не созданы. Для соз-дания etcd-кластера необходимо составить следующую декла-ративную конфигурацию:
apiVersion: "etcd.coreos.com/v1beta1"
kind: "Cluster"
Глава 9. Выбор владельца 159
metadata:
# Имя может быть любым
name: "my-etcd-cluster"
spec:
# size может принимать значения 1, 3 и 5
size: 3
# Устанавливаемая версия etcd
version: "3.1.0"
Сохраните данную конфигурацию в файл etcd-cluster.yaml и создайте кластер с помощью такой команды: kubectl create -f etcd-cluster.yaml
После создания кластера оператор etcd автоматически создаст pod-группы с копиями кластера. Копии кластера можно уви-деть, выполнив команду:
kubectl get pods
После запуска всех трех копий адреса точек доступа к ним мож-но получить с помощью такой команды:
Читать дальше