1. 为什么要在 Docker 里折腾 Doris 存算分离把 Doris 拆成计算和存储两层来部署这件事在最近一年被问到的频率明显高了起来。原因其实不复杂传统一体化部署里BE 节点既管数据落盘又管查询计算扩容的时候存储和算力被迫一起加成本压不下来而存算分离把数据放到共享存储对象存储或 HDFS上计算节点变成无状态可以按查询压力弹性伸缩闲时缩到零也不是不行。Doris 从 3.0 版本开始正式把存算分离作为一等公民来支持社区里讨论doris 集群部署doris 使用的帖子底下十有八九会有人追问分离模式怎么搭。但真到自己动手很多人第一步就卡住了——官方文档给的是物理机或 K8s 的部署路径手头只有一台开发机或者一台测试服务器想先用 Docker 把整套链路跑通看看效果结果发现镜像怎么选、FE 和 BE 的配置怎么改、共享存储挂哪里、MSMeta Service这个新组件又该怎么起全是问号。我前后在三四台不同配置的机器上折腾过这套组合踩的坑足够写一篇完整的记录所以这篇就把 Doris 存算分离的 Docker 部署从头到尾捋一遍重点讲清楚每一步为什么这么做以及哪些地方最容易翻车。这篇文章适合三类人看一是已经用过 Doris 一体化部署、想迁移到分离架构的运维或后端二是想快速在本地验证存算分离特性、不想被 K8s 环境劝退的开发者三是单纯想搞明白存算分离到底分离了什么、MS 组件扮演什么角色的技术好奇者。全程用 Docker 和 Docker Compose 操作不需要额外的集群编排工具一台 8 核 16G 以上的机器就能跑起来。先说清楚一个前提Doris 存算分离模式下有三个核心角色——FEFrontend负责元数据和查询规划、BEBackend负责计算分离模式下也叫 Compute Node、MSMeta Service负责元数据管理和事务。MS 是分离模式新增的一体化部署里没有这个东西。数据本身放在共享存储上BE 本地只留缓存。理解了这个角色划分后面的配置就不会觉得莫名其妙。2. 部署前的环境盘点与镜像选择2.1 机器配置和 Docker 环境的硬性门槛Doris 存算分离对资源的要求比一体化模式要高一截因为 MS 本身要占内存BE 的缓存也要留空间。我实测下来单机跑通全链路的最低配置是 8 核 CPU、16G 内存、50G 以上磁盘。如果内存只有 8GBE 启动后很容易因为缓存分配失败而反复重启日志里会看到allocate memory failed之类的报错。磁盘方面如果用本地目录模拟共享存储要额外留出数据空间如果接对象存储本地磁盘主要给日志和缓存用20G 也够。Docker 版本建议 20.10 以上Compose 用 v2 版本就是docker compose而不是docker-compose那个命令。这里有个高频坑Windows 和 Mac 上装 Docker Desktop启动时报virtualization support not detected或者docker desktop failed to start because virtualization support not detected这基本是 BIOS 里虚拟化没开或者和 Hyper-V、WSL2 的配置冲突。Linux 上则是failed to connect to the docker api at npipe这类问题多半是 Docker 服务没起来或者当前用户不在 docker 组里。这些环境问题不解决后面所有步骤都是白搭所以先把docker info跑通再说。还有一个容易被忽略的点Doris 的 BE 在分离模式下需要能访问共享存储如果共享存储是宿主机上的一个目录容器里要通过 volume 挂进去路径权限要对得上。我见过有人挂载后 BE 一直报权限拒绝查了半天发现是容器内 UID 和宿主机目录 owner 不一致。建议挂载目录统一用 1000:1000 或者直接 chmod 777 先跑通生产环境再收紧。2.2 镜像 tag 怎么选才不踩雷Doris 官方在 Docker Hub 上提供的镜像一体化模式常用的是apache/doris:fe-x.x.x和apache/doris:be-x.x.x这种分开的 tag。但存算分离模式需要 MS 组件官方镜像里 MS 是单独打包的tag 形如apache/doris:ms-x.x.x。这里第一个大坑就是不是所有版本都提供 MS 镜像。3.0 早期的一些小版本镜像里根本没有 ms 这个 tag你docker pull会直接报 manifest unknown。我建议直接用 3.0.3 及以上的稳定版镜像齐全bug 也少。选 tag 的时候还要注意 FE、BE、MS 三个镜像的版本必须完全一致。我试过 FE 用 3.0.3、BE 用 3.0.2结果 BE 注册到 FE 的时候报版本不兼容日志里提示version mismatch。所以拉镜像的时候三个一起拉tag 写死同一个版本号别图省事用 latestlatest 指向哪个版本完全看运气。组件推荐镜像作用内存建议FEapache/doris:fe-3.0.3元数据、查询规划4GBEapache/doris:be-3.0.3计算、缓存8GMSapache/doris:ms-3.0.3元数据服务、事务2G拉镜像的命令很直接docker pull apache/doris:fe-3.0.3 docker pull apache/doris:be-3.0.3 docker pull apache/doris:ms-3.0.3如果拉取速度慢配置一下国内镜像加速器这个属于 Docker 基础操作不展开。拉完之后用docker images确认三个镜像都在tag 一致再往下走。2.3 共享存储用什么方案模拟存算分离的核心是共享存储生产上一般用 S3、OSS 这类对象存储或者 HDFS。但本地验证阶段没必要真去开一个对象存储账号用本地文件系统模拟就行。Doris 支持file://协议的共享存储路径虽然官方不推荐用于生产但测试完全够用。具体做法是在宿主机上建一个目录比如/data/doris/shared_storage然后通过 volume 挂载到 BE 和 MS 容器里。BE 写数据、MS 管元数据都指向这个目录。这里要注意MS 和 BE 必须挂载同一个宿主机目录否则 MS 记录的元数据路径和 BE 实际写入的路径对不上查询的时候会报文件找不到。如果你想让测试更接近生产可以用 MinIO 在 Docker 里起一个 S3 兼容的对象存储Doris 配置里填 MinIO 的 endpoint 和 access key。这个方案稍微复杂一点但能提前暴露对象存储相关的配置问题比如 endpoint 格式、region 设置、path style 访问等。我个人的建议是第一次跑通先用本地目录确认整套链路没问题后再换成 MinIO 验证对象存储配置分两步走排错的时候不会眉毛胡子一把抓。3. 用 Docker Compose 编排三个核心组件3.1 网络规划为什么必须自定义 bridge 网络Doris 三个组件之间要互相通信BE 要连 FE 注册FE 要连 MS 拿元数据BE 还要连 MS 做事务。如果直接用默认的 bridge 网络容器之间只能用 IP 互相访问而 IP 在容器重启后会变配置写死了就会失效。所以必须创建一个自定义的 bridge 网络让容器之间能用容器名做 DNS 解析。docker network create doris-net创建完之后在 Compose 文件里给每个服务指定networks: - doris-net这样 FE、BE、MS 之间就能用服务名互相 ping 通了。这个点看起来简单但我见过太多人图省事用默认网络结果 BE 注册不上 FE日志里全是连接超时查半天才发现是网络隔离的问题。自定义网络还有一个好处可以固定子网段避免和宿主机所在网段冲突。比如宿主机是 192.168.1.0/24Doris 网络就用 172.20.0.0/16互不干扰。Compose 里可以这样写networks: doris-net: driver: bridge ipam: config: - subnet: 172.20.0.0/163.2 MS 容器的启动参数与配置要点MS 是存算分离的大脑它管的是元数据、事务日志和集群成员信息。启动 MS 容器的时候最关键的是把共享存储路径和监听地址配对。MS 默认监听 5000 端口HTTP和 5100 端口内部 RPCFE 和 BE 都要能访问到。MS 的配置文件在容器里的路径是/opt/apache-doris/ms/conf/doris_cloud.conf核心配置项有这么几个# MS 监听地址0.0.0.0 表示所有网卡 brpc_listen_port 5000 # 元数据存储路径指向共享存储 meta_service_brpc_port 5100 # 共享存储根路径 storage_root_path /mnt/shared_storage这里有个细节storage_root_path在 MS 里指的是元数据存放的根目录不是数据文件目录。数据文件目录是 BE 那边配置的。两个路径可以指向同一个共享存储的不同子目录比如 MS 用/mnt/shared_storage/metaBE 用/mnt/shared_storage/data这样结构清晰排查问题的时候一眼能看出哪个目录是谁写的。启动 MS 容器的命令大致是这样docker run -d --name doris-ms \ --network doris-net \ -v /data/doris/shared_storage:/mnt/shared_storage \ -p 5000:5000 -p 5100:5100 \ apache/doris:ms-3.0.3注意端口映射5000 是给 FE 和 BE 连的5100 是 MS 内部用的两个都要暴露。如果只映射 5000BE 连 MS 的时候会报 RPC 连接失败。3.3 FE 配置里那几个必须改的项FE 在分离模式下的配置和一体化模式差别不大但有几个关键项必须改。FE 的配置文件在/opt/apache-doris/fe/conf/fe.conf需要关注的是# 元数据存储路径FE 自己的元数据还是放本地 meta_dir /opt/apache-doris/fe/doris-meta # 开启存算分离模式 cloud_mode true # MS 的地址格式是 host:port cloud_meta_service_endpoint doris-ms:5100 # FE 自己的编辑日志端口 edit_log_port 9010 # 查询端口 query_port 9030 # HTTP 端口 http_port 8030cloud_mode true这个开关是分离模式的入口不开的话 FE 会按一体化模式启动根本不认 MS。cloud_meta_service_endpoint填的是 MS 的容器名加 5100 端口因为我们在同一个自定义网络里容器名能直接解析。FE 启动的时候还有个坑如果meta_dir目录里有之前一体化模式留下的元数据FE 启动会失败报元数据版本不兼容。所以第一次跑分离模式务必用一个干净的目录或者把旧数据清掉。我一般会在宿主机上给 FE 单独建一个目录挂进去比如/data/doris/fe_meta这样容器删了重建元数据还在不用每次重新初始化。3.4 BE 作为计算节点的特殊配置BE 在分离模式下叫 Compute Node它的配置变化最大。核心是告诉它数据不存本地去共享存储读写。BE 的配置文件在/opt/apache-doris/be/conf/be.conf# 开启存算分离 cloud_mode true # MS 地址 cloud_meta_service_endpoint doris-ms:5100 # 共享存储路径BE 从这里读写数据 storage_root_path /mnt/shared_storage/data # BE 自己的心跳端口 heartbeat_service_port 9050 # BE 的 brpc 端口 brpc_port 9060 # Web 端口 webserver_port 8040storage_root_path在 BE 里指的是数据文件的存放位置在分离模式下这个路径实际指向共享存储。BE 本地还会有一个缓存目录默认在be/storage下可以通过file_cache_path单独指定。缓存的作用是加速热数据查询第一次查冷数据会从共享存储拉拉完缓存在本地下次就快了。BE 启动后要注册到 FE注册地址通过--be_port和--host参数指定。在 Docker 里BE 的 host 就是容器名doris-beFE 那边通过ALTER SYSTEM ADD BACKEND doris-be:9050把它加进来。这一步如果网络不通或者端口写错BE 会一直处于ALIVE之外的状态SHOW BACKENDS里看到的是dead。4. 从零到一跑通全链路的实操步骤4.1 目录准备与权限处理先把宿主机上的目录结构建好这一步看着琐碎但目录乱了后面排错很痛苦。我习惯这样组织mkdir -p /data/doris/shared_storage/meta mkdir -p /data/doris/shared_storage/data mkdir -p /data/doris/fe_meta mkdir -p /data/doris/be_cache chmod -R 777 /data/dorisshared_storage/meta给 MS 用shared_storage/data给 BE 用fe_meta是 FE 自己的元数据be_cache是 BE 的本地缓存。权限先给 777 保证跑通生产环境再按最小权限原则收紧。这里再强调一次MS 和 BE 挂载的shared_storage必须是同一个宿主机目录否则元数据和数据对不上。4.2 启动顺序MS 必须先于 FE 和 BE三个组件的启动顺序有讲究MS 最先起FE 其次BE 最后。因为 FE 启动时要连 MS 拿集群元数据MS 没起来 FE 会启动失败BE 启动时要连 FE 注册FE 没起来 BE 会反复重试。启动 MSdocker run -d --name doris-ms \ --network doris-net \ -v /data/doris/shared_storage:/mnt/shared_storage \ -p 5000:5000 -p 5100:5100 \ apache/doris:ms-3.0.3等 MS 日志里出现MS service started之类的字样再启动 FEdocker run -d --name doris-fe \ --network doris-net \ -v /data/doris/fe_meta:/opt/apache-doris/fe/doris-meta \ -p 8030:8030 -p 9030:9030 -p 9010:9010 \ apache/doris:fe-3.0.3FE 启动后用docker logs doris-fe看日志等到出现FE started并且没有报 MS 连接错误再启动 BEdocker run -d --name doris-be \ --network doris-net \ -v /data/doris/shared_storage:/mnt/shared_storage \ -v /data/doris/be_cache:/opt/apache-doris/be/storage \ -p 8040:8040 -p 9050:9050 -p 9060:9060 \ apache/doris:be-3.0.3BE 启动后不会自动注册到 FE需要手动加。用 MySQL 客户端连 FE 的 9030 端口mysql -h 127.0.0.1 -P 9030 -u root然后执行ALTER SYSTEM ADD BACKEND doris-be:9050;执行完等几秒SHOW BACKENDS\G看一下如果Alive是true说明 BE 注册成功。如果一直是false去 BE 日志里找heartbeat相关的报错多半是 FE 地址或端口配错了。4.3 验证存算分离是否真的生效BE 注册成功后建个库建张表插点数据然后去共享存储目录里看看数据文件是不是真的写到了/data/doris/shared_storage/data下面。如果数据出现在 BE 容器的本地目录而不是共享存储说明cloud_mode没生效回去检查 BE 配置。CREATE DATABASE test_db; USE test_db; CREATE TABLE t1 (id INT, name VARCHAR(50)) DISTRIBUTED BY HASH(id) BUCKETS 1; INSERT INTO t1 VALUES (1, a), (2, b), (3, c); SELECT * FROM t1;查询能出结果同时共享存储目录下出现了数据文件这才算真正跑通了存算分离。我见过有人配置写对了但数据还是落本地最后发现是 BE 启动时加载了旧的be.conf容器里的配置文件被覆盖了。所以每次改配置最好把容器删了重建别用docker restartrestart 不会重新读配置文件。5. 那些让我熬夜的报错与排查思路5.1 BE 注册不上 FE 的三种典型原因BE 注册失败是最常见的坑表现是SHOW BACKENDS里Alive为false或者压根看不到 BE。排查的时候按这个顺序来第一网络通不通。进 BE 容器docker exec -it doris-be bash然后ping doris-fe能 ping 通说明 DNS 解析没问题。ping 不通就是网络没配对检查两个容器是不是在同一个自定义网络里。第二端口对不对。BE 注册用的是 9050 端口heartbeat_service_portFE 那边ALTER SYSTEM ADD BACKEND填的端口必须和 BE 配置里的一致。我遇到过有人填了 9060brpc 端口结果一直注册不上改成 9050 就好了。第三FE 的cloud_mode开没开。如果 FE 没开分离模式它不认 MSBE 注册的时候会报元数据服务不可用。检查 FE 日志里有没有cloud mode is disabled之类的提示。5.2 MS 启动后 FE 连不上的排查链路FE 连不上 MS日志里一般会报connect to meta service failed或者brpc connection refused。这时候先确认 MS 容器是不是真的起来了docker ps看状态docker logs doris-ms看有没有报错。MS 启动失败常见的原因是storage_root_path目录不存在或者没权限容器里访问不到就起不来。如果 MS 起来了但 FE 连不上进 FE 容器telnet doris-ms 5100试试不通就是网络问题。通了还报错检查 FE 配置里cloud_meta_service_endpoint的格式必须是host:port不能带http://前缀。这个细节文档里没强调但填错了 FE 会解析失败。还有一种情况是 MS 的 5100 端口没映射到宿主机但 FE 和 MS 在同一个 Docker 网络里其实不需要映射也能通。映射端口主要是为了方便宿主机上的工具访问容器间通信走的是内部网络不受端口映射影响。5.3 数据写入共享存储失败的权限陷阱数据写不进共享存储BE 日志里会报permission denied或者read-only file system。前面提过 UID 不一致的问题这里展开说Doris 容器里跑 BE 进程的用户 UID 通常是 1000如果宿主机上/data/doris/shared_storage的 owner 是 root权限是 755那 UID 1000 的用户就没有写权限。解决办法要么把目录 owner 改成 1000:1000要么 chmod 777。还有一个隐蔽的坑如果共享存储目录是通过 NFS 挂载的NFS 的root_squash选项会把 root 用户映射成 nobody导致权限判断和本地文件系统不一样。本地测试用本地目录就好别引入 NFS 增加变量。6. 跑通之后的一些调优和扩展思路6.1 BE 缓存大小怎么定才不浪费内存BE 的本地缓存默认会占用不少内存如果机器内存紧张可以调小。缓存相关的配置在be.conf里# 缓存路径 file_cache_path /opt/apache-doris/be/storage/cache # 缓存大小上限单位字节 file_cache_size 1073741824file_cache_size默认可能是 2G 或更大8G 内存的机器上建议调到 1G 左右留出内存给查询执行。缓存太小会影响热数据查询性能太大又可能触发 OOM需要根据实际查询模式权衡。我的经验是如果查询集中在少数几张表上缓存给大点如果查询很分散缓存命中率低给再大也是浪费。6.2 从本地目录切换到对象存储要改什么本地目录跑通后想换成 MinIO 或真实对象存储主要改两个地方MS 的storage_root_path和 BE 的storage_root_path都要改成对象存储的 URL 格式比如s3://bucket-name/path。同时要配置 access key、secret key、endpoint 和 region这些在doris_cloud.conf和be.conf里都有对应的配置项。切换的时候有个顺序先停 BE再停 FE最后停 MS改完配置按 MS、FE、BE 的顺序重启。如果直接改配置重启MS 里记录的旧路径和新路径对不上会报元数据不一致。稳妥的做法是清空共享存储重新初始化测试环境数据不重要的话这是最省事的方案。6.3 这套单机编排怎么平滑扩展到多 BE单机跑通后想加第二个 BE 节点Docker Compose 里复制一份 BE 服务改容器名和端口映射然后ALTER SYSTEM ADD BACKEND把新节点加进去就行。因为 BE 是无状态的数据都在共享存储上新节点起来后能直接读到已有数据不需要数据迁移。这正是存算分离的价值所在——扩容计算节点变得非常简单。不过多 BE 的时候要注意每个 BE 的be_port、brpc_port、webserver_port在宿主机上映射的端口不能冲突。容器内部端口可以一样但宿主机映射要错开比如第二个 BE 映射成 9051、9061、8041。Compose 里用不同的 service 名区分网络还是同一个doris-net。最后分享一个我自己的习惯每次部署完把docker inspect出来的容器 IP、端口映射、挂载路径整理成一张表存下来下次出问题的时候直接对照比翻日志快得多。这套 Doris 存算分离的 Docker 部署我前后重装了七八次才把每个坑都摸清楚希望这篇记录能让你少走点弯路。
