
1. 为什么要在 Docker 里折腾 Doris 存算分离第一次听到Doris 存算分离这个词很多人脑子里冒出来的画面是把数据拆开存到对象存储里但真正动手部署时才发现坑不在概念上而在组件之间的依赖关系和网络配置上。我前后在测试环境里搭过五六次从最开始的单机 all-in-one 到后来的多节点分离架构踩过的坑足够写一篇完整的复盘。这篇就把整个部署链路拆开讲清楚包括为什么这么设计、每一步在干什么、哪些参数不能乱改。先把结论摆出来Doris 的存算分离架构核心是把计算节点Compute Node和存储层解耦。传统存算一体模式下BEBackend节点既管计算又管本地磁盘上的数据副本扩容时要搬数据缩容时要保证副本安全运维成本很高。存算分离之后数据落在共享存储通常是 S3 兼容的对象存储比如 MinIO上计算节点变成无状态的可以随时拉起、随时销毁弹性扩缩容变得非常轻量。那为什么用 Docker 来部署原因很实际Doris 的组件不少——FEFrontend、BEBackend、MSMeta Service存算分离模式下的元数据服务每个组件对系统环境、JDK 版本、依赖库都有要求。用 Docker 可以把这些环境差异全部封进镜像里本地开发机、测试服务器、CI 环境跑的是同一套东西不会出现我本地能跑服务器上起不来的经典问题。而且存算分离模式下组件更多手工装一遍的时间成本很高容器化之后一条docker compose up就能把整套拉起来。这篇文章适合三类人看一是想在自己机器上快速体验 Doris 存算分离架构的开发者二是需要在测试环境搭一套可复现集群的运维同学三是已经用过存算一体 Doris想搞清楚分离模式到底多了哪些组件、配置差异在哪里的老用户。下面我会从架构拆解开始一步步讲到镜像选型、网络配置、启动顺序、验证方法以及我实际踩过的那些坑。2. 存算分离架构里到底多了哪些组件2.1 FE、BE、MS 三者的职责边界存算一体模式下Doris 集群只有两类进程FE 和 BE。FE 负责元数据管理、查询规划、权限控制BE 负责数据存储和计算执行。数据以 tablet 的形式存在 BE 的本地磁盘上通常三副本。存算分离模式引入了一个新角色Meta ServiceMS。它的作用是管理元数据把原本 FE 里的一部分元数据职责拆出来同时配合共享存储做数据的持久化。BE 在分离模式下不再把数据写到本地磁盘而是写到 S3 兼容的对象存储上本地磁盘只作为缓存使用。这里有个容易混淆的点MS 不是替代 FE 的元数据功能而是专门为存算分离场景设计的元数据服务。FE 依然管 SQL 解析、查询计划这些MS 管的是 tablet 元数据、行集rowset元数据这类和存储强相关的东西。理解这个边界很重要后面配置的时候你就知道哪些参数该配在 FE 上哪些该配在 MS 上。2.2 共享存储为什么选 S3 兼容接口存算分离的存储这一层Doris 走的是 S3 协议。这意味着你可以用 AWS S3、阿里云 OSS、腾讯云 COS也可以在本地用 MinIO 搭一个 S3 兼容的服务。选 MinIO 的原因很简单它是单二进制、部署轻量、完全兼容 S3 API本地测试用起来最省事。对象存储在这里承担的是数据湖的角色。BE 写数据时先把数据写到本地缓存再异步上传到对象存储读数据时如果本地缓存命中就直接读没命中就从对象存储拉。这个缓存机制是性能的关键后面讲配置的时候会重点说缓存目录怎么设。2.3 容器化部署的组件拓扑用 Docker 部署时典型的拓扑是这样的组件容器名端口作用MinIOminio9000/9001S3 兼容对象存储Meta Servicedoris-ms5000存算分离元数据服务FEdoris-fe8030/9030查询入口、元数据管理BEdoris-be8040/9060计算执行、缓存管理四个容器跑在同一个 Docker 网络里通过容器名互相访问。这里要注意Doris 的组件之间通信对网络延迟比较敏感所以不要用默认的 bridge 网络跨主机通信本地测试用自定义 bridge 网络就够了生产环境要考虑网络性能。3. 镜像选型与 Docker 环境准备3.1 官方镜像和自建镜像怎么选Doris 官方在 Docker Hub 上提供了apache/doris系列的镜像包括apache/doris:fe-2.1.x、apache/doris:be-2.1.x这种按组件拆分的标签。存算分离模式需要 2.1 及以上版本因为 MS 组件是 2.1 才正式引入的。我的建议是测试环境直接用官方镜像生产环境考虑自建。官方镜像的好处是省事坏处是镜像体积大FE 镜像动辄 1GB 以上而且默认配置不一定符合你的需求。自建镜像可以基于官方镜像做二次封装把配置文件、启动脚本、健康检查都打进去。如果你要自建Dockerfile 大概长这样FROM apache/doris:fe-2.1.6 COPY fe.conf /opt/apache-doris/fe/conf/fe.conf COPY entrypoint.sh /entrypoint.sh RUN chmod x /entrypoint.sh ENTRYPOINT [/entrypoint.sh]注意版本号要对齐FE、BE、MS 三个组件的版本必须一致混版本会出现元数据不兼容的问题这个坑我踩过报错信息很隐晦排查了半天才发现是版本没对齐。3.2 Docker 环境的最低要求Doris 对内存比较敏感FE 默认 JVM 堆是 8GBBE 默认也是 8GB 起步。如果你在本地开发机上跑建议至少给 Docker 分配 16GB 内存否则容器会被 OOM Killer 干掉。Docker Desktop 用户要注意几个设置内存限制Settings → Resources → Memory调到 16GB 以上虚拟化支持Windows 用户需要开启 WSL2 或 Hyper-V如果报 virtualization support not detected去 BIOS 里开 VT-x/AMD-V磁盘镜像大小默认 64GB 可能不够对象存储的数据虽然不在容器里但缓存目录会占空间Linux 用户直接用系统 Docker 就行但要注意vm.max_map_count这个内核参数Doris 的 BE 需要它至少是 2000000sudo sysctl -w vm.max_map_count2000000这个参数不调BE 启动时会报 mmap 相关的错误而且错误信息不会直接告诉你是这个参数的问题很容易卡住。3.3 网络规划容器间怎么互相找到Docker Compose 默认会创建一个 bridge 网络所有服务在同一个 compose 文件里就能通过服务名互相访问。但 Doris 的组件注册机制有点特殊FE 注册到 MS 时会把自己的 IP 写进去BE 注册到 FE 时也一样。如果容器重启后 IP 变了注册信息就失效了。解决办法是给每个容器固定 IP或者用network_mode: hostLinux 下可行Mac/Windows 不行。我推荐固定 IP 的方式在 compose 文件里这样写networks: doris-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 services: doris-fe: networks: doris-net: ipv4_address: 172.20.0.10这样每个组件的 IP 是固定的重启也不会变注册信息一直有效。4. 从零编写 docker-compose 编排文件4.1 MinIO 服务的配置要点MinIO 是整套架构里最简单的一环但配置也有讲究minio: image: minio/minio:latest container_name: minio command: server /data --console-address :9001 environment: MINIO_ROOT_USER: dorisadmin MINIO_ROOT_PASSWORD: Doris123456 volumes: - ./minio-data:/data ports: - 9000:9000 - 9001:9001 networks: doris-net: ipv4_address: 172.20.0.5几个关键点MINIO_ROOT_PASSWORD长度至少 8 位否则 MinIO 会拒绝启动--console-address指定控制台端口方便你在浏览器里查看 bucket数据目录挂载到宿主机容器删了数据还在。启动后第一件事是创建 bucket。Doris 不会自动创建 bucket你需要手动建一个比如叫doris-data。可以在 MinIO 控制台点几下建好也可以用mc命令行工具docker exec minio mc alias set local http://localhost:9000 dorisadmin Doris123456 docker exec minio mc mb local/doris-data4.2 Meta Service 容器的启动参数MS 是存算分离的核心它的配置直接决定整个集群能不能起来doris-ms: image: apache/doris:ms-2.1.6 container_name: doris-ms environment: - MS_CONFIGmeta_service.conf volumes: - ./ms.conf:/opt/apache-doris/ms/conf/meta_service.conf - ./ms-data:/opt/apache-doris/ms/data ports: - 5000:5000 networks: doris-net: ipv4_address: 172.20.0.6 depends_on: - miniometa_service.conf里最关键的是对象存储的连接信息s3_endpointhttp://172.20.0.5:9000 s3_access_keydorisadmin s3_secret_keyDoris123456 s3_bucketdoris-data s3_regionus-east-1注意s3_endpoint要用容器 IP 或容器名不能用localhost因为 MS 是在容器里访问 MinIO 的。s3_region虽然 MinIO 不校验但 Doris 的 SDK 要求必填随便填一个就行。4.3 FE 与 BE 的差异化配置FE 的配置重点是告诉它 MS 在哪里meta_service_endpoint172.20.0.6:5000 deploy_modeclouddeploy_modecloud这个参数是存算分离模式的开关不设的话 FE 会按存算一体模式启动然后找不到 MS 就报错。BE 的配置多了缓存相关的参数meta_service_endpoint172.20.0.6:5000 deploy_modecloud storage_root_path/opt/apache-doris/be/storage file_cache_path/opt/apache-doris/be/cache file_cache_size10737418240file_cache_size是本地缓存大小单位字节这里设了 10GB。缓存越大读性能越好但别超过容器可用内存。storage_root_path在分离模式下只存缓存和临时文件不存实际数据。4.4 启动顺序与依赖关系Doris 的组件启动有严格顺序MinIO → MS → FE → BE。MS 启动时要连 MinIO 初始化元数据FE 启动时要连 MS 注册自己BE 启动时要连 FE 和 MS。在 compose 文件里用depends_on控制顺序但depends_on只保证容器启动顺序不保证服务就绪。所以更稳妥的做法是在启动脚本里加健康检查或者手动分步启动docker compose up -d minio sleep 10 docker compose up -d doris-ms sleep 15 docker compose up -d doris-fe sleep 20 docker compose up -d doris-be这个 sleep 时间不是随便写的MS 初始化元数据大概需要 10 秒左右FE 注册到 MS 需要 15 秒左右BE 启动最慢因为它要初始化缓存目录。实际时间取决于机器性能第一次启动建议多等一会儿。5. 部署过程中最容易翻车的几个点5.1 容器 IP 变化导致注册失效前面提过固定 IP 的重要性这里展开说下症状。如果你没固定 IP容器重启后 BE 可能连不上 FE日志里会看到heartbeat failed或者register to fe failed。更隐蔽的情况是 FE 连不上 MS日志里报meta service unavailable但 MS 容器明明是 running 状态。排查方法进容器ping一下对方的 IP看通不通。如果不通八成是 IP 变了。修复就是重新配置固定 IP然后重启所有容器。5.2 对象存储连接超时的排查链路BE 写数据到 MinIO 超时是很常见的坑报错信息通常是S3 request failed或者timeout。排查链路是这样的先进 BE 容器用curl测试 MinIO 的 endpoint 通不通curl http://172.20.0.5:9000如果通检查 access key 和 secret key 对不对如果 key 对检查 bucket 存不存在如果 bucket 存在检查 bucket 的权限策略MinIO 默认是 private需要确认 key 有读写权限我遇到过一次是 bucket 名字写错了配置里写的是doris_data实际建的是doris-data下划线和中划线的区别排查了半小时。5.3 内存不足引发的容器反复重启Doris 的组件对内存很敏感尤其是 BE。如果 Docker 分配的内存不够BE 启动到一半会被 OOM Killer 干掉然后 Docker 的 restart policy 会把它拉起来再被干掉形成循环。判断方法docker inspect doris-be看State.OOMKilled是不是 true。如果是就得加内存或者调小 BE 的 JVM 堆和缓存大小。调小 JVM 堆在be.conf里改JAVA_OPTS-Xmx4g -Xms4g但注意堆调太小会影响查询性能测试环境 4GB 勉强够用生产环境建议 16GB 起步。5.4 版本不一致导致的元数据不兼容FE、BE、MS 三个组件的版本必须完全一致。我试过 FE 用 2.1.6、BE 用 2.1.5结果 BE 注册到 FE 时报version mismatch而且这个错误在 FE 日志里只出现一次很容易漏看。检查方法进每个容器执行cat /opt/apache-doris/*/version对比版本号。不一致就统一换成同一个版本。6. 集群启动后的验证与基础操作6.1 用 MySQL 客户端连上 FEDoris 的 FE 兼容 MySQL 协议可以用任何 MySQL 客户端连接mysql -h 127.0.0.1 -P 9030 -u root默认没有密码直接回车就能进。进去之后先看集群状态SHOW BACKENDS; SHOW FRONTENDS;SHOW BACKENDS里Alive字段是 true 就说明 BE 注册成功了。如果 BE 没出现检查 BE 容器的日志。6.2 建表验证存算分离是否生效建一张测试表插入数据然后去 MinIO 控制台看 bucket 里有没有文件生成CREATE DATABASE test_db; USE test_db; CREATE TABLE test_table ( id INT, name VARCHAR(50) ) DISTRIBUTED BY HASH(id) BUCKETS 1 PROPERTIES (replication_num 1); INSERT INTO test_table VALUES (1, test);插入之后去 MinIO 的doris-databucket 里看应该能看到类似data/开头的目录结构。如果有说明存算分离生效了数据确实写到了对象存储。6.3 查询性能的初步观察存算分离模式下第一次查询会走对象存储速度比存算一体慢因为要拉数据到本地缓存。第二次查询如果缓存命中速度就正常了。可以用EXPLAIN看查询计划确认有没有走缓存。我实测下来小数据量几万行的查询第一次大概 1-2 秒缓存命中后 100 毫秒以内。数据量大之后差距更明显所以缓存目录一定要放在 SSD 上。7. 几个实际使用中的经验补充7.1 缓存目录的磁盘选择BE 的file_cache_path建议放在 SSD 上机械硬盘的随机读性能太差缓存命中率再高也快不起来。如果是 Docker 部署可以把宿主机的 SSD 目录挂载进去volumes: - /ssd/doris-cache:/opt/apache-doris/be/cache7.2 日志排查的入口Doris 的日志分散在各个组件里FE 的日志在/opt/apache-doris/fe/log/fe.logBE 的在/opt/apache-doris/be/log/be.logMS 的在/opt/apache-doris/ms/log/meta_service.log。出问题先看 BE 的日志大部分存储相关的问题都在那里。7.3 停止和清理的正确姿势停止集群用docker compose down但注意数据目录是挂载在宿主机的down不会删数据。如果要彻底清理得手动删挂载目录docker compose down rm -rf ./minio-data ./ms-data ./fe-data ./be-data但删之前确认数据不要了对象存储里的数据删了就真没了。7.4 后续可以扩展的方向这套单机 Docker 部署跑通之后可以往几个方向扩展一是把 BE 拆成多个容器模拟多计算节点二是把 MinIO 换成真实的 S3 或 OSS测试云上环境三是加上监控Doris 自带 Prometheus 接口可以接 Grafana 看指标。这些等基础环境稳定了再折腾先把单节点跑通最重要。我个人在实际操作中的体会是Doris 存算分离的 Docker 部署难点不在 Docker 本身而在组件之间的依赖关系和网络配置。把 IP 固定好、版本对齐、启动顺序控制住基本就不会有大问题。剩下的就是调参数根据机器配置和业务需求慢慢优化。