ClickHouse 25.4 基于 Docker 单机部署实战指南
ClickHouse 25.4 基于 Docker 单机部署,是我这半年在测试环境和生产小集群里来回折腾后,觉得最值得写下来的一套组合。很多人第一次接触 ClickHouse,习惯直接去官网扒二进制包或者用 apt 装,结果不是被系统依赖坑了,就是升级的时候把自己搞到崩溃;而 Docker 单机部署恰好能绕开这堆破事——镜像一拉,目录一挂,几分钟就能起来一个能跑查询、能接数据、能备份恢复的实例。这篇文章就是给那些准备上手 ClickHouse,又不想在环境部署上浪费太多时间的人看的,不管是做数据分析、日志存储还是简单的用户画像场景,照着下面的步骤走一遍,基本都能顺利落地。
1. 部署选型:为什么单机环境也要走 Docker
1.1 三种部署方式对比,别再一上来就 apt install
先说结论:单机部署 ClickHouse,Docker 不一定是最“原生”的方式,但一定是最省心的方式。我把三种常见方式都试过,放在一起对比会更直观:
| 部署方式 | 安装速度 | 升级成本 | 系统耦合 | 适合场景 |
|---|---|---|---|---|
| 官方DEB/RPM包 | 快,但依赖多 | 中,需停服操作 | 高,库版本可能冲突 | 已用包管理维护的服务端 |
| 二进制tar.gz解压 | 快,无依赖 | 中,需手动迁数据 | 低,但配置全靠手写 | 对目录结构有洁癖的人 |
| Docker容器 | 最快,一条命令 | 极低,换镜像即可 | 极低,仅依赖内核 | 个人开发、测试、快速交付 |
我自己最早是在一台 CentOS 7 上用的二进制包部署,当时图它“原生、没中间层”。后来做版本升级,从 23.8 升到 24.3,光备份数据、停服务、替换二进制、恢复权限就折腾了一个下午。换成 Docker 之后,升级就是先 docker pull 新版本镜像,再 docker compose up -d 重新创建容器,数据目录原封不动挂进去,起来就是新版本。这种体验上的差距,只有被旧方式折磨过的人才懂。
1.2 部署前先检查宿主机 Docker 环境,不然全是坑
很多人的 ClickHouse 还没开始部署,先卡在 Docker 本身起不来。这里我把常见的宿主机环境检查项列出来,你对照着过一遍,省得后面踩坑。
Windows 用户如果装的是 Docker Desktop,最经典的问题就是启动时报错:Virtualization support not detected 或者 Docker Desktop failed to start because virtualization support wasn't detected。这基本就是 Hyper-V 或者 WSL2 没开。我的建议是直接装 WSL2 内核,然后在 Docker Desktop Settings 里把 Use WSL 2 based engine 勾上;如果 BIOS 里虚拟化被关掉了,那还得进 BIOS 把 Intel VT-x 或 AMD SVM 打开,这一步没法绕过。还有一类报错是 Failed to connect to the docker api at npipe:////./pipe/docker_engine,这个多半是 Docker Desktop 服务没起来,或者装了之后没完全启动,到 Windows 服务列表里确认 Docker Desktop Service 在运行,再不行就以管理员身份重启一下 Docker Desktop。
Linux 用户相对省事,但也要注意两点。第一,docker 服务启动失败时,先 systemctl status docker 看日志,常见原因是 selinux 拦截,临时可以 setenforce 0 验证,长期建议在 /etc/selinux/config 里改掉或者配置正确的策略。第二,老系统上 docker 版本太低,很多新镜像的配置项不支持,干脆把老版本卸干净,用官方脚本重装一版新的。我在 Ubuntu 20.04 上就处理过一次 docker 服务起不来的问题,最后发现是 /var/lib/docker 所在分区满了,日志和镜像把空间吃完了,清理之后就恢复了。
1.3 镜像拉取慢怎么办,先配置镜像加速源
国内网络环境下,docker pull 拉官方镜像慢是常态,ClickHouse 官方镜像有好几百 MB,不快一点真的很影响心情。解决思路很简单,就是给 Docker 配置镜像加速源。
Linux 上改 /etc/docker/daemon.json,Windows 上在 Docker Desktop 的 Settings -> Docker Engine 里改同一份 JSON,加一段 registry-mirrors 就可以。改完必须重启 docker 服务或重启 Docker Desktop 才生效,可以 docker info 查看 Registry Mirrors 一栏确认是否生效。用的时候可以这样写:
{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ] }注意镜像加速源属于“尽力而为”的服务,偶尔失效是正常的,所以我一般同时配两个。如果拉取还是很慢,就换个时间段再拉,或者找一台网络好的机器把镜像 save 成 tar 包,再 load 到目标机器上。这个方法在离线环境特别管用。
提示:配置完镜像源之后,如果 docker pull 仍然报 timeout 或 EOF,不要死磕一个源,直接换一个再试,多数情况是源又挂了。
2. 镜像选择与核心配置解析,搞懂这些参数再动手
2.1 ClickHouse 25.4 版本号怎么看,镜像 tag 怎么选
ClickHouse 的版本号规则很多人看不懂,其实非常简单:主版本号前两位是年份,后两位是月份。25.4 就是 2025 年 4 月发布的版本。这种按月发版的节奏意味着功能迭代很快,但同时也说明你没必要追最新,选一个够用、够稳的版本长期用就行。官方 Docker 镜像的命名是 clickhouse/clickhouse-server,tag 直接对应版本号,比如 clickhouse/clickhouse-server:25.4。
镜像 tag 的选择我有一条比较保守的经验:生产环境选 .2 或 .3 这种小版本,比如 25.4.3 这种,因为大版本刚发布时可能会有一些边角 bug,后续小版本会针对性地修掉;测试环境就无所谓,latest 也能用,但 latest 本质上是一个滚动标签,今天拉的 latest 和三个月后拉的 latest 不是一个东西,如果你需要可复现的环境,千万别用 latest。我见过有人写 docker-compose.yml 时图省事用 latest,结果半年后重新部署了一个新版本,配置语法变了,服务起不来,排查了半天才发现是镜像变了。
2.2 docker-compose.yml 逐项拆解,每个参数都有讲究
部署 ClickHouse 单机版,我强烈建议用 docker compose,而不是裸 docker run。docker compose 可以把端口、目录、环境变量、资源限制全部固化到文件里,换机器迁移时只需要带上这个文件和数据目录,其他什么都不用管。下面是我实测过的一套配置:
version: "3.8" services: clickhouse: image: clickhouse/clickhouse-server:25.4 container_name: clickhouse-server restart: unless-stopped ports: - "8123:8123" - "9000:9000" - "9009:9009" ulimits: nofile: soft: 262144 hard: 262144 volumes: - ./data:/var/lib/clickhouse - ./logs:/var/log/clickhouse-server - ./config.d:/etc/clickhouse-server/config.d:ro - ./users.d:/etc/clickhouse-server/users.d:ro environment: - TZ=Asia/Shanghai - CLICKHOUSE_PASSWORD=your_strong_password - CLICKHOUSE_DB=default - CLICKHOUSE_USER=default mem_limit: 4g先说端口。8123 是 HTTP 接口,用来跑查询、接 BI 工具;9000 是 native 协议端口,clickhouse-client 和大部分驱动程序都走这个;9009 是集群内部数据复制用的,单机部署其实用不到,但保留端口方便以后扩展成集群。如果 9000 端口被其他服务占了,可以在宿主机侧改映射,比如 "19000:9000",客户端连接时指定端口 19000 就行。
再说挂载目录。/var/lib/clickhouse 是数据目录,必须挂出来,否则容器一删数据全没了;/var/log/clickhouse-server 是日志目录,挂出来是为了排查问题方便;config.d 和 users.d 是自定义配置目录,ClickHouse 会自动读取这两个目录下的 xml 文件,用来覆盖镜像内的默认配置。这种“只读挂载配置文件”的做法,相当于把配置变更和镜像解耦,改配置不需要重做镜像,只需要改宿主机上的文件再重启容器。
ulimits 里 nofile 设置文件句柄数上限是必须的,ClickHouse 官方文档也建议调高这个值。因为 ClickHouse 是列式存储,查询时一个分区会打开很多文件,默认的 1024 句柄根本不够用,跑大查询时会出现 Too many open files 的报错。mem_limit 限制容器最大内存,防止 ClickHouse 在测试机上吃光所有内存把系统拖死。TZ 设置时区,不然 ClickHouse 的日志时间和你本地时间对不上,排查问题会很难受。
2.3 config.d 和 users.d 目录,配置文件优先级要搞明白
ClickHouse 的配置体系有一个继承和覆盖的关系。镜像内 /etc/clickhouse-server/config.xml 是主配置,config.d 目录下的文件会覆盖主配置的对应项;users.xml 是用户权限配置,users.d 目录下的文件会覆盖用户配置。这个机制有点像 CSS 的层叠样式表,后加载的、更具体的配置会覆盖默认值。
我一般会在 config.d 目录下放一个 01_memory.xml,用来调整内存相关的参数,内容如下:
<clickhouse> <max_server_memory_usage>3000000000</max_server_memory_usage> <max_memory_usage_for_all_queries>2000000000</max_memory_usage_for_all_queries> <max_concurrent_queries>100</max_concurrent_queries> <background_pool_size>8</background_pool_size> </clickhouse>max_server_memory_usage 的单位是字节,我这里的 3000000000 约等于 3GB,目的是给系统留 1GB 余量。max_memory_usage_for_all_queries 限制所有查询总共能用的内存,防止某个大查询把内存吃爆。max_concurrent_queries 控制并发查询数,默认值很大,测试机上调小一点可以保护自己。background_pool_size 是后台合并线程数,单机部署不需要太大,8 个足够了。
users.d 目录下放一个 01_user.xml 来覆盖用户密码,不建议直接改 users.xml,因为镜像升级时这个文件可能会被覆盖,而 users.d 是独立目录,升级不会动。示例:
<clickhouse> <users> <default> <password remove="1"/> <password_sha256_hex>你的SHA256哈希</password_sha256_hex> <networks> <ip>127.0.0.1</ip> <ip>::1</ip> <ip>你的内网网段</ip> </networks> <profile>default</profile> <quota>default</quota> </default> </users> </clickhouse>password_sha256_hex 的生成方法:echo -n "你的密码" | sha256sum,把输出的哈希值填进去。networks 里限制允许连接的 IP,生产环境务必不要留 0.0.0.0/0,否则相当于数据库裸奔在网络上,谁都能连。
3. 实操部署全流程,从零到能查数据
3.1 初始化目录结构和配置文件,一次到位
部署前先在宿主机上创建一个工作目录,比如 /opt/clickhouse,然后在里面建好 data、logs、config.d、users.d 这几个子目录:
mkdir -p /opt/clickhouse/{data,logs,config.d,users.d}接着把上面那段 docker-compose.yml 保存到 /opt/clickhouse/docker-compose.yml。然后创建 config.d/01_memory.xml 和 users.d/01_user.xml 这两个配置文件。注意用户密码配置我用的是 CLICKHOUSE_PASSWORD 环境变量来指定默认用户密码,如果你在 users.d 里又指定了 default 用户密码,两者可能会冲突,新版本镜像里环境变量优先,二选一就好。
我个人的习惯是:单机测试环境直接用 CLICKHOUSE_PASSWORD 环境变量设个临时密码,省事;一旦要长期使用,就把环境变量从 compose 文件里删掉,改用 users.d 方式管理密码,这样密码能跟随配置文件一起走,也更接近生产习惯。
3.2 启动容器并验证,别急着建表先看日志
目录和文件准备好之后,在 /opt/clickhouse 目录下执行:
docker compose up -d第一次启动要拉镜像,耐心等一会儿。启动完成后看容器状态:
docker ps看到 clickhouse-server 的状态是 Up,再去看日志有没有报错:
docker logs -f clickhouse-server正常情况下日志末尾会出现 Ready for connections 字样,说明服务已经就绪。如果容器一直处于 Restarting 状态,多半是配置文件写错了,可以用 docker logs 看具体报错,常见的就是 XML 格式不对,或者配置文件里的标签层级错了。这里教大家一个绕过容器直接验证配置的方法:
docker run -it --rm \ -v /opt/clickhouse/config.d:/etc/clickhouse-server/config.d:ro \ -v /opt/clickhouse/users.d:/etc/clickhouse-server/users.d:ro \ clickhouse/clickhouse-server:25.4 \ clickhouse server --config-file=/etc/clickhouse-server/config.xml \ --print-config这个命令会让 ClickHouse 解析配置并打印最终生效的配置项,如果配置文件有语法错误会直接报出来。这一步排查配置问题非常高效,比反复重启容器看日志省时间多了。
3.3 用 clickhouse-client 建表验证,数据能写能读才算完事
容器起来之后,进入容器内部执行 clickhouse-client:
docker exec -it clickhouse-server clickhouse-client --password your_strong_password看到 clickhouse-client 的提示符之后,先验证版本:
SELECT version();如果返回 25.4.x,说明版本正确。接着建一张最简单的 MergeTree 表试试:
CREATE TABLE test_events ( event_date Date, event_type String, event_value UInt64 ) ENGINE = MergeTree() PARTITION BY event_date ORDER BY (event_date, event_type);然后插入几条测试数据:
INSERT INTO test_events VALUES ('2025-04-01', 'view', 100), ('2025-04-01', 'click', 50), ('2025-04-02', 'view', 200);查询验证:
SELECT event_date, count() AS cnt, sum(event_value) AS total FROM test_events GROUP BY event_date ORDER BY event_date;能正确返回分组统计结果,说明整个链路已经通了。这时再顺便查一下系统表,看看刚建的表是否正常:
SELECT name, engine, partition_key FROM system.tables WHERE database = 'default';单机部署到这里已经算成功了。不过我建议你再多做一步:确认一下数据目录里真的写了文件。在宿主机执行:
ls -lh /opt/clickhouse/data/data/default/test_events/能看到按分区组织的目录结构和 .bin 数据文件,说明数据确实持久化到宿主机磁盘了,而不是停留在容器内部。这一步确认过之后,你才可以放心地删除容器再重新创建,数据不会丢。
4. 备份恢复与整体迁移,别等数据丢了才后悔
4.1 单机备份两大利器:clickhouse-client 导出与 BACKUP 命令
数据是数据库的命根子,单机部署尤其要重视备份,因为没有别的节点帮你兜底。ClickHouse 25.4 版本支持 BACKUP 命令,可以备份到本地目录或 S3。单机环境我一般备份到本地目录,一条 SQL 搞定:
BACKUP TABLE test_events TO Disk('backup_disk', 'test_events_backup');但要注意,使用 Disk 备份到本地需要先在 config.d 里配置备份磁盘。如果你不想折腾这块,更通用的做法是直接用 clickhouse-client 把数据导出成 TSV 或 Parquet 文件:
docker exec clickhouse-server clickhouse-client \ --password your_strong_password \ --query "SELECT * FROM test_events FORMAT TSV" \ > /opt/clickhouse/backup/test_events.tsv这种方式的好处是通用性强,不管 ClickHouse 版本怎么升级,数据都能导回来;缺点是大表导出速度慢,几十 GB 以上就不太适合了。我的习惯是:小表用 TSV 导出,大表用 BACKUP 命令,两者结合。
4.2 恢复流程,把备份数据导入新容器
恢复数据和备份是配套的。TSV 文件恢复很简单,先建好同结构的表,再用 clickhouse-client 把数据导入:
cat /opt/clickhouse/backup/test_events.tsv | \ docker exec -i clickhouse-server clickhouse-client \ --password your_strong_password \ --query "INSERT INTO test_events FORMAT TSV"导入完成后查一下 count() 是否等于备份前记录数,做一次校验。如果是 BACKUP 命令备份出来的,恢复时也是用 RESTORE 命令:
RESTORE TABLE test_events FROM Disk('backup_disk', 'test_events_backup');恢复之前最好先清空或重命名原表,避免主键冲突或数据重复。这块没啥黑科技,但一定要定期执行并验证,我见过太多人配好了备份却从没恢复过,真到恢复那天发现备份文件是坏的,那才叫欲哭无泪。
4.3 整体迁移思路,换机器不是复制粘贴那么简单
如果是把整个 ClickHouse 实例迁移到另一台机器,比如从测试机迁到正式服务器,最简单的方案就是“数据目录整体搬运”。这个方案我在实际环境验证过,前提是两边的 ClickHouse 版本一致,操作步骤是:先在旧机器上停容器,再用 rsync 或 tar 把 /opt/clickhouse/data 整个打包拷贝到新机器,最后在新机器上挂载这个数据目录启动新容器。
为啥要停容器再搬运?因为 ClickHouse 写入时数据是先写内存再异步落盘的,如果不停容器直接拷贝数据目录,可能会拷到还没来得及 merge 的中间状态文件,启动时容易报错。停服拷贝虽然会有一小段不可用时间,但胜在安全可控。迁移完成后,先用 SELECT count() 验证关键表的数据量,再抽查几条数据和时间范围,确认数据没丢没旧才算迁移成功。整体迁移这件事,最忌讳的就是图快而省略验证环节,数据完整性的确认永远值得多花几分钟。
5. 常见问题与排查技巧实录,把踩过的坑一次说清
5.1 ClickHouse 容器起不来、连不上、查询报错的典型场景
部署和使用过程中遇到的问题,我整理成了一张速查表,基本都是我实际踩过的:
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 容器启动后立刻退出或一直 Restarting | 配置文件 XML 格式错误,或数据目录权限不对 | docker logs 看报错;宿主机执行 chown -R 1000:1000 /opt/clickhouse/data 修复权限 |
| 宿主机 curl localhost:8123 无响应 | 端口没映射对,或容器内服务未就绪 | docker ps 确认端口映射;docker logs 看是否 Ready for connections |
| clickhouse-client 连接报错 | 密码不对,或 networks 限制了来源 IP | 检查 users.d 配置和容器环境变量,确认 CLICKHOUSE_PASSWORD |
| 查询报 Too many open files | 文件句柄数不够 | 检查 docker-compose.yml 里 ulimits 配置,调大后重启容器 |
| 写入时报 Memory limit exceeded | 内存参数设置过低或真实内存不足 | 调整 config.d 里的 max_server_memory_usage,或给 Docker 加内存 |
| 查询速度越来越慢 | 分区过多且未及时合并 | 执行 OPTIMIZE TABLE xxx FINAL,或调整 merge 线程参数 |
端口映射问题里还有一类很隐蔽的坑:容器公网 IP 地址变了。如果宿主机配了多个 IP,ClickHouse 可能绑定到了内网 IP 而不是公网 IP,导致你从外网访问不了。这种情况在 config.d 里监听配置改成 0.0.0.0 就能解决,但记住改成 0.0.0.0 之后必须搭配密码和 IP 白名单,不能裸奔。
5.2 日志文件膨胀、磁盘空间不足,这种慢性病最坑人
ClickHouse 默认会把每次查询的日志写到 system.query_log 表,这个表的数据默认会保留 30 天,如果查询量大,这个表会非常占空间。数据目录在宿主机上逐步膨胀,最终把磁盘撑爆。我之前就遇到过一台测试机,磁盘满了之后容器直接进入只读状态,写入全部失败,排查半天才发现是 system.query_log 太大。
解决办法有两个方向。第一个是定期清理:
ALTER TABLE system.query_log DELETE WHERE event_time < now() - INTERVAL 7 DAY;第二个是从根上控制保留时间,在 config.d 里加一个配置:
<clickhouse> <query_log> <database>system</database> <table>query_log</table> <flush_interval_milliseconds>7500</flush_interval_milliseconds> <partition_by>toYYYYMM(event_date)</partition_by> <retention_size>1000000000</retention_size> <retention_time>7</retention_time> </query_log> </clickhouse>retention_size 和 retention_time 分别控制日志表保留的最大字节数和天数,超过就自动清理。这样设置之后,日志膨胀问题基本不用再手动管了。
5.3 Docker 镜像下载慢、磁盘占用大,两个实用技巧分享
镜像下载慢的问题前面讲过配置镜像源,这里再补充两个冷门但好用的技巧。第一个是给 Docker 配置镜像仓库的垃圾回收策略。ClickHouse 镜像升级几次之后,旧镜像会残留不少空间,用 docker system df 一看,一堆 dangling 镜像占着好几十 GB。定期执行:
docker system prune -f可以清理悬空镜像和停止的容器。第二个技巧是把 Docker 的数据目录迁移到其他盘。很多人装 Docker 时默认把数据放在系统盘,结果系统盘被镜像撑爆了,我在做 Windows Docker Desktop 和 Linux 部署时都遇到过。
Linux 上修改 /etc/docker/daemon.json 添加>{ "data-root": "/data/docker" }
然后重启 docker 服务,旧数据如果有需要可以一并迁移过去。Windows 上 Docker Desktop 的 Settings -> Resources -> Advanced 里可以直接改 Disk image location。这个操作能很好地避免“系统盘被 Docker 占满导致服务各种诡异报错”的问题,属于一劳永逸的基础配置。
5.4 配置修改不生效、升级后行为变化,三个排查大招
改完配置重启容器后,发现配置没生效,这是非常常见的困惑。第一个排查大招是确认文件挂载是否生效。很多人改的是容器内部的路径,比如 docker exec 进去改了 /etc/clickhouse-server/config.xml,容器一删改动就没了,因为根本没挂载出来。正确的做法是:配置文件只放在宿主机 /opt/clickhouse/config.d 和 /opt/clickhouse/users.d 目录下,容器内禁止手动改文件。
第二个大招是分清 config.xml 和 config.d 的优先级。如果两个地方都配置了同一个参数,config.d 会覆盖 config.xml,但前提是 XML 的标签路径完全一致,写错层级就会静默不生效。遇到这种情况,用前面说的 clickhouse server --print-config 命令,把最终生效的配置打印出来,一目了然。
第三个大招是版本行为差异。ClickHouse 每个版本都在升级,25.4 相比旧版本改了不少默认值。比如旧版本默认允许 HTTP 接口不带密码访问,新版本对空密码的默认策略更严格。升级后突然连不上了,先想想是不是版本行为变化,然后去官网查该版本的 changelog。这个思路能省下大量排查时间。
部署 ClickHouse 25.4 单机版这件事,真正跑通之后你会发现它其实比想象中简单,真正花时间的往往是 Docker 环境本身和那些藏在细节里的配置项。我个人在实际操作中的体会是:不要一开始就追求生产级别的高可用和分布式,先把单机实例跑顺,把备份恢复和配置管理的基本功练熟,再往集群方向走会顺畅得多。最后再分享一个小技巧:把 docker-compose.yml 和 config.d、users.d 这几个文件放到一个 git 仓库里管理,每次改动都留痕,这样万一哪次改配置改挂了,回滚起来只要一条 git checkout 加重启容器的操作,比靠脑子记住“上次到底改了什么”靠谱多了。