工作流脚本和依赖 JAR 散在各节点本地盘:用 DolphinScheduler 资源中心接到 RustFS

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,资源文件就脱离了单台机器的磁盘,集群扩节点、换机器都不再担心文件失踪,运维只管加机器,不用再管谁的资源在哪个盘上。

相关推荐
Eyfcom41 分钟前
〖物流云原生实战 01〗K8s Operator 在物流定时任务中的实践:从“裸 CronJob”到“声明式任务编排”
云原生·物流管理系统·物流云原生实战
冬奇Lab9 小时前
一天一个开源项目(第259篇):Modern Web Guidance —— Chrome 团队给 AI 编码 Agent 的「现代 Web 最佳实践」外挂
人工智能·开源
阿里云云原生14 小时前
阿里云智能体白皮书开源进展
云原生
阿里云云原生15 小时前
万联智策基于 AgentCore 构建数字员工团队,800+智能体规模化落地
云原生
孟健16 小时前
我用 REA 逆向自己的 Electron 桌面端,扒出 178 个 IPC 通道后我反而放心了
架构·开源·ai编程
分布式存储与RustFS16 小时前
群晖 NAS 被 C3RB3R 锁了:本地备份一起被加密,RustFS 对象锁为什么删不掉
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
真好啊又活了一天17 小时前
服务器和虚机配置IPV6
运维·服务器·github
zixu_star17 小时前
定时任务是什么?从日常自动化到 AI 应用,软件如何自动执行任务?
运维·人工智能·自动化
Cosolar17 小时前
极简简历:开源免费,希望帮助到更多求职者
开源·github·敏捷开发