Хочу показать простой и повторяемый способ запустить шардированный, реплицируемый кластер ClickHouse на ноутбуке с помощью Docker Compose — чтобы учиться, экспериментировать и безопасно ломать/чинить без влияния на прод.
Зачем и кому это нужно
Быстро развернуть учебный кластер на локальной машине.
На практике потрогать репликацию (replication), шардирование (sharding), балансировку и проверить отказоустойчивость.
Экспериментировать без риска: всё в контейнерах; если что-то пошло не так — снесли и подняли заново.
Почему Docker Compose? Повторяемо, прозрачно, наглядно, легко снести и поднять заново — для обучения самое то.
Как устроен кластер ClickHouse (в двух словах)
В ClickHouse репликация и согласование происходят на уровне таблиц, а не всего сервера. Таблицы на движках Replicated*MergeTree
используют ClickHouse Keeper или ZooKeeper для координации операций (создание, вставка, слияние, удаление партиций и т. д.). Таблицы без репликации (MergeTree
и др.) этих сервисов не требуют.
Кластеры описываются в конфигурации сервера; одну и ту же ноду можно включать в несколько логических «раскладок» (кластеров) с разным числом шардов и реплик.
В учебном стенде для простоты используем одиночный ZooKeeper. В продакшене для новых инсталляций официально рекомендуют ClickHouse Keeper в составе трёх нод: он быстрее и проще в эксплуатации. ZooKeeper остаётся поддерживаемым и часто используется там, где уже есть опыт и инфраструктура под него.
Репозиторий с примером
Полный проект с Compose-файлом и конфигами:
github.com/dementev-dev/clickhouse-learning-cluster
Состав стенда
1× ZooKeeper (учебный, одиночный).
4× ClickHouse Server (версия 25.1).
1× HAProxy для балансировки 8123/9000.
Рис. 1. Схема кластера
Базовый шаблон сервиса ClickHouse
x-configs: &ch-default-configs
image: clickhouse/clickhouse-server:25.1
environment:
TZ: "Europe/Moscow"
ulimits:
nproc: 65535
nofile:
soft: 262144
hard: 262144
networks:
- ch_learning_net
depends_on:
- zookeeper
Зачем ulimits
:
nofile=262144
— рекомендация ClickHouse для запаса по файловым дескрипторам (много фоновых задач и соединений). В тестовой среде можно меньше, но в продакшене лучше держать рекомендуемое.nproc=65535
— достаточно для учебного стенда; если словите «Maximum number of threads is lower than 30000», поднимайте лимит — в контейнерах это частая причина проблем.
Координатор (ZooKeeper)
Для простоты — одиночный ZooKeeper.
services:
zookeeper:
image: zookeeper:3.9.3
networks: [ch_learning_net]
environment:
- ALLOW_ANONYMOUS_LOGIN=yes
- ZOOKEEPER_CLIENT_PORT=2181
- TZ=Europe/Moscow
ports:
- "2182:2181" # наружу
- "2888:2888"
- "3888:3888"
⚠️ В продакшене анонимный доступ нельзя. Здесь он только ради простоты учебного стенда.
Первая нода ClickHouse
Остальные (click2…click4
) аналогично, меняются только макросы.
click1:
<<: *ch-default-configs
volumes:
- ./configs/default_user.xml:/etc/clickhouse-server/users.d/default_user.xml
- ./configs/z_config.xml:/etc/clickhouse-server/config.d/z_config.xml
- ./configs/macros_ch1.xml:/etc/clickhouse-server/config.d/macros.xml
# сюда кладем все то, что потом хотим загрузить с файловой системы в ClickHouse
- ./data:/var/lib/clickhouse/user_files/data
ports:
- "8002:9000" # native для отладки
- "9123:8123" # http для отладки
Что в этих файлах конфигурации
users.d/default_user.xml
— задаём пароль и базовые права пользователю default
.
<clickhouse>
<users>
<default>
<password>yourStrongPass</password>
<access_management>1</access_management>
</default>
</users>
</clickhouse>
Пользователь
default
— фактически суперпользователь. В реальных средах лучше создать отдельного пользователя и роль-модель, аdefault
ограничить/отключить.
config.d/z_config.xml
— где искать ZooKeeper + описание кластеров.
<clickhouse>
<zookeeper>
<node>
<host>zookeeper</host>
<port>2181</port>
</node>
</zookeeper>
<remote_servers>
<c2sh2rep>
<shard>
<replica><host>click1</host><port>9000</port></replica>
<replica><host>click2</host><port>9000</port></replica>
</shard>
<shard>
<replica><host>click3</host><port>9000</port></replica>
<replica><host>click4</host><port>9000</port></replica>
</shard>
</c2sh2rep>
</remote_servers>
</clickhouse>
config.d/macros.xml
— уникальные макросы на каждую ноду (для путей в Keeper/ZooKeeper и имён реплик).
<clickhouse>
<macros>
<cluster>c2sh2rep</cluster>
<shard_c2sh2rep>01</shard_c2sh2rep>
<replica_c2sh2rep>01</replica_c2sh2rep> <!-- на click2 будет 02 и т. д. -->
</macros>
</clickhouse>
Почему важна уникальность? Первый аргумент ReplicatedMergeTree
— путь в Keeper/ZooKeeper. Он должен быть уникален для каждой таблицы в пределах шарда. Удобно собирать путь из макросов {shard}
, {database}
, {table}
— так не перепутаете реплики при рестартах/переименованиях. Имя реплики уникально внутри шарда.
Балансировщик HAProxy
Простая отказоустойчивость и распределение подключений. Здесь проверки только TCP-доступности (8123 — HTTP, 9000 — native), чего достаточно для учебного стенда.
haproxy.cfg
:
global
log stdout format raw local0
defaults
log global
mode tcp
timeout connect 5s
timeout client 1m
timeout server 1m
frontend ch_native
bind *:9000
default_backend ch_native_pool
backend ch_native_pool
balance roundrobin
server ch1 click1:9000 check
server ch2 click2:9000 check
server ch3 click3:9000 check
server ch4 click4:9000 check
frontend ch_http
bind *:8123
default_backend ch_http_pool
backend ch_http_pool
balance roundrobin
server ch1 click1:8123 check
server ch2 click2:8123 check
server ch3 click3:8123 check
server ch4 click4:8123 check
Сервис в Compose:
haproxy:
image: haproxy:2.9
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
ports: ["9000:9000", "8123:8123"]
depends_on: [click1, click2, click3, click4]
networks: [ch_learning_net]
Запуск и быстрая проверка
docker compose up -d
Проверка соединения:
-- через любой узел (или через HAProxy на 8123/9000)
SELECT version();
SHOW CLUSTERS;
SELECT cluster,
groupArray(concat(host_name,':',toString(port))) AS hosts
FROM system.clusters
GROUP BY cluster
ORDER BY cluster;
Реплицируемая таблица + шардирование через Distributed
-- очищаем предыдущую версию, если была
DROP TABLE IF EXISTS user_scores_rep ON CLUSTER c2sh2rep SYNC;
DROP TABLE IF EXISTS user_scores ON CLUSTER c2sh2rep SYNC;
CREATE TABLE user_scores_rep ON CLUSTER c2sh2rep (
user_id UInt32,
avg_score Float32,
created_at DateTime
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/shard_{shard_c2sh2rep}/{database}/{table}',
'{replica_c2sh2rep}'
)
ORDER BY (user_id);
CREATE TABLE user_scores ON CLUSTER c2sh2rep
AS user_scores_rep
ENGINE = Distributed(c2sh2rep, default, user_scores_rep, user_id);
?Используем 'SYNC' для удаления таблицы без задержки. Без модификора SYNC удаление роисходит асинхронно — таблица помечается как удалённая, а физическое удаление данных и метаданных происходит позже, в фоне. Использование SYNC важно, если нужно быть уверенным, что таблица действительно удалена к моменту завершения запроса — например, перед созданием новой таблицы с тем же именем или для последовательных операций с метаданными.
Заливаем данные:
INSERT INTO user_scores
SELECT
number % 100000 + 1 AS user_id,
toFloat32(rand() % 50 + rand() % 50) / 10 AS avg_score,
now() - (number * 86400 / 1000) AS created_at
FROM numbers(100000);
SELECT count() FROM user_scores; -- sanity-check
Как понять, куда легли данные
SELECT shardNum() AS shard_id,
count() AS rows_per_shard
FROM user_scores
GROUP BY shard_id
ORDER BY shard_id;
shardNum()
работает при обращении к Distributed
-таблице или при ON CLUSTER
. Если выполнить на локальной таблице — будет ошибка (функция не определена в локальном контексте).
Проверка репликации:
SELECT table, is_leader, total_replicas, active_replicas, replica_delay
FROM system.replicas
WHERE database = 'default' AND table = 'user_scores_rep';
system.replicas
показывает состояние локальных реплицируемых таблиц текущей ноды. В нашем примере пары (click1
/click2
) и (click3
/click4
) — это два разных шарда, так что смотрите по одной ноде из каждой пары.
Частые вопросы
Keeper или ZooKeeper?
Для новых прод-кластеров рекомендуют ClickHouse Keeper (встроен, проще, быстрее). ZooKeeper остаётся поддерживаемым и уместен там, где он уже развёрнут.
Сколько нод координации в проде?
Обычно минимум 3 (и для Keeper, и для ZooKeeper). В учебке — одна нода для простоты.
Обязательно ли ulimit nofile=262144
?
Это рекомендация ClickHouse. На малых стендах может работать и меньше, но лучше сразу «как в доке».
А nproc
?
Если встретили «Maximum number of threads is lower than 30000», поднимайте лимит nproc
.
Что почитать дальше
Репликация в ClickHouse: уникальные пути, макросы и пр.
Масштабирование и шаблоны раскладок (1×2, 2×1, 2×2)
Балансировка и прокси (HAProxy на TCP-уровне)
Что дальше
В следующих статьях будем учиться работать с уже развернутым кластером на практике.