小公司ClickHouse集群版完整部署指南(实战版)

一步一个脚印一个坑 1小时前 ⋅ 7 阅读
ad

据我多年做全链路分析系统的经验来看,大部分公司数据体量,用单机版基本足够了。像Clickhouse这种OLAP 实时分析型数据库,在大量数据插入和查询方面都特别猛,一次插入几万条数据跟玩似的。但是更新和删除的时候都比较拉胯了,不过正常情况下,也没人专门更新或者删除一条日志啊,所以中小公司用单机版加个备份脚本就可以了。但是集群有集群的好处,它容错率高啊,在高可用场景下很有必要,今天我们就来介绍一个低成本的clickhouse集群。

我们也做了一套全链路监控产品Webfunny,我们就是采用的这种集群方式,既能满足并发场景,成本也比较低,有兴趣的小伙伴可以了解一下。

一、架构设计

推荐架构(1 分片 2 副本 + 独立 ZooKeeper)

┌─────────────────────────────────────────────┐
│              应用服务层                       │
│  Node.js / 业务服务                          │
└──────────────┬──────────────────────────────┘
               │ HTTP 8123 / TCP 9000
┌──────────────┴──────────────────────────────┐
│           ClickHouse 集群                    │
│                                             │
│  ch-node1 (8c16g+SSD)   ch-node2 (8c16g+SSD)│
│  ClickHouse 主库    ←→   ClickHouse 从库     │
│  写入优先               读取优先              │
└──────────────┬──────────────────────────────┘
               │ 2181/2182/2183
┌──────────────┴──────────────────────────────┐
│        ZooKeeper 集群(独立服务器)            │
│                                             │
│  zk-server (2c4g+SSD)                       │
│  实例1: 2181   实例2: 2182   实例3: 2183     │
└─────────────────────────────────────────────┘

为什么 ZooKeeper 要独立部署?

  • ZooKeeper 与 ClickHouse 共存会导致资源竞争,CPU 相互影响
  • ZooKeeper 对磁盘 IO 延迟极敏感,ClickHouse 的高 IO 会影响 ZooKeeper 响应
  • ZooKeeper JVM 进程本身占用约 500MB~2GB 内存,独立部署更可控

硬件要求

角色 规格 磁盘
ClickHouse 主库 8c16g 起步 ESSD PL1 500GB+
ClickHouse 从库 8c16g 起步 ESSD PL1 500GB+
ZooKeeper 专用 4c8g ESSD Entry 40GB(系统盘即可)

磁盘选型说明:ClickHouse 的写入性能对磁盘 IOPS 要求较高,推荐 ESSD PL1(5万 IOPS)。ZooKeeper 只需低延迟顺序写,ESSD Entry(1万 IOPS,0.3ms 延迟)完全够用。


二、ZooKeeper 集群部署(单机 3 实例)

这里采用的一台机器启动3个zookeeper实例,虽然成本比较低,但是如果这个机器挂了,集群就歇菜了,所以这个集群也叫伪集群模式,哈哈。这个模式主要是为了降低成本,又可以增加clickhouse的性能,如果预算充足,就启动三个机器部署zookeeper最稳妥

1. 安装 Java 11

# CentOS/RHEL
yum install -y java-11-openjdk java-11-openjdk-devel

# Ubuntu/Debian
apt-get install -y openjdk-11-jdk

# 验证
java -version

2. 下载并安装 ZooKeeper 3.8.4

cd /opt

# 使用国内镜像下载(速度更快)
wget https://mirrors.aliyun.com/apache/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz

# 解压
tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz

# 创建软链接
ln -s /opt/apache-zookeeper-3.8.4-bin /opt/zookeeper

# 验证
ls /opt/zookeeper/bin/

⚠️ 注意:软链接要在创建配置目录之前完成,否则后续创建的 conf 目录会被软链接覆盖。

3. 创建目录结构

# 数据目录(独立于安装目录,防止被升级覆盖)
mkdir -p /opt/zkdata/node1/{data,log}
mkdir -p /opt/zkdata/node2/{data,log}
mkdir -p /opt/zkdata/node3/{data,log}

# 配置目录(独立于安装目录)
mkdir -p /opt/zkconf/conf1
mkdir -p /opt/zkconf/conf2
mkdir -p /opt/zkconf/conf3

