本文基于开源项目 clickhouse_1s3r,完整拆解 ClickHouse 25.8 LTS 在「1分片3副本 + 内置 Keeper」架构下的自动化部署与高可用测试方案。从架构设计、Ansible 编排、核心配置到故障切换原理,一篇讲透。


一、为什么需要 1分片3副本?

在实际的生产场景中,ClickHouse 集群面临的核心挑战是 数据可靠性 和 服务连续性。单节点部署一旦宕机,轻则服务中断,重则数据丢失。1分片3副本(1 Shard × 3 Replicas)架构正是为此而生:

特性单节点1分片3副本
数据冗余❌ 无✅ 三副本
容错能力❌ 0✅ 容忍1节点故障
读写可用性单点多副本负载分担
元数据管理N/ARaft 共识(3节点多数派)

核心架构设计

                    ┌──────────────────────────────────────────┐
                    │          Distributed 表 (业务入口)         │
                    │     ENGINE = Distributed(...)              │
                    └──────────────────┬───────────────────────┘
                                       │
                    ┌──────────────────┼───────────────────────┐
                    │                  │                       │
              ┌─────▼─────┐     ┌─────▼─────┐         ┌─────▼─────┐
              │  Replica1  │     │  Replica2  │         │  Replica3  │
              │  (node1)   │     │  (node2)   │         │  (node3)   │
              │ Replicated │────▶│ Replicated │────────▶│ Replicated │
              │ MergeTree  │     │ MergeTree  │         │ MergeTree  │
              └─────┬─────┘     └─────┬─────┘         └─────┬─────┘
                    │                  │                       │
              ┌─────▼─────┐     ┌─────▼─────┐         ┌─────▼─────┐
              │  Keeper 1  │     │  Keeper 2  │         │  Keeper 3  │
              │ server_id=1│     │ server_id=2│         │ server_id=3│
              └───────────┘     └───────────┘         └───────────┘
                    │                  │                       │
                    └────────── Raft 共识协议 ─────────────────┘

关键设计决策:

  1. 内置 Keeper 替代 ZooKeeper:ClickHouse 25.8 LTS 内置 Keeper 组件,无需额外维护 ZK 集群,大幅降低运维复杂度
  2. ReplicatedMergeTree + Distributed:本地表用 ReplicatedMergeTree 实现副本同步,分布式表作为业务统一入口
  3. internal_replication = true:写入只命中一个副本,由副本间自动同步,减少写入放大
  4. Raft 3节点多数派:容忍最多1个节点故障,保证元数据一致性

二、集群拓扑与端口规划

2.1 节点规划

节点IP主机名Keeper ID角色
node1192.168.10.201xt-wxqb-ck-11shard1-replica1 + Keeper
node2192.168.10.202xt-wxqb-ck-22shard1-replica2 + Keeper
node3192.168.10.203xt-wxqb-ck-33shard1-replica3 + Keeper

2.2 端口规划

端口协议用途
9000TCPClickHouse 客户端连接
8123HTTPHTTP API / Playground
9009TCP集群节点间通信
9181TCPKeeper 客户端
9234TCPKeeper 内部同步
9235TCPKeeper Raft
9363HTTPPrometheus 指标
注意:Keeper 的  server_id 三节点必须唯一(1/2/3),这是 Raft 选举的基础。配置错误会导致集群无法形成仲裁。

2.3 目录规范

/data/common/                    # 程序安装根目录
/data/common/clickhouse          # CK 程序目录(软链接→版本目录)
/data/common/clickhouse/conf     # 配置文件目录
/data/common/clickhouse/logs     # 日志目录
/data/clickhouse/                # CK 数据目录
/data/clickhouse/keeper/         # Keeper 数据目录
/data/clickhouse/tmp/            # 临时目录

三、Ansible 自动化部署:项目结构全貌

整个项目采用标准 Ansible Roles 组织,结构清晰:

