Хочу показать простой и повторяемый способ запустить шардированный, реплицируемый кластер 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.
Что почитать дальше
Репликация (ReplicatedMergeTree): уникальные пути, макросы, типовые ошибки — официальная документация ClickHouse.
Масштабирование и развёртывание кластеров (1×2, 2×1, 2×2) на ClickHouse Keeper — репликация + масштабирование.
Балансировка и прокси для ClickHouse: HAProxy/NGINX на TCP-уровне и ограничения нативного протокола — Altinity® Knowledge Base.
Мониторинг репликации: что смотреть в
system.replicas— ClickHouse Docs.
Материалы к статье: репозиторий с кодом.
Что дальше
В следующих статьях будем учиться работать с уже развернутым кластером на практике.
— Дмитрий Дементьев, Эксперт, «Концепт Разработка».
Контакты: GitHub • Канал Telegram • почта