# 统一日志目录
mkdir -p /opt/zkdata/logs

4. 创建配置文件

以 ZooKeeper 服务器 IP 为 172.19.117.126 为例:

实例1(客户端端口 2181):

cat > /opt/zkconf/conf1/zoo.cfg << 'EOF'
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zkdata/node1/data
dataLogDir=/opt/zkdata/node1/log
clientPort=2181
adminPort=8081
maxClientCnxns=1000
maxSessionTimeout=60000
autopurge.purgeInterval=1
autopurge.snapRetainCount=5
4lw.commands.whitelist=*
server.1=172.19.117.126:2888:3888
server.2=172.19.117.126:2889:3889
server.3=172.19.117.126:2890:3890
EOF

实例2(客户端端口 2182):

cat > /opt/zkconf/conf2/zoo.cfg << 'EOF'
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zkdata/node2/data
dataLogDir=/opt/zkdata/node2/log
clientPort=2182
adminPort=8082
maxClientCnxns=1000
maxSessionTimeout=60000
autopurge.purgeInterval=1
autopurge.snapRetainCount=5
4lw.commands.whitelist=*
server.1=172.19.117.126:2888:3888
server.2=172.19.117.126:2889:3889
server.3=172.19.117.126:2890:3890
EOF

实例3(客户端端口 2183):

cat > /opt/zkconf/conf3/zoo.cfg << 'EOF'
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zkdata/node3/data
dataLogDir=/opt/zkdata/node3/log
clientPort=2183
adminPort=8083
maxClientCnxns=1000
maxSessionTimeout=60000
autopurge.purgeInterval=1
autopurge.snapRetainCount=5
4lw.commands.whitelist=*
server.1=172.19.117.126:2888:3888
server.2=172.19.117.126:2889:3889
server.3=172.19.117.126:2890:3890
EOF

5. 创建 myid 文件

echo "1" > /opt/zkdata/node1/data/myid
echo "2" > /opt/zkdata/node2/data/myid
echo "3" > /opt/zkdata/node3/data/myid

# 验证
cat /opt/zkdata/node1/data/myid  # 应输出 1
cat /opt/zkdata/node2/data/myid  # 应输出 2
cat /opt/zkdata/node3/data/myid  # 应输出 3

6. 配置日志轮转(防止日志无限增长)

ZooKeeper 3.8.x 使用 logback 管理日志,默认不限制日志文件大小,在高负载场景下可能导致磁盘被撑满。

cat > /opt/zookeeper/conf/logback.xml << 'EOF'
<?xml version="1.0"?>
<configuration>
  <property name="zookeeper.log.dir" value="/opt/zkdata/logs"/>
  <property name="zookeeper.log.file" value="zookeeper.log"/>

  <appender name="ROLLINGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${zookeeper.log.dir}/${zookeeper.log.file}</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>${zookeeper.log.dir}/zookeeper.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      <maxFileSize>100MB</maxFileSize>
      <maxHistory>5</maxHistory>
      <totalSizeCap>500MB</totalSizeCap>
    </rollingPolicy>
    <encoder>
      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="ROLLINGFILE"/>
  </root>
</configuration>
EOF

7. 创建 systemd 服务(3 个实例)

for i in 1 2 3; do
cat > /etc/systemd/system/zookeeper${i}.service << EOF
[Unit]
Description=ZooKeeper Instance ${i}
After=network.target

[Service]
Type=forking
Environment="ZOOCFGDIR=/opt/zkconf/conf${i}"
Environment="ZOO_LOG_DIR=/opt/zkdata/logs"
Environment="ZOO_LOG4J_PROP=INFO,ROLLINGFILE"
Environment="JVMFLAGS=-Xms512m -Xmx1g"
ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zkconf/conf${i}/zoo.cfg
ExecStop=/opt/zookeeper/bin/zkServer.sh stop /opt/zkconf/conf${i}/zoo.cfg
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF
done

systemctl daemon-reload

8. 启动并验证集群

# 启动
systemctl start zookeeper1 zookeeper2 zookeeper3

# 设置开机自启
systemctl enable zookeeper1 zookeeper2 zookeeper3