clickhouse_1s3r/
├── ansible.cfg                  # Ansible 全局配置
├── inventory.ini                # 主机清单(节点分组、SSH)
├── group_vars/
│   └── all.yml                  # 全局变量(版本/端口/路径/密码/Keeper拓扑)
├── playbooks/
│   ├── site.yml                 # 一键部署入口
│   ├── deploy_cluster.yml       # 主部署 Playbook(6个Play)
│   ├── ha_test.yml              # 高可用测试 Playbook
│   └── cleanup_cluster.yml      # 集群清理
├── roles/
│   ├── ck_common/tasks/         # 环境初始化
│   ├── ck_install/tasks/        # 软件安装
│   │   └── templates/           # config.xml / users.xml / systemd
│   └── ck_cluster/tasks/        # 集群启动(并行点火)
├── scripts/
│   ├── config.env               # Shell 脚本变量
│   ├── ha_test.sh               # HA 测试脚本
│   └── test_data_gen.sql        # 测试数据 SQL
├── files/                       # 离线安装包
└── docs/                        # 部署手册 + HA 测试手册

设计亮点

  1. Roles 分层ck_common(环境)→ ck_install(安装)→ ck_cluster(启动),职责清晰
  2. Tags 分阶段:支持 --tags=common/install/start/init 按需执行
  3. 模板化配置config.xml.j2users.xml.j2clickhouse.service.j2 全部通过 Jinja2 渲染
  4. 离线部署:安装包预置于 files/,目标节点无需联网

四、核心配置解读

4.1 inventory.ini — 主机清单

[ck_node1]
192.168.10.201  hostname=xt-wxqb-ck-1  keeper_server_id=1

[ck_node2]
192.168.10.202  hostname=xt-wxqb-ck-2  keeper_server_id=2

[ck_node3]
192.168.10.203  hostname=xt-wxqb-ck-3  keeper_server_id=3

[ck_cluster:children]
ck_node1
ck_node2
ck_node3

[all:vars]
ansible_user=root
ansible_password=xxxx
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
ansible_become=true
ansible_become_method=sudo

每个节点通过主机变量指定 hostname 和 keeper_server_id,Ansible 在渲染配置模板时会自动注入这些值。ck_cluster:children 将三个节点聚合为一个组,便于并行执行。

4.2 group_vars/all.yml — 全局变量

这是整个项目的"控制面板",所有可配置参数集中管理:

# ==================== 集群拓扑 ====================
ck_nodes:
  - host: "192.168.10.201"
    hostname: "xt-wxqb-ck-1"
    shard: "01"
    replica: "replica1"
  - host: "192.168.10.202"
    hostname: "xt-wxqb-ck-2"
    shard: "01"
    replica: "replica2"
  - host: "192.168.10.203"
    hostname: "xt-wxqb-ck-3"
    shard: "01"
    replica: "replica3"

ck_cluster_name: "ck_1s3r_cluster"

# ==================== Keeper 集群参数 ====================
keeper_nodes:
  - server_id: 1
    host: "xt-wxqb-ck-1"
    ip: "192.168.10.201"
  - server_id: 2
    host: "xt-wxqb-ck-2"
    ip: "192.168.10.202"
  - server_id: 3
    host: "xt-wxqb-ck-3"
    ip: "192.168.10.203"

# ==================== 性能与存储参数 ====================
ck_max_memory_usage: "10G"
ck_max_server_memory: "16G"
ck_compression_method: "zstd"
ck_compression_level: 3
复用提示:修改  ck_nodes 和  keeper_nodes 的 IP/主机名,即可适配不同环境。端口、密码、路径等参数也都在此文件统一修改。

4.3 部署 Playbook — 6 个 Play 的编排逻辑

deploy_cluster.yml 是部署核心,按以下顺序执行:

# Play 1: 环境初始化 — 3节点并行
- name: 1. 环境初始化 - 所有 ClickHouse 节点
  hosts: ck_cluster
  roles:
    - role: ck_common
  tags: [common, prepare]

# Play 2: 安装 ClickHouse — 3节点并行
- name: 2. 安装 ClickHouse - 所有节点 (并行)
  hosts: ck_cluster
  roles:
    - role: ck_install
  tags: [install]

# Play 3: 启动集群 — 3节点并行点火
- name: 3. 启动 ClickHouse - 3节点并行点火
  hosts: ck_cluster
  roles:
    - role: ck_cluster
  tags: [start]

