Что такое etcd и для чего он используется?

«Что такое etcd и для чего он используется?» — вопрос из категории DevOps, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

etcd — это распределённое, отказоустойчивое key-value хранилище, спроектированное для надежного хранения критически важных данных в распределённых системах. Его основное назначение — быть распределённым источником истины (source of truth) для конфигурации и обнаружения сервисов.

Ключевые особенности:

  • Консенсус: Использует алгоритм Raft для обеспечения согласованности данных между узлами кластера.
  • Высокая доступность (HA): Данные реплицируются между несколькими узлами, что обеспечивает отказоустойчивость.
  • Watch-механизм: Клиенты могут подписываться на изменения ключей (watch), получая уведомления в реальном времени.
  • Транзакции: Поддержка атомарных операций сравнения-и-установки (Compare-and-Swap, CAS) и сравнения-и-удаления (CAD).
  • Простой HTTP/gRPC API: Легко интегрируется с различными языками программирования.

Главный сценарий использования: Kubernetes использует etcd как бэкенд-хранилище для всех данных кластера:

  • Состояние всех объектов (поды, сервисы, деплойменты).
  • Конфигурации (ConfigMaps, Secrets).
  • Информация о нодах и их статусе.

Пример базовых операций через etcdctl (CLI):

# Записать значение
etcdctl put /app/config/database_url "postgres://localhost:5432"

# Прочитать значение
etcdctl get /app/config/database_url

# Прослушивать изменения ключа (watch)
etcdctl watch /app/config/database_url

# Выполнить атомарную транзакцию (CAS)
etcdctl txn --interactive
# В интерактивном режиме:
compare:
  value("/app/leader") = "node1"

success requests (запускаются, если сравнение верно):
put /app/leader "node2"

failure requests (если не верно):
put /app/leader "node1"

Пример на Go (используя официальный клиент):

import "go.etcd.io/etcd/client/v3"

func main() {
    cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"localhost:2379"},
        DialTimeout: 5 * time.Second,
    })
    // Запись
    cli.Put(context.Background(), "key", "value")
    // Чтение
    resp, _ := cli.Get(context.Background(), "key")
    for _, ev := range resp.Kvs {
        fmt.Printf("%s : %sn", ev.Key, ev.Value)
    }
}

Альтернативы: Consul, ZooKeeper, Apache ZooKeeper.