# 验证集群选主状态
/opt/zookeeper/bin/zkServer.sh status /opt/zkconf/conf1/zoo.cfg
/opt/zookeeper/bin/zkServer.sh status /opt/zkconf/conf2/zoo.cfg
/opt/zookeeper/bin/zkServer.sh status /opt/zkconf/conf3/zoo.cfg

正常输出示例:

Mode: follower
Mode: leader
Mode: follower

三、ClickHouse 集群配置

1. 配置 /etc/hosts(所有 ClickHouse 节点都要执行)

cat >> /etc/hosts << EOF
172.19.117.121  ck1
172.19.117.120  ck2
172.19.117.126  zk-server
EOF

# 锁定文件,防止云服务器重启后被 cloud-init 覆盖
chattr +i /etc/hosts

# 验证锁定
lsattr /etc/hosts
# 输出应包含 ----i--------

2. 配置 ZooKeeper 连接地址

编辑 /etc/clickhouse-server/config.xml,找到 <zookeeper> 段修改为:

<zookeeper>
    <node>
        <host>172.19.117.126</host>
        <port>2181</port>
    </node>
    <node>
        <host>172.19.117.126</host>
        <port>2182</port>
    </node>
    <node>
        <host>172.19.117.126</host>
        <port>2183</port>
    </node>
</zookeeper>

⚠️ 必须使用 IP 地址,不要使用主机名,否则 DNS 解析失败会导致 ClickHouse 无法连接 ZooKeeper。

3. 配置集群拓扑

<remote_servers>
    <webfunny_cluster>
        <shard>
            <replica>
                <host>172.19.117.121</host>
                <port>9000</port>
                <user>your_user</user>
                <password>your_password</password>
            </replica>
            <replica>
                <host>172.19.117.120</host>
                <port>9000</port>
                <user>your_user</user>
                <password>your_password</password>
            </replica>
        </shard>
    </webfunny_cluster>
</remote_servers>

<!-- 宏变量,用于 ReplicatedMergeTree 表路径 -->
<macros>
    <shard>01</shard>
    <!-- 每台机器不同:replica1 / replica2 -->
    <replica>replica1</replica>
</macros>

4. 建表语句(ReplicatedMergeTree)

-- 在集群所有节点上执行
CREATE TABLE IF NOT EXISTS MonitorData ON CLUSTER webfunny_cluster
(
    happenTime  DateTime,
    projectId   String,
    userId      String,
    -- 其他字段...
)
ENGINE = ReplicatedMergeTree(
    '/clickhouse/tables/{database}/{shard}/{table}',
    '{replica}'
)
PARTITION BY toYYYYMM(happenTime)
ORDER BY (projectId, happenTime)
TTL toDate(happenTime) + INTERVAL 90 DAY;

5. 重启 ClickHouse 并验证

sudo systemctl restart clickhouse-server
sudo systemctl status clickhouse-server

# 验证 ZooKeeper 连接正常(日志里不再出现 Cannot resolve host)
sudo tail -f /var/log/clickhouse-server/clickhouse-server.log | grep -i zookeeper

四、容量规划参考

按照这个配置,每天存5000万 ~ 2亿的日志量,应该是没有问题的,快来试试吧。如有不妥的地方,也欢迎指正。

日志量 写入速度(峰值) 推荐架构 单节点配置
< 5000万/天 < 600行/秒 1分片2副本 8c16g
5000万~2亿/天 < 2500行/秒 1分片2副本(优化后足够) 16c32g
2亿~10亿/天 < 12000行/秒 2分片2副本 16c32g × 4
> 10亿/天 > 12000行/秒 4+ 分片 按需扩展

关于Webfunny

Webfunny专注于前端监控系统,前端埋点系统的研发。 致力于帮助开发者快速定位问题,帮助企业用数据驱动业务,实现业务数据的快速增长。支持H5/Web/PC前端、微信小程序、支付宝小程序、UniApp和Taro等跨平台框架。实时监控前端网页、前端数据分析、错误统计分析监控和BUG预警,第一时间报警,快速修复BUG!支持私有化部署,Docker容器化部署,可支持千万级PV的日活量!

  点赞 0   收藏 0
  • 一步一个脚印一个坑
    共发布145篇文章 获得4个收藏
全部评论: 0