# Play 4: 等待 Keeper 仲裁 + 初始化 DDL
- name: 4. 等待 Keeper 仲裁形成并初始化 DDL
  hosts: ck_node1
  tags: [init]
  tasks:
    - name: 等待 Keeper 仲裁形成 (最多等待 5 分钟)
      command: >
        {{ ck_install_dir }}/bin/clickhouse-client
        -u {{ ck_admin_user }} --password '{{ ck_default_password }}'
        --query "SELECT 1"
      register: keeper_check
      until: keeper_check.rc == 0
      retries: 60
      delay: 5

    - name: 创建 ReplicatedMergeTree 本地表
      command: >
        {{ ck_install_dir }}/bin/clickhouse-client
        --query "
        CREATE TABLE IF NOT EXISTS test.data_local ON CLUSTER {{ ck_cluster_name }}
        (id UInt64, dt DateTime, msg String)
        ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/test/data_local', '{replica}')
        PARTITION BY toYYYYMM(dt)
        ORDER BY id;
        "

    - name: 创建 Distributed 分布式表
      command: >
        {{ ck_install_dir }}/bin/clickhouse-client
        --query "
        CREATE TABLE IF NOT EXISTS test.data_dist ON CLUSTER {{ ck_cluster_name }}
        (id UInt64, dt DateTime, msg String)
        ENGINE = Distributed('{{ ck_cluster_name }}', 'test', 'data_local', rand());
        "

编排逻辑解析:

Play主机范围执行方式核心动作
1ck_cluster(3台)并行hosts解析、关防火墙/SELinux、创建用户/目录、内核参数优化、禁用THP
2ck_cluster(3台)并行上传解压安装包、创建软链接、渲染config.xml/users.xml、部署systemd
3ck_cluster(3台)并行systemctl start --no-block 三节点同时点火
4ck_node1(单台)串行等待Keeper仲裁→建库建表→插入验证数据
5ck_node1(单台)串行输出集群节点/Keeper/副本状态
5.1ck_cluster(3台)并行各节点查询本地表,验证数据一致性
6ck_node1(单台)串行输出部署完成Banner(连接信息)
为什么三节点并行启动? Keeper 基于 Raft 协议,需要多数派(2/3)节点在线才能形成仲裁。如果逐台启动,先启动的节点会一直等待其他节点,导致超时。并行启动让 Keeper 尽快达成 quorum。

五、一键部署实战

5.1 前置准备

# 1. 克隆项目
git clone https://github.com/eagle-qi/clickhouse_1s3r.git
cd clickhouse_1s3r

# 2. 放置离线安装包(约178MB)
cp /path/to/clickhouse-25.8.28.1-lts-x86_64.tgz files/

# 3. 修改配置
vim inventory.ini        # SSH 连接信息
vim group_vars/all.yml   # 集群参数(密码等)

5.2 测试连通性

ansible -i inventory.ini ck_cluster -m ping

预期输出:

192.168.10.201 | SUCCESS => { "changed": false, "ping": "pong" }
192.168.10.202 | SUCCESS => { "changed": false, "ping": "pong" }
192.168.10.203 | SUCCESS => { "changed": false, "ping": "pong" }

5.3 一键部署

# 完整部署
ansible-playbook -i inventory.ini playbooks/site.yml

# 或分阶段执行
ansible-playbook -i inventory.ini playbooks/deploy_cluster.yml --tags=common    # 仅环境初始化
ansible-playbook -i inventory.ini playbooks/deploy_cluster.yml --tags=install   # 仅安装软件
ansible-playbook -i inventory.ini playbooks/deploy_cluster.yml --tags=start     # 仅启动集群
ansible-playbook -i inventory.ini playbooks/deploy_cluster.yml --tags=init      # 仅初始化DDL

5.4 部署后验证

-- 查看集群节点
SELECT cluster, host_name, host_address, shard_num, replica_num, is_active
FROM system.clusters
WHERE cluster = 'ck_1s3r_cluster'
FORMAT PrettyCompact;

-- 查看副本状态
SELECT database, table, replica_name, is_readonly, total_replicas, active_replicas
FROM system.replicas
FORMAT PrettyCompact;

预期输出:3个节点全部 is_active=1total_replicas=3active_replicas=3

5.5 Keeper 状态检查

# 使用内置 keeper-client 查询(25.8 LTS 推荐方式)
echo mntr | /data/common/clickhouse/bin/clickhouse-keeper-client -h 127.0.0.1 -p 9181

