1. 为什么要在 Docker 里折腾 Doris 存算分离第一次接触 Doris 存算分离架构的时候我脑子里冒出来的第一个问题就是好好的存算一体不用为什么要拆开后来在一个日志分析项目里被现实教育了——计算节点和存储节点绑死扩容只能整体扩磁盘和 CPU 永远配不均衡成本压不下来。存算分离就是来解决这个问题的。Doris 的存算分离核心思路是把BEBackend的计算能力和数据存储解耦。数据不再落在本地磁盘上而是放到共享存储里计算节点变成无状态的工作单元可以随用随起、用完就销毁。这个架构在云原生场景下特别香尤其是需要弹性伸缩、按需付费的业务。那为什么用 Docker 来部署原因很直接Doris 集群组件多FE、BE、MS、Meta Service 等手动装一遍环境依赖能折腾半天版本对不上、端口冲突、配置文件散落各处换台机器又得重来。Docker 把这些东西打包成镜像一条命令拉起来环境隔离干净销毁重建也就几秒钟的事。对于想快速验证存算分离架构、或者做本地开发测试的人来说Docker 是最省事的路径。这篇内容适合谁看如果你是刚接触 Doris 的运维或后端开发想在自己机器上跑一套存算分离集群做验证或者你已经在用存算一体想评估迁移到分离架构的可行性那这篇实操记录应该能帮你少踩几个坑。我会从架构选型讲到具体部署步骤再到实际会遇到的问题排查尽量把每个决策背后的原因说清楚。需要提前说明的是存算分离架构对 Doris 版本有要求建议使用 2.1.x 及以上版本早期版本对分离模式的支持不完善Meta Service 的稳定性也有差异。下面所有操作都基于这个前提展开。2. 存算分离架构拆解与 Docker 部署方案选型2.1 存算分离到底分离了什么很多人以为存算分离就是把数据挪到 S3 就完事了其实远不止。Doris 存算分离架构里几个核心组件的职责发生了根本变化。FEFrontend依然负责元数据管理、查询解析和调度这一点没变。但BEBackend不再持久化数据它变成了纯计算节点本地磁盘只用来做缓存和临时文件。真正的数据落在共享存储上可以是 S3、OSS、MinIO 这类对象存储也可以是 HDFS。关键的新角色是Meta ServiceMS。它接管了原来 BE 里的一部分元数据管理职责负责管理 Tablet 的元信息、事务日志等。在存算一体模式下这些信息是存在 BE 本地 RocksDB 里的分离之后必须抽出来做成独立服务否则 BE 重启就丢元数据了。还有一个容易被忽略的点存算分离模式下BE 的本地磁盘不再是数据可靠性的保障。这意味着你不能像以前那样随便找块盘就往上怼缓存盘的选择会直接影响查询性能。我一般建议用 NVMe SSD 做缓存盘容量不用太大但 IOPS 要够。2.2 为什么选 Docker 而不是裸机部署裸机部署 Doris 存算分离不是不行但有几个现实问题依赖版本敏感Doris 对 glibc、JDK 版本有要求不同 Linux 发行版差异大装完经常遇到动态库缺失。组件启动顺序有讲究MS 要先于 BE 启动FE 又要和 MS 建立连接手动管理这些依赖关系很烦。环境清理困难测试完想彻底清干净得手动删目录、杀进程、清配置容易残留。Docker 方案把这些都封装掉了。用 docker compose 编排启动顺序、网络配置、卷挂载全部声明式管理docker compose down -v一条命令清干净。对于验证性部署和小规模测试环境这是效率最高的方式。不过要提醒一句Docker 部署不适合直接上生产。容器化的网络和存储性能有损耗生产环境建议用物理机或 K8s 集群部署。Docker 方案的价值在于快速验证和开发测试。2.3 共享存储的选择MinIO 还是真实对象存储存算分离必须有共享存储。本地测试最方便的是用 MinIO 模拟 S3 接口Docker 起一个 MinIO 容器配好 access key 和 secret key 就能用。如果你已经有云上的对象存储也可以直接对接但要注意网络延迟——存算分离对存储的访问延迟比存算一体敏感得多。我实测下来本地 MinIO 在同一个 Docker 网络里延迟可以控制在毫秒级做功能验证完全够用。但如果你的查询对延迟要求高MinIO 单节点可能成为瓶颈可以考虑多节点部署或者直接上云存储。存储方案适用场景优点注意事项MinIO 单节点本地开发测试部署简单S3 兼容单点故障性能有限MinIO 多节点小规模测试集群有一定冗余配置复杂度上升云对象存储生产/准生产高可用弹性容量网络延迟费用HDFS已有 Hadoop 生态与现有集群复用运维成本高2.4 版本与镜像选择Doris 官方在 Docker Hub 上提供了镜像但存算分离相关的镜像标签需要留意。我建议直接用apache/doris官方镜像标签选2.1.x系列的具体版本号不要用latest因为latest可能随时变今天能跑的配置明天可能就不兼容了。Meta Service 的镜像通常和 FE、BE 在同一个发布包里但启动命令不同。有些第三方镜像把 MS 单独拆出来了用的时候要确认镜像里是否包含doris_meta_service这个可执行文件。3. Docker 环境准备与核心配置实操3.1 基础环境检查在开始之前先确认你的 Docker 环境是正常的。运行docker version能看到 Client 和 Server 两端的信息说明 Docker 守护进程在跑。如果只看到 Client 信息Server 报错那可能是 Docker Desktop 没启动或者 Linux 上 docker 服务没开。内存方面存算分离集群至少需要8GB 可用内存FE 大概吃 2-3GB每个 BE 至少 2GBMS 也要 1GB 左右。磁盘至少留 20GB 给镜像和缓存。这些数字不是拍脑袋来的是我在多次部署中观察到的实际占用低于这个配置容器会频繁 OOM。端口方面Doris 默认用到的端口比较多提前确认没有被占用FE8030HTTP、9020RPC、9030MySQL 协议BE8040HTTP、9060RPC、9050BRPCMS5000gRPCMinIO9000API、9001控制台可以用ss -tlnp | grep -E 8030|9020|9030|8040|9060|9050|5000|9000|9001快速检查。3.2 目录结构规划我习惯把配置、数据、日志分开挂载这样容器销毁重建时数据不丢排查问题也方便。推荐目录结构doris-separated/ ├── docker-compose.yml ├── conf/ │ ├── fe.conf │ ├── be.conf │ └── ms.conf ├── data/ │ ├── fe/ │ ├── be/ │ └── ms/ └── logs/ ├── fe/ ├── be/ └── ms/这个结构的好处是所有持久化数据都在宿主机上容器只是运行环境。改配置直接改conf/下的文件重启容器生效不用进容器里改。3.3 docker-compose.yml 核心配置下面是我实际用的一套 compose 配置做了精简但保留了关键部分。先看整体结构再逐段解释。version: 3.8 services: minio: image: minio/minio:latest container_name: doris-minio command: server /data --console-address :9001 environment: MINIO_ROOT_USER: dorisadmin MINIO_ROOT_PASSWORD: Doris123456 ports: - 9000:9000 - 9001:9001 volumes: - ./data/minio:/data networks: - doris-net doris-ms: image: apache/doris:2.1.6-ms container_name: doris-ms environment: - MS_CONFIG/opt/apache-doris/ms/conf/doris_cloud.conf ports: - 5000:5000 volumes: - ./conf/ms.conf:/opt/apache-doris/ms/conf/doris_cloud.conf - ./data/ms:/opt/apache-doris/ms/data - ./logs/ms:/opt/apache-doris/ms/log depends_on: - minio networks: - doris-net doris-fe: image: apache/doris:2.1.6-fe container_name: doris-fe environment: - FE_CONFIG/opt/apache-doris/fe/conf/fe.conf ports: - 8030:8030 - 9020:9020 - 9030:9030 volumes: - ./conf/fe.conf:/opt/apache-doris/fe/conf/fe.conf - ./data/fe:/opt/apache-doris/fe/doris-meta - ./logs/fe:/opt/apache-doris/fe/log depends_on: - doris-ms networks: - doris-net doris-be: image: apache/doris:2.1.6-be container_name: doris-be environment: - BE_CONFIG/opt/apache-doris/be/conf/be.conf ports: - 8040:8040 - 9060:9060 - 9050:9050 volumes: - ./conf/be.conf:/opt/apache-doris/be/conf/be.conf - ./data/be:/opt/apache-doris/be/storage - ./logs/be:/opt/apache-doris/be/log depends_on: - doris-fe networks: - doris-net networks: doris-net: driver: bridge这里有几个关键决策需要解释。为什么用自定义 bridge 网络而不是默认网络默认 bridge 网络下容器之间只能用 IP 通信容器重启 IP 会变配置里写死的地址就失效了。自定义网络支持容器名 DNS 解析配置里直接写doris-ms、minio这样的服务名重启也不影响。为什么 MinIO 用 latest 而 Doris 用固定版本MinIO 的 S3 API 很稳定latest 基本不会出兼容问题。Doris 不同版本之间配置项和启动参数有差异固定版本能保证配置可复现。depends_on 能保证启动顺序吗只能保证容器启动顺序不能保证服务就绪。MS 容器起来了但 gRPC 端口还没监听FE 就去连会失败。所以实际部署时我建议手动分步启动确认前一个服务就绪后再起下一个。3.4 各组件配置文件详解ms.confMeta Service 配置# Meta Service 监听地址 host 0.0.0.0 port 5000 # 元数据存储路径 meta_service_storage_root_path /opt/apache-doris/ms/data # 对接的对象存储配置 storage_type S3 s3_endpoint http://minio:9000 s3_access_key dorisadmin s3_secret_key Doris123456 s3_bucket doris-data s3_region us-east-1s3_endpoint这里写的是minio而不是 IP因为它们在同一个 Docker 网络里用服务名解析。s3_region对 MinIO 来说其实不重要但 Doris 的 S3 客户端要求必填随便填一个合法的 region 名就行。fe.confFE 配置关键项# 开启存算分离模式 cloud_mode true # Meta Service 地址 cloud_meta_service_endpoint doris-ms:5000 # FE 元数据目录 meta_dir /opt/apache-doris/fe/doris-meta # 查询端口 query_port 9030 http_port 8030 rpc_port 9020 # 优先网络 priority_networks 172.16.0.0/12cloud_mode true是存算分离的总开关不开这个后面全白搭。priority_networks要填 Docker 网络的网段默认 bridge 网络一般是172.17.0.0/16或172.18.0.0/16具体用docker network inspect doris-net看一下。填错了 FE 会选错网卡导致 BE 注册不上。be.confBE 配置关键项# 存算分离模式 cloud_mode true # Meta Service 地址 cloud_meta_service_endpoint doris-ms:5000 # BE 本地存储路径只做缓存 storage_root_path /opt/apache-doris/be/storage # 缓存大小限制根据实际内存调整 file_cache_size 10737418240 # 心跳端口 heartbeat_service_port 9050 brpc_port 9060 webserver_port 8040file_cache_size我设了 10GB这是本地缓存的上限。存算分离下 BE 会缓存热数据到本地缓存越大查询越快但别超过宿主机可用内存的 60%。4. 分步部署与集群初始化全流程4.1 启动 MinIO 并创建 Bucket先起 MinIO因为 MS 启动时就要连对象存储桶不存在会直接报错。docker compose up -d minio等几秒访问http://localhost:9001用配置里的账号密码登录创建一个名为doris-data的 bucket。也可以用 mc 命令行工具创建docker exec -it doris-minio mc alias set local http://localhost:9000 dorisadmin Doris123456 docker exec -it doris-minio mc mb local/doris-data创建完确认一下 bucket 存在mc ls local能看到doris-data就对了。注意MinIO 的 root 用户密码至少 8 位且不能包含特殊字符导致 URL 解析问题。我用的Doris123456实测没问题但如果你换成带#或%的密码S3 客户端可能会解析失败。4.2 启动 Meta Servicedocker compose up -d doris-ms docker logs -f doris-ms日志里看到Meta Service started successfully或者 gRPC server 监听 5000 端口的记录说明 MS 就绪了。如果报连接 S3 失败检查 MinIO 是否正常、bucket 是否创建、endpoint 地址是否正确。MS 启动后会在data/ms目录下生成元数据文件可以ls -la data/ms确认。如果目录是空的说明 MS 没正常初始化看日志找原因。4.3 启动 FE 并确认注册docker compose up -d doris-fe docker logs -f doris-feFE 启动时会去连 MS日志里出现Successfully connected to meta service就说明连接建立。然后 FE 会开始监听 9030 端口可以用 MySQL 客户端连一下mysql -h 127.0.0.1 -P 9030 -u root能进去就说明 FE 正常。进去之后执行SHOW FRONTENDS;应该能看到当前 FE 节点Alive为true。4.4 启动 BE 并加入集群BE 启动前需要先在 FE 里注册 BE 节点。存算分离模式下BE 的注册方式和存算一体略有不同需要指定 compute group。docker compose up -d doris-be docker logs -f doris-beBE 日志里看到register to fe successfully就说明注册成功。然后在 MySQL 客户端里执行SHOW BACKENDS;应该能看到 BE 节点Alive为trueSystemDecommissioned为false。如果 BE 一直不出现检查priority_networks配置和网络连通性。4.5 创建存算分离表并验证集群就绪后建一张测试表验证存算分离是否真正生效CREATE DATABASE test_db; USE test_db; CREATE TABLE test_table ( id INT, name VARCHAR(50), create_time DATETIME ) DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 3 PROPERTIES ( replication_num 1, storage_medium HDD );存算分离模式下replication_num可以设为 1因为数据可靠性由共享存储保障不需要多副本。这是存算分离的一个显著优势——省存储成本。插入几条数据INSERT INTO test_table VALUES (1, alice, NOW()), (2, bob, NOW()); SELECT * FROM test_table;能查到数据就说明整个链路通了。再去 MinIO 控制台看doris-databucket应该能看到 Doris 写入的数据文件。4.6 验证计算节点无状态特性存算分离的核心卖点之一是 BE 无状态。验证方法很简单把 BE 容器删掉再重建数据应该还在。docker compose stop doris-be docker compose rm -f doris-be docker compose up -d doris-be等 BE 重新注册后再查test_table数据应该完整。如果数据丢了说明数据实际写到了本地而不是共享存储需要检查cloud_mode和 MS 配置。这个验证很重要我第一次部署时就是因为cloud_mode没在 BE 配置里生效数据写到了本地删容器就丢了。排查了半天才发现是配置文件挂载路径写错了容器里读的是默认配置。5. 常见问题排查与性能调优实录5.1 启动阶段高频问题速查问题现象可能原因排查方法解决方案MS 启动报 S3 连接失败endpoint 地址错误或 bucket 不存在检查 ms.conf 和 MinIO确认地址用服务名bucket 已创建FE 连不上 MS网络不通或 MS 未就绪docker exec doris-fe ping doris-ms确认同网络等 MS 完全启动BE 注册不上 FEpriority_networks 配置错误看 BE 日志的 IP 选择改成 Docker 网络实际网段建表报 no backendBE 未注册或 compute group 问题SHOW BACKENDS确认 BE Alive检查 compute group查询报 storage 错误对象存储权限或路径问题看 FE/BE 日志的 S3 报错检查 access key 和 bucket 权限5.2 网络配置踩坑记录Docker 网络这块我踩过最坑的一次是priority_networks配错了。当时 Docker 默认 bridge 网段是172.17.0.0/16我配了172.16.0.0/12结果 FE 选了一个不存在的网卡地址BE 怎么都注册不上。排查方法进容器ip addr看实际网卡和 IP然后docker network inspect doris-net看网段。两个对上了再填priority_networks。还有一个隐蔽问题如果宿主机上有多个 Docker 网络容器可能拿到多个 IP。priority_networks要填能覆盖容器实际通信网段的那个否则 FE 可能选错。5.3 存算分离特有的性能问题存算分离架构下查询性能对对象存储的延迟非常敏感。我实测下来同样的查询在存算一体下 200ms存算分离下可能到 500ms差距主要来自 S3 访问延迟。优化手段有几个加大 file_cache_size。BE 本地缓存命中时查询性能和存算一体差不多。缓存越大命中率越高。但要注意别把内存吃满留够给查询执行的内存。用本地 SSD 做缓存盘。如果 BE 的storage_root_path指向机械盘缓存读写会成为瓶颈。换成 NVMe SSD缓存命中时的性能提升很明显。合理设置分区和分桶。存算分离下扫描的数据量直接决定 S3 访问次数。分区裁剪和分桶裁剪能大幅减少不必要的数据读取。我一般建议分区粒度按天分桶数按数据量算单桶 1-2GB 比较合适。开启查询结果缓存。Doris 支持查询结果缓存对于重复查询能直接返回缓存结果完全绕过 S3 访问。5.4 容器资源限制与稳定性Docker 默认不限制容器资源BE 可能把宿主机内存吃光。建议在 compose 里加资源限制doris-be: deploy: resources: limits: memory: 8G reservations: memory: 4Glimits是硬上限超了容器会被 OOM kill。reservations是软保证Docker 会尽量预留。BE 的内存主要花在查询执行和缓存上限制太死会导致查询失败太松又影响宿主机其他服务。我一般给 BE 分配宿主机内存的 50%-60%。5.5 数据持久化与备份策略Docker 部署下数据分两部分元数据在data/fe和data/ms实际数据在 MinIO 的data/minio。备份策略要覆盖这两部分。元数据备份可以用 Doris 自带的BACKUP命令也可以直接打包data/fe和data/ms目录。MinIO 的数据目录直接 rsync 到备份盘就行。注意备份时最好先停掉写入或者用 Doris 的备份命令做一致性快照。直接拷贝运行中的元数据目录可能拿到不一致的状态。5.6 从测试到生产的差距Docker 单机部署验证完如果要上生产有几个差距要补高可用单 FE、单 BE、单 MS 都是单点生产至少 3 FE、多 BE、MS 也要考虑冗余。网络Docker bridge 网络的性能不如 host 网络或物理网络生产建议用 host 模式或 K8s CNI。存储MinIO 单节点不适合生产要么多节点 MinIO要么用云对象存储。监控Docker 部署缺少完善的监控生产需要接入 Prometheus GrafanaDoris 自带 metrics 接口。升级容器化升级需要重建容器生产环境要考虑滚动升级方案。我个人的经验是Docker 方案用来做架构验证和开发测试非常合适但别指望直接搬上生产。把 Docker 环境当成一个可丢弃的验证沙盒验证完架构可行性再用 K8s 或物理机方案正式部署这样最稳妥。最后分享一个实用技巧如果你经常需要重建环境可以把 MinIO 的数据目录单独挂载到宿主机的一个固定路径这样即使docker compose down -v清掉了容器卷MinIO 里的数据还在重建后重新挂载就能恢复。FE 和 MS 的元数据同理单独备份出来重建时恢复回去能省掉重新初始化的时间。
