
DolphinScheduler 跑工作流,脚本、依赖 JAR、UDF 这些资源文件默认存在各节点的本地盘上。单机环境没问题,集群一扩节点、机器一换,资源就找不到了,依赖这些文件的任务也跟着挂。更麻烦的是,运维人员把资源传到 A 节点,调度器却在 B 节点拉起任务,缺文件只能手动再传一遍。DolphinScheduler 的资源中心(resource center)支持把存储后端切成 S3,每个上传的文件都成为桶里 dolphinscheduler/<tenant>/resources/ 之下的对象。用 RustFS 当这个 S3 后端,资源文件就脱离了单台机器的磁盘,worker 节点执行任务时统一从桶里读,扩节点不再担心文件失踪。本文用官方验证过的组合把链路跑通:DolphinScheduler 3.2.1 对 rustfs/rustfs-x86-musl:v2.3.1。
资源中心为什么要换存储后端
资源中心是 DolphinScheduler 存放工作流附件的地方,分两类:一类是用户在 UI 上传的脚本、JAR、UDF 等文件,另一类是任务执行时临时落盘的中间产物。默认后端是 LOCAL,也就是各节点自己的磁盘。LOCAL 模式下,master 把资源元信息记进数据库,但实际文件躺在调用节点的本地目录里,其他 worker 想用就得先把文件同步过去,靠的是服务器之间的 ssh 拷贝。集群规模一大,这套本地同步就成了故障温床:节点宕机、磁盘满、rsync 失败,任何一个都会让任务找不到资源。
切到 S3 之后,逻辑变了。用户上传的文件直接写进对象存储,元数据里记的是对象路径,worker 执行任务时从同一个桶里按需拉取,不再有节点之间的文件拷贝。好处是资源和工作流定义解耦,扩节点只是加机器,不需要迁移任何本地文件;代价是 worker 必须能稳定访问这个桶,所以后端对象存储本身的可用性和带宽就成了关键路径。
先起 standalone 服务器
官方指南用 standalone 镜像做本地集成验证,它单容器捆绑了 master、worker、API、alert 和内置 ZooKeeper,省去组件间联网的麻烦:
docker run -d --name dolphinscheduler --hostname dolphinscheduler \
--network oo-rustfs_default -p 12345:12345 \
apache/dolphinscheduler-standalone-server:3.2.1
Web UI 在 http://localhost:12345/dolphinscheduler/ui,默认登录 admin / dolphinscheduler123。这个镜像里已经带着 RustFS 的 oo-rustfs_default 网络,所以容器之间能直接用服务名互通,本地跑通不需要额外配网。
把 S3 属性追加进 common.properties
DolphinScheduler 的存储后端配置在 /opt/dolphinscheduler/conf/common.properties。官方指南特别强调:把 S3 属性追加 到现有文件末尾,而不是整文件替换。这个文件里还堆着大量其他设置,比如 HDFS、Azure 的开关和各类超时参数,整替换会把 resource.storage.type 之外的内容全部清掉,残留的旧配置还可能把后端指回 Azure。
docker exec dolphinscheduler bash -c "cat >> /opt/dolphinscheduler/conf/common.properties << 'EOF'
resource.storage.type=S3
resource.storage.base.dir=/ds-resources
resource.aws.s3.bucket.name=ds-demo
resource.aws.s3.endpoint=http://<your-rustfs-endpoint>:9000
resource.aws.access.key.id=<your-access-key>
resource.aws.secret.access.key=<your-secret-key>
resource.aws.region=us-east-1
EOF"
docker restart dolphinscheduler
resource.storage.type=S3 这一行是总开关,写丢它就会回退到默认后端。resource.aws.s3.bucket.name 是桶名,resource.aws.s3.endpoint 填 RustFS 的地址加 9000 端口,resource.aws.region 在 S3 兼容场景下可以随便填一个值,RustFS 不靠它路由。docker restart 之后等 API 恢复(约一分钟),再用 RustFS 的 rc CLI(原生命令行工具)把桶建好:
rc mb rustfs/ds-demo
桶建好之后,DolphinScheduler 启动时不会自动帮你建桶,所以这一步不能省,否则上传会直接报 NoSuchBucket。
经 API 上传一个资源文件
DolphinScheduler 的资源创建端点,在 file、type、currentDir 之外,还要求 name 和 fullName 两个表单字段,缺哪个都会报 Required request parameter。先登录拿 session id:
printf "ds resource file stored in rustfs" > /tmp/ds-file.txt
TOKEN=$(curl -s -m 10 -X POST http://localhost:12345/dolphinscheduler/login \
-d "userName=admin&userPassword=dolphinscheduler123" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['data']['sessionId'])")
curl -s -m 30 -X POST "http://localhost:12345/dolphinscheduler/resources" \
-H "session-id: $TOKEN" -H "Cookie: sessionId=$TOKEN" \
-F "file=@/tmp/ds-file.txt" -F "type=FILE" -F "currentDir=/" \
-F "name=ds-file.txt" -F "fullName=/ds-file.txt" -F "description=demo"
返回 {"code":0,"msg":"success",...} 就说明上传成功。name 是显示名,fullName 是带前导斜杠的对象路径,两者不一致会让 UI 显示和实际落点错位,建议保持同名同路径。
双向验证:桶里和 API 都要对得上
上传成功只说明 API 收下了文件,还得确认它真的进了 RustFS 桶。用 rc 列一下:
rc ls rustfs/ds-demo/ -r
会看到文件落在租户的资源前缀之下,同时还有一个空的 udfs 前缀:
dolphinscheduler/default/resources/ds-file.txt
dolphinscheduler/default/udfs/
dolphinscheduler/default/ 是 DolphinScheduler 按租户自动生成的根前缀,default 是默认租户名,resources/ 下面放用户上传的文件,udfs/ 下面放注册过的 UDF 函数。S3 后端的对象键布局由 DolphinScheduler 的租户结构决定,跟 common.properties 里那个 resource.storage.base.dir 不是一回事,后者是 LOCAL 模式的本地基目录,切到 S3 后基本不参与路径计算。
再从 API 侧读回内容,确认落进桶里的就是原文件:
curl -s -m 30 "http://localhost:12345/dolphinscheduler/resources/view-ui?fullName=/ds-file.txt&skipLineNum=100&limit=100" \
-H "session-id: $TOKEN" -H "Cookie: sessionId=$TOKEN" | grep "ds resource"
输出 ds resource file stored in rustfs 就说明内容和上传时一致。两端都对得上,资源中心到 RustFS 的链路才算真正打通。