关键指标:

  • zk_server_state — 应为 leader 或 follower,有且仅有1个 leader
  • zk_num_alive_connections — 活跃连接数
  • zk_approximate_data_size — 元数据大小

六、生产建表最佳实践

6.1 ReplicatedMergeTree 本地表

-- 创建数据库(所有节点)
CREATE DATABASE IF NOT EXISTS your_db ON CLUSTER ck_1s3r_cluster;

-- 创建本地副本表(所有节点)
CREATE TABLE IF NOT EXISTS your_db.your_table_local ON CLUSTER ck_1s3r_cluster
(
    id UInt64,
    event_time DateTime,
    data String
)
ENGINE = ReplicatedMergeTree(
    '/clickhouse/tables/{shard}/your_db/your_table_local',
    '{replica}'
)
PARTITION BY toYYYYMM(event_time)
ORDER BY (id, event_time)
SETTINGS
    compression_method = 'zstd',
    compression_level = 3;

6.2 Distributed 分布式表

-- 创建分布式表(业务统一入口)
CREATE TABLE IF NOT EXISTS your_db.your_table_dist ON CLUSTER ck_1s3r_cluster
(
    id UInt64,
    event_time DateTime,
    data String
)
ENGINE = Distributed('ck_1s3r_cluster', 'your_db', 'your_table_local', rand());

6.3 建表规范要点

规范说明
所有 DDL 加 ON CLUSTER保证三节点表结构一致
业务读写统一操作 Distributed 表规避副本读写不一致
ReplicatedMergeTree 路径用宏{shard} 和 {replica} 由 config.xml 宏定义自动替换
启用 ZSTD 压缩compression_method='zstd', compression_level=3,压缩比高、CPU 开销低
磁盘清理用分区剥离ALTER TABLE ... DROP PARTITION,避免大量 DELETE 产生碎片

七、高可用测试:5大场景全流程

项目内置了完整的高可用测试方案,支持 Ansible Playbook 和 Shell 脚本 两种方式。

7.1 测试场景一览

测试场景验证点
0准备测试数据自动建表 uk.uk_price_paid_local + 插入10行 UK 房价数据
1数据写入与三副本同步单节点写入 → 三节点均可见
2单节点宕机剩余2节点读写正常,数据不丢失
3宕机节点恢复自动从 Keeper 追赶数据,三节点一致
4双节点同时宕机读可用/写拒绝(Keeper失仲裁),恢复后数据零丢失
5最终一致性验证全集群数据量、min/max/avg 一致

7.2 执行 HA 测试

# 方式一:Ansible 自动化测试
ansible-playbook -i inventory.ini playbooks/ha_test.yml

# 方式二:Shell 脚本测试(无需 Ansible 环境)
bash scripts/ha_test.sh

7.3 故障切换原理

理解 HA 测试的底层逻辑,核心在于 ClickHouse Keeper 的 Raft 共识 和 ReplicatedMergeTree 的副本同步

节点宕机
  ↓
Keeper 感知失联(session_timeout: 30s)
  ↓
Raft 多数派继续工作(2/3存活)
  ↓
读写请求路由到健康节点
  ↓
宕机节点恢复启动
  ↓
Keeper 重新建立会话
  ↓
副本从 Keeper 拉取缺失的 log entries
  ↓
追赶上最新状态,标记为 active

关键细节:

  • 3节点集群:容忍最多1个节点故障(需 2/3 多数派)
  • 双节点宕机时:Keeper 失去仲裁,写入被拒绝,但读在存活节点上仍可用
  • Leader 故障:剩余 Follower 自动发起选举,选出新 Leader
  • 节点恢复后:自动从 Keeper 拉取缺失的操作日志完成数据追赶

7.4 测试报告

测试完成后自动输出汇总报告:

