Keeper — самый недооценённый компонент инсталляции. Он не хранит ваши данные, потребляет мало ресурсов, и про него забывают — ровно до того момента, когда он теряет кворум и весь кластер переходит в read-only.
Зачем он нужен
ReplicatedMergeTree не хранит состояние репликации в самих узлах ClickHouse. Всё это живёт в Keeper:
- метаданные реплик и структура таблиц;
- очередь репликации — какие куски надо скачать/слить;
- выборы лидера для слияний;
- блокировки, дедупликация вставленных блоков, назначения мутаций.
Keeper — это встроенная в ClickHouse реализация того же протокола, что и ZooKeeper, но на Raft, заметно экономнее по ресурсам и рекомендуемая по умолчанию для новых установок. Если у вас legacy на ZooKeeper — он поддерживается, но новые кластеры делают на Keeper.
Что происходит, когда Keeper недоступен: реплицированные таблицы становятся доступны только на чтение. Вставки падают. Данные при этом целы, но приём данных стоит — а значит, встаёт и пайплайн.
Сколько узлов
Всегда нечётное число, минимум 3. Кворум — это большинство:
| Узлов | Кворум | Переживёт отказов |
|---|---|---|
| 1 | 1 | 0 (нет отказоустойчивости) |
| 3 | 2 | 1 |
| 5 | 3 | 2 |
Отсюда два практических правила:
- Два узла хуже одного. Кворум для двух — те же 2, то есть отказ любого узла ломает кворум, но при этом вы платите за две машины и добавляете сетевые задержки.
- Больше пяти обычно не нужно. Каждый дополнительный узел удлиняет согласование записи. 3 — стандарт, 5 — если хочется пережить одновременный отказ двух узлов (или две зоны из трёх).
Разносите узлы по зонам доступности. Классическая схема на три зоны: по одному Keeper в каждой — отказ зоны оставляет кворум 2 из 3.
Отдельно от ClickHouse
Самая частая архитектурная ошибка — держать Keeper тем же процессом или на тех же нодах, что и ClickHouse. Так делать не стоит:
- ClickHouse под нагрузкой съедает CPU и диск; Keeper при этом чувствителен к задержкам — ему нужен предсказуемо быстрый
fsync; - тяжёлый запрос, выедающий память ноды, легко уносит с собой и Keeper — вы теряете сразу и данные-узел, и голос в кворуме;
- обновление ClickHouse не должно перезапускать координацию.
Поэтому: отдельные поды, отдельные диски, podAntiAffinity обязателен — два узла Keeper на одной ноде обесценивают кворум.
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: clickhouse-keeper
topologyKey: kubernetes.io/hostnameОбратите внимание на required, а не preferred: для кворума мягкое пожелание — это не гарантия.
Ресурсы и диск
Keeper небольшой, но требовательный к латентности записи, а не к объёму:
- Диск: небольшой (единицы–десятки ГиБ), но быстрый, лучше SSD/NVMe. Он пишет журнал транзакций и снапшоты; медленный сетевой диск с плавающей латентностью — плохой выбор. Отдельный том, не общий с ClickHouse.
- Память: состояние держится в памяти. Для типового кластера — единицы ГиБ. Растёт с числом узлов/таблиц/кусков.
- CPU: скромно, 1–2 ядра обычно достаточно.
requests=limitsдля памяти, чтобы не словить вытеснение.
Отдельно: не экономьте на PDB. PodDisruptionBudget с maxUnavailable: 1 не даст drain’у ноды выбить два узла Keeper разом.
За чем следить
Минимальный набор сигналов, который стоит завести до первого инцидента:
- живость кворума — есть ли лидер, сколько узлов в кластере;
- латентность запросов к Keeper (рост — предвестник проблем);
- число сессий и watch’ей — аномальный рост говорит о проблемах на стороне ClickHouse;
- размер журнала и снапшотов, свободное место на томе;
- со стороны ClickHouse — таблицы
system.replicas(флагis_readonly) иsystem.replication_queue(растущая очередь = реплики не успевают или Keeper тормозит).
Быстрая проверка состояния изнутри пода:
# четырёхбуквенные команды, как у ZooKeeper
echo mntr | nc localhost 9181
echo stat | nc localhost 9181А со стороны базы — тревожный признак, который стоит вынести в алерт:
SELECT database, table, is_readonly, absolute_delay, queue_size
FROM system.replicas
WHERE is_readonly OR queue_size > 100;Грабли, на которые наступают
- Забыли связать кластер с Keeper. В официальном операторе это
keeperClusterRef. Поды здоровы, репликации нет — расхождение обнаружится позже и больно. - Чётное число узлов. Не даёт выигрыша в отказоустойчивости.
- Keeper на тех же нодах, что ClickHouse. Один тяжёлый запрос — и минус узел кворума.
- Медленный диск под журнал. Латентность Keeper превращается в латентность вставок.
- Нет мониторинга. Keeper тихо работает месяцами, и о нём вспоминают в момент отказа.
- Восстановление после потери кворума — процедура нетривиальная (recovery-режим, пересборка). Отрепетируйте её на тестовом кластере до того, как понадобится.