三个排查点
一是服务器启动时报 Azure 的 clientId/tenantId/clientSecret 错误,几乎都是 common.properties 被整文件替换、resource.storage.type=S3 丢了,后端回退到 Azure 才会去要那三个参数。按上面的追加方式重来即可,别碰文件里已有的其他段落。
二是 API 返回 Required request parameter 'name'/'fullName' is not present。资源创建端点明确要求这两个字段,很多老教程只传 file、type、currentDir,在 3.2.1 上会直接拒绝,把 name 和 fullName 都带上就行。
三是 API 对 token 调用返回 405。登录和 token 端点都接受 POST,但不同版本里 /api/v2/token 的路径有差异,踩坑时改用上面的登录表单拿 sessionId,并在每次调用都带上 session-id 头与 Cookie: sessionId=...,会话期间复用同一个 token。
上线前确认边界
这套部署官方标注为本地集成测试,不是生产配置。落到生产要补几件事:给 DolphinScheduler 单独建一组访问密钥,通过 RustFS 内置的 IAM(身份与访问管理)把权限收窄到资源桶,不要拿管理员密钥直接挂;多实例的 worker 执行任务时都走同一套 S3 资源中心,所以确认所有 worker 都能访问这个桶,网络层面别漏掉任何节点;另外 standalone 单容器只是演示,生产建议走集群模式,把 master、worker、API 分开部署,再各自指向同一个 RustFS 桶。
跑完想清掉环境,停止容器并清空桶:
docker rm -f dolphinscheduler
rc rm rustfs/ds-demo/ --recursive --force
当前 RustFS 稳定版是 1.0.5(2026-11-10 发布),对接命令以官方集成指南为准(docs.rustfs.com/zh/developer/integration/big-data/dolphinscheduler)。把资源中心接进 RustFS,资源文件就脱离了单台机器的磁盘,集群扩节点、换机器都不再担心文件失踪,运维只管加机器,不用再管谁的资源在哪个盘上。