╔══════════════════════════════════════════════════════════════════╗
║       ClickHouse 1分片3副本 高可用测试报告                        ║
╠══════════════════════════════════════════════════════════════════╣
║  测试1: 数据写入与三副本同步验证              ✓ PASS             ║
║  测试2: 节点3单独宕机 + 剩余节点读写          ✓ PASS             ║
║  测试3: 节点3恢复 + 数据自动追同步            ✓ PASS             ║
║  测试4: 节点1+2同时宕机(双节点)              ✓ PASS             ║
║         - 节点3独立读验证                     ✓                  ║
║         - 写入预期失败(Keeper失仲裁)          ✓                  ║
║         - 双节点恢复 + 全量同步               ✓                  ║
║  测试5: 全集群最终一致性验证                  ✓ PASS             ║
╠══════════════════════════════════════════════════════════════════╣
║  结论: 1S3R集群在单节点故障时完全可用, 读写不中断                ║
║        双节点宕机时Keeper失去仲裁导致写失败, 读仍可用            ║
║        所有节点恢复后数据完整同步零丢失                          ║
╚══════════════════════════════════════════════════════════════════╝

Shell 脚本方式还会在 /tmp/clickhouse_ha_test/ 目录下生成 Markdown 格式的详细报告,包含各节点 Keeper mntr 完整输出和数据统计。


八、常见问题排查

Q1: Keeper 集群无法选举 Leader

# 查看 Keeper 日志
tail -200 /data/common/clickhouse/logs/clickhouse-server.log | grep -i keeper

# 检查节点间网络连通性
telnet xt-wxqb-ck-1 9234
telnet xt-wxqb-ck-2 9234
telnet xt-wxqb-ck-3 9234

解决:确保 server_id 三台互不相同(1/2/3),确保 /etc/hosts 解析正确,三节点必须接近同时启动。

Q2: 数据不同步

-- 检查副本状态
SELECT database, table, replica_name, is_session_expired, active_replicas
FROM system.replicas;

-- 检查集群配置
SELECT * FROM system.clusters WHERE cluster = 'ck_1s3r_cluster';

解决:确认 internal_replication=true,确认 Keeper 集群健康。

Q3: 启动报权限错误

# 确保所有目录归属正确
chown -R zxws:zxws /data/common /data/clickhouse
chmod 755 /data/common /data/clickhouse

Q4: 双节点宕机后写入失败

这是 预期行为,不是 Bug。Keeper 基于 Raft 协议,2/3 节点宕机时无法形成多数派仲裁,写入会被拒绝。读操作在存活节点上仍可用。恢复节点后 Keeper 重新形成仲裁,写入自动恢复。


九、生产环境注意事项

序号注意事项
1全程使用 zxws 用户,禁止 root 运行 ClickHouse 进程
2修改 default 用户密码 为强密码(默认密码仅供测试)
3所有 DDL 加 ON CLUSTER,保证三节点表结构一致
4业务读写统一操作 Distributed 表,规避副本不一致
5Keeper server_id 必须唯一(1/2/3),否则 Raft 选举异常
6三节点尽量同时启动,Keeper 需要多数派形成 quorum
7磁盘清理优先用分区剥离,避免大量 DELETE 产生碎片
8Keeper 数据独立存放,禁止与业务数据混用
9至少保持2个节点在线,运维操作时避免同时停多台
10定期执行 HA 测试,验证集群高可用能力

十、总结

本文完整拆解了基于 ClickHouse 25.8 LTS 的 1分片3副本高可用集群方案,核心要点回顾:

  1. 架构选择:1分片3副本 + 内置 Keeper,无需 ZooKeeper,运维更轻量
  2. 自动化部署:Ansible Roles 分层编排,6个 Play 覆盖从环境初始化到验证全流程
  3. 配置管理group_vars/all.yml 统一管理所有参数,修改即可适配不同环境
  4. 高可用验证:5大测试场景覆盖单节点/双节点故障,自动化执行 + 报告生成
  5. 生产规范:DDL 加 ON CLUSTER、业务走 Distributed 表、ZSTD 压缩、分区剥离清理

项目地址https://github.com/eagle-qi/clickhouse_1s3r

如果你正在规划 ClickHouse 生产集群部署,或者想验证高可用能力,这个项目可以帮你省去大量重复工作。一键部署,一键测试,开箱即用。


版权声明:本文为原创技术博客,参考开源项目  clickhouse_1s3r 编写。转载请注明出处。

参考 clickhouse 集群部署 - 小吉猫 - 博客园
Replication + scaling | ClickHouse Docs
ClickHouse 3节点集群安装_clickhouse三节点部署-CSDN博客

更多推荐