Распределенное KV-хранилище на базе etcd

Недавно передо мной встал вопрос выбора системы хранения инфраструктурных данных для небольшого проекта. Объем — несколько тысяч записей, основные требования — система должна быть распределенной, отказоустойчивой и достаточно простой в обслуживании. Я выбирал из всего спектра NoSQL баз данных и KV-хранилищ и остановил свой выбор на распределенном KV-кластере на базе etcd.

В этой статье я постараюсь обосновать этот выбор и рассказать, не углубляясь в технические дебри, об этой технологии в научно-популярном ключе: что это вообще такое, где применяется и почему я выбрал именно etcd. Всё-таки, KV-хранилища — это достаточно нишевая тема, даже для IT.

Общая концепция

KV (key-value, «ключ-значение») — одна из старейших концепций в Computer Science. По сути, это структура данных, которая хранит пары: ключ и соответствующее ему значение. Примеры пар «ключ-значение» окружают нас повсюду:

Подобные структуры используются, когда нужно быстро находить что-то по известному признаку. Не углубляясь в теорию, отметим главное преимущество: такая структура обеспечивает практически мгновенный поиск нужного значения по ключу, без перебора всего массива данных. Это работает по принципу телефонной книги — зная фамилию человека, вы сразу открываете нужную букву, а не листаете весь справочник от корки до корки.

Концепция реализована на базе хеш-таблиц, впервые описанных Гансом Петером Луном в IBM в 1953 году — это раньше, чем появились такие языки программирования как Fortran (1957), COBOL (1959) и C (1972). Основной поинт здесь в том, что идея эта не только не нова, но и намного древнее чем большинство из современных языков программирования.

В языках программирования эта концепция реализована в виде коллекций, в просторечье называемых “мапами”. Их могут называть словарями (python), картами (go, js), ассоциативными массивами (php), а также хэш-картами (java) или хеш-таблицами (как бы намекая, что под капотом используется технология хеширования), в зависимости от контекста и сферы применения. Детали реализации различаются, но на уровне общей идеи — это одно и то же.

Но одно дело — KV-коллекция в памяти одной программы на одном компьютере. А что, если такие данные нужно хранить на нескольких серверах одновременно — и гарантировать, что все они видят одно и то же?

От словаря — к инфраструктуре

В сетевой инфраструктуре масса задач, которые по своей природе сводятся к работе с парами «ключ-значение»:

Наличие спроса на решение подобных задач закономерно породило предложение: быстродействие и простота хеш-таблиц не могла остаться без внимания разработчиков инфраструктуры. В этой нише концепция реализована в виде KV-хранилищ, и представлена широким спектром решений от разных разработчиков, каждое из которых сконцентрировано на том или ином аспекте:

Redis — пожалуй, самый известный. Хранит данные в оперативной памяти, невероятно быстрый. Его используют Twitter, GitHub, StackOverflow — но именно как кэш и брокер сообщений, а не как источник истины. Консистентность — не его сильная сторона: если нода упала, данные могут потеряться. Для кэша это нормально. Для хранения состояния инфраструктуры — нет.

ZooKeeper — ветеран. Создан в Apache для экосистемы Hadoop, используется Kafka. Надёжный, проверенный временем. Но: написан на Java (тяжёлый), сложный API, непростая эксплуатация. У ZooKeeper есть полушутливая репутация в индустрии: он настолько сложен в обслуживании, что для его поддержки нужен отдельный зоопарк инженеров.

Consul (HashiCorp) — больше, чем KV-хранилище. Это целая платформа: service discovery, health checking, service mesh. KV в нём есть, но оно — часть большой экосистемы. Мощный инструмент, но для задачи «просто хранить ключи» — избыточен. Используется, когда нужна вся экосистема HashiCorp (Vault, Nomad, Terraform).

etcd — создан командой CoreOS специально для распределённого хранения конфигураций. Выбран как основа Kubernetes — и это, пожалуй, лучшая рекомендация. Компактный, написан на Go (один бинарник), простой HTTP/gRPC API, строгая консистентность через протокол Raft.

Все эти решения оптимизированы для скорости и минимальной задержки, активно используя оперативную память для быстрого доступа к данным (при этом etcd и ZooKeeper надёжно сохраняют данные на диск). Но различаются они не столько скоростью, сколько подходом к куда более фундаментальной проблеме.

Проблема, у которой нет идеального решения

Помимо задачи хранения и обработки в реальном времени, есть ещё один класс проблем, на решение которых направлены усилия всех перечисленных систем. Это задачи синхронизации данных и обеспечения консистентности информации.

Представьте, что у вас работают сотни серверов — которые обслуживают один и тот же сервис, работают с одними и теми же данными и выполняют, в целом, одну и ту же функцию. При этом обновление информации происходит не мгновенно и не одновременно на всех узлах: когда в одном сегменте сети информация обновилась — другой об этом может ещё ничего не знать.

В таких системах всегда стоит задача обновления устаревающей информации, разрешения конфликтов и достижения трёх целей:

Проблема в том, что эти три цели внутренне противоречивы и взаимоисключающи. Это не предположение, а математически доказанное ограничение — CAP-теорема, сформулированная Эриком Брюером в 2000 году и формально доказанная Сетом Гилбертом и Нэнси Линч из MIT в 2002-м. Своего рода закон физики для распределённых систем: из трёх — можно выбрать только два.

Каждое решение в такой системе — это всегда компромисс между скоростью ответа и гарантией актуальности данных, между доступностью и согласованностью, между простотой и надёжностью.

И вот тут становится понятно, почему перечисленные выше KV-хранилища такие разные — они по разному подходят к поиску компромисса в одной и той же неразрешимой задаче:

Raft: протокол согласия

Для нашего проекта мы сделали выбор в пользу консистентности и устойчивости. Решать вопросы консистентности можно по-разному, и одним из решений является протокол Raft.

Raft — это протокол консенсуса. Он даёт чёткие ответы на то, как и в какой последовательности нужно совершать действия, чтобы данные в распределённой системе не теряли согласованность и сохранялись даже при выходе из строя отдельных узлов.

Работает он по принципу голосования:

Отсюда правило нечётного числа узлов: кластер из нечетного числа узлов (3, 5, …) выдерживает отказ до (N-1)/2 узлов, сохраняя работоспособность.

Это правило означает, что кластер из 3 узлов переживёт потерю 1, из 5 — потерю 2. Кластер из 2 узлов бесполезен — потеря одного означает потерю большинства. Как в парламенте: решение принято, когда большинство проголосовало «за», даже если кто-то покинул зал.

Осознанный выбор

etcd — компактное KV-хранилище, построенное на протоколе Raft. Оно делает одну вещь — и делает её хорошо. Эта философия идеально вписывается в наши задачи — несложое решение, обеспечивающих базовую функциональность.

Для нашего проекта etcd закрывает ключевые потребности:

Это осознанный выбор — потому что etcd оптимален для нашего масштаба. Возможно, со временем мы перейдём на более комплексные решения — например, Consul, который помимо KV предоставляет встроенный health checking и интеграцию с системами мониторинга. Но на данном этапе etcd — ровно столько, сколько нужно. Не больше и не меньше.

Мой канал в телеграм

@A1exMa
21.04.2026 14:50 UTC
Первоисточник