☰
K8s离线环境部署DolphinScheduler+SeaTunnel完整指南
2026/10/22 21:23:47 网站建设 项目流程

k8s离线环境部署DolphinScheduler+SeaTunnel,这个组合我前前后后折腾了一个多月,踩了不少坑,也沉淀了不少经验。先说清楚这套东西是干嘛的:DolphinScheduler做任务编排调度,SeaTunnel做数据同步抽取,两者搭在一起,就是一个完整的、可视化的数据集成调度平台。很多公司内部环境是隔离的,没有外网,这就逼着你必须走离线部署这条路。这篇文章就是把我在离线环境里从零到一部署这套组合的完整过程、关键配置和排坑记录都写出来,给同样被困在内网的同学一个参考。

1. 整体方案设计与前置准备

1.1 “离线”到底离到什么程度

做离线部署,第一件事不是下载安装包,而是搞清楚“离线”的边界。我这次的环境是:k8s集群本身是好的,但是所有节点都不能访问外网,内部有一个私有的镜像仓库,可以接收我上传的镜像。软件包、依赖、镜像,全部要靠u盘或者内部文件服务器搬运进去。

这里有个很容易忽略的问题:离线不代表完全不让动网络,而是分三种情况:

  • 纯离线:所有节点无外网,内部有私有镜像仓库和yum源
  • 半离线:部分节点有外网,可以下载,但生产环境隔离
  • 仅k8s离线:应用层完全隔离,但镜像仓库、对象存储等都是内网服务

确认清楚边界再动手,不然方案设计会跑偏。我这次属于第一种,纯离线,所有安装包都得靠自己的“人肉快递”。

先列一下我这次环境的基本信息:

  • k8s版本:1.24.x
  • 存储方案:NFS作为默认StorageClass
  • 镜像仓库:Harbor私有仓库
  • DolphinScheduler版本:3.1.9
  • SeaTunnel版本:2.3.5
  • 数据库:使用已有的MySQL 8.0

为啥选这两个版本?DolphinScheduler 3.x和SeaTunnel 2.3.x是可以稳定兼容的,SeaTunnel 2.3.5支持MySQL CDC,和DolphinScheduler的Workflow配合度高,而且这两个版本都有现成的Docker镜像可以直接拿来改造。

1.2 为什么选择DolphinScheduler + SeaTunnel这套组合

市面上的调度和数据同步工具不少,比如DolphinScheduler对标的还有Apache Airflow,SeaTunnel对标的还有DataX、Flink CDC,为啥我偏偏选了这两个?

DolphinScheduler的优势在于:

  • 自带工作流可视化编排界面,业务人员也能看懂依赖关系
  • 支持定时调度、依赖触发、失败重跑,这些是生产级调度平台的基本盘
  • 部署方式灵活,可以源码部署也可以k8s部署

SeaTunnel的优势在于:

  • 不用写代码,通过配置文件就能定义数据源和同步逻辑
  • 支持MySQL、PostgreSQL、Oracle、ClickHouse、Hive、Kafka等几十种数据源
  • 基于Apache Zeta引擎,批流一体,性能比第一代版本有了明显提升

这两个组合起来,DolphinScheduler负责“什么时候干什么活”,SeaTunnel负责“怎么把数据从A挪到B”,边界清晰,互不干扰,比在一个系统里强行揉在一起要好维护得多。

1.3 离线安装包清单

这一步非常关键,做少了后面就得反复折腾,做多了u盘装不下。我列一下我整理的清单:

  • DolphinScheduler镜像:apache/dolphinscheduler:3.1.9及相关组件镜像
  • SeaTunnel镜像:apache/seatunnel:2.3.5,以及seatunnel-web镜像
  • SeaTunnel连接器包:seatunnel-connectors.tar.gz,包含MySQL、JDBC、Kafka、Hive等常用连接器
  • DolphinScheduler源码包:apache-dolphinscheduler-3.1.9-src.tar.gz,用于生成初始化SQL和修改配置
  • SeaTunnel客户端包:apache-seatunnel-2.3.5-bin.tar.gz
  • 基础依赖:JDK 1.8,MySQL驱动jar包
  • 网络调优工具包:比如iproute2、telnet等排查工具,离线环境装这些很麻烦,提前备好

镜像怎么拿?有外网的时候提前pull下来,然后通过docker save打成tar包,到内网后用docker load加载,再retag到私有仓库。如果是arm架构的机器,还需要注意镜像的架构匹配,这个很多人容易踩坑,后面我会专门说。

2. 核心原理与架构拆解

2.1 DolphinScheduler在k8s中的部署逻辑

DolphinScheduler在k8s里跑,和传统的主机部署有本质的区别。它在主机上部署时,会拆成MasterServer、WorkerServer、ApiServer、AlertServer、LoggerServer这几个进程,各司其职:

  • MasterServer:负责任务的调度、DAG切分、任务分发
  • WorkerServer:负责实际执行任务,可以水平扩展
  • ApiServer:提供Rest API,给前端页面调用
  • AlertServer:负责告警通知
  • LoggerServer:负责日志服务

在k8s上部署时,这些进程变成了一个个Deployment或者StatefulSet,但逻辑架构不变。DolphinScheduler官方提供了Helm Chart,所以最稳妥的方式是用Helm部署,而不是自己手写一堆yaml。Helm Chart里已经把Master、Worker、Api、Alert拆成了不同的组件,并且支持配置副本数、资源配额、持久化存储、环境变量。

Master和Worker在k8s里的区别还在于:Master需要的是稳定的状态,不适合随便重启,建议用Deployment配合反亲和性,保证Master实例分散在不同节点上;Worker是可以随时弹的,业务负载大就多拉几个Worker实例。

2.2 SeaTunnel在k8s中的运行方式

SeaTunnel本身设计成独立运行的引擎,它不感知k8s,所以要在k8s环境里跑起来,有几种主流方式:

第一种是直接用SeaTunnel镜像启动一个Pod,Pod里通过命令行执行配置好的同步作业,这是最简单的方式,适合一次性的数据同步任务。

第二种是把SeaTunnel整合到DolphinScheduler对应的Worker中,通过DolphinScheduler的Shell任务类型去调用Seatunnel的提交命令。这种方式适合把同步任务纳入DolphinScheduler的调度体系,形成统一的“调度+数据同步”流程。

第三种是SeaTunnel Zeta引擎的Cluster模式,作为一个常驻的分布式服务运行在k8s中,Job通过Client发送给Cluster执行。

我这次采用的是第二种方式。原因很简单:通过Shell调用SeaTunnel客户端,虽然看起来有点“土”,但胜在稳定可控。DolphinScheduler负责重试、告警、定时,SeaTunnel负责真正的数据搬运,出了问题边界清楚。第一种方式脱离调度体系,生产上没法用;第三种方式架构更复杂,对内网环境来说需要维护Zeta集群本身的健康状态,反而增加负担。

2.3 数据同步任务的执行链路

搞清一次数据同步任务从触发到落地的完整链路,对排查问题非常有帮助。任务在DolphinScheduler里被触发后,链路是这样的:

DolphinScheduler Master根据DAG分发的状态,把任务发给对应的Worker节点,Worker节点会找到配好的SeaTunnel客户端路径,执行提交命令,SeaTunnel启动Zeta引擎(或者连接已运行的引擎),读取配置文件,初始化Source、Transform、Sink三个阶段的插件,然后开始拉数据、处理数据、写目标库,最后返回执行状态给DolphinScheduler Worker。

这里最常出问题的点有两个:

  • Worker找不到SeaTunnel客户端路径,或者环境变量没配置好
  • SeaTunnel配置文件里的数据库连接串、账号密码写得有问题

链路长,排错就要一层一层来,先把DolphinScheduler的日志看透,再到SeaTunnel的日志里去找真正的报错原因。

3. 离线部署实操全流程

3.1 准备离线仓库

这一步是整个离线部署的“地基”。我用的方案是先把Harbor装在一台可以访问外网的机器上(这台机器后面再搬到内网),或者直接在已经有Harbor的内网环境里,通过跳板机把镜像传进去。

具体步骤如下:

  1. 在能联网的机器上,用docker pull拉取需要的官方镜像
  2. 用docker save把镜像打成tar包,最好是gzip压缩,体积更小
  3. 把tar包拷贝到内网跳板机
  4. 在内网跳板机上用docker load把镜像加载进来
  5. 用docker tag打上内网Harbor的地址标签
  6. 用docker push推送到内网Harbor
  7. 所有k8s节点配置/etc/docker/daemon.json,把Harbor地址加入insecure-registries

注意一下:很多人在第4步docker load之后,直接忘了打tag,导致推送的时候就镜像名还是原来的docker.io/apache/dolphinscheduler:3.1.9,不是harbor地址/项目名/镜像名,这样推送必然失败。这个细节值得留意。

3.2 初始化MySQL数据库

DolphinScheduler使用关系型数据库存储元数据,包括工作流定义、任务实例、调度计划等。首次部署必须执行初始化脚本。

我从源码包里找到了dolphinscheduler_mysql.sql这个初始化脚本。脚本执行前需要先建库:

CREATE DATABASE dolphinscheduler DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON dolphinscheduler.* TO 'dolphinscheduler'@'%' IDENTIFIED BY '你的密码'; FLUSH PRIVILEGES;

执行初始化脚本:

mysql -h mysql主机地址 -u dolphinscheduler -p 你的密码 < dolphinscheduler_mysql.sql

需要注意,如果MySQL的sql_mode设置得比较严格,比如开启了ONLY_FULL_GROUP_BY,初始化脚本可能报错。我遇到过一次,后来把sql_mode调整了一下,去掉ONLY_FULL_GROUP_BY,重新导入就好了。这个属于MySQL本身的兼容问题,不是DolphinScheduler的bug。

3.3 修改Helm Chart的values.yaml

Helm Chart是部署的“说明书”,values.yaml则是这份说明书的“可调参数页”。这一步我花了不少时间,因为官方默认的values.yaml是按在线环境写的,里面很多镜像地址默认指向docker.io,必须全部改掉。

我挑几个关键配置项说一下:

# 镜像仓库地址 image: registry: harbor.internal.com tag: 3.1.9 # 数据库连接配置 common: database: host: mysql.internal.com port: "3306" user: dolphinscheduler password: "我的密码" database: dolphinscheduler # Worker副本数 worker: replicas: 2 resources: limits: cpu: 2 memory: 4Gi requests: cpu: 500m memory: 2Gi

根据我自己的经验,这里有三个地方非常容易出错:

一是数据库的时区配置。DolphinScheduler默认用的是UTC时区,如果MySQL也是UTC,没问题,但如果你MySQL用的是CST(中国标准时间,其实这里是UTC+8),就得在jdbc连接串里加上serverTimezone=Asia/Shanghai,否则调度时间会和预计时间差8个小时,这个坑非常隐蔽。

二是Worker的存储配置。Worker在跑任务时会产生日志和临时文件,默认是挂在容器的临时目录里,Pod一重启数据就没了。日志丢了还能忍,但如果SeaTunnel的配置文件和临时下载的连接器也放在临时目录里,Pod重建后任务必然失败。所以需要把日志目录、临时目录都挂到持久化存储上。

三是资源配额。官方默认的requests很低,但如果你把requests设得太高,k8s调度时会因为节点资源不足而pending。我一开始没经验,把Worker的requests设成了2C/4G,结果两个Worker pod起不来,一直Pending。后来改成500m/2G,调度就正常了。requests和limits要拉开差距,requests是调度依据,limits是资源上限。

3.4 使用Helm部署DolphinScheduler

改完values.yaml就可以正式部署了。如果你在内网环境没有Helm仓库,需要先把Helm Chart包下载好,放到内网中。具体操作:

# 在有外网的机器上 helm repo add dolphin https://dolphinscheduler.apache.org/helm-chart/ helm pull dolphin/dolphinscheduler --version 3.1.9

拉下来的chart包拷贝到内网后,直接使用:

helm install dolphinscheduler ./dolphinscheduler-3.1.9.tgz -f values.yaml -n>kubectl get pods -n>kubectl describe pod xxx -n>export SEATUNNEL_HOME=/opt/seatunnel export PATH=$PATH:/opt/seatunnel/bin

为了让这个环境变量自动生效,我在Worker容器的启动脚本里加了对应的source语句,或者在DolphinScheduler的Workflow里直接用绝对路径调用。用绝对路径更稳,因为不同Pod的环境变量初始化时机不同,有时候会因为找不到变量导致任务执行失败。

3.6 打通DolphinScheduler和SeaTunnel的调用

在DolphinScheduler中新建一个工作流,添加一个Shell任务,内容写:

/opt/seatunnel/bin/seatunnel.sh --config /opt/seatunnel/config/mysql_to_mysql.conf -e local

这个mysql_to_mysql.conf就是我们的数据同步作业配置。为了方便管理,我把所有SeaTunnel的配置文件也放到了共享存储里,DolphinScheduler通过Shell任务直接读取这些配置文件。

为了让整个流程更“DolphinScheduler化”,我同时也做了几件事:

  • 在DolphinScheduler里配置了告警组,任务失败时发钉钉或者邮件通知
  • 设置了任务超时时间,防止数据量大的同步任务卡死
  • 配置了失败重试次数,解决临时性的网络抖动问题

这样下来,整个数据平台就是真正“活”的调度系统了,定时、触发、重试、告警,全都在DolphinScheduler面面俱到。

4. 常见问题与排查技巧实录

4.1 镜像拉取失败

离线环境最常见的就是镜像拉取失败,报错信息通常是image pull back off。先排除一下是哪个节点在拉镜像,然后手动在那台节点上执行docker pull试试,看能不能拉到。如果手动拉取没问题,说明是k8s的安全策略问题,检查daemon.json里的insecure-registries是否配置正确,然后重启docker服务。

还有一个小坑:Harbor里配置的项目如果是私有的,k8s节点需要配置imagePullSecret,否则同样拉不下来。在Helm的values.yaml里,有一个imagePullSecrets配置项,要填上对应的secret名字。

4.2 MySQL连接失败

这个报错在各阶段都遇到过,表现形式五花八门:

  • DolphinScheduler初始化时报Exception connecting to database
  • Pod启动时报Connection refused
  • 任务执行时报Communications link failure

排查思路是:先从DolphinScheduler的Pod里用telnet测一下数据库端口通不通,然后看数据库账号密码是否正确,再看MySQL是否允许远程连接。内网环境还有个很隐蔽的问题:MySQL的bind-address如果配置成了127.0.0.1,那其他机器肯定连不上,必须改成0.0.0.0。

4.3 时区错乱

我刚才提过时区问题,这里详细展开。如果你发现DolphinScheduler里设置的定时调度时间和实际执行时间差8个小时,基本就是时区不对。

解决方法就是在values.yaml里数据库连接串加serverTimezone=Asia/Shanghai,同时DolphinScheduler本身的时区也要设置成Asia/Shanghai。有的老版本问题更多,需要在启动参数里加-Duser.timezone=GMT+8,好在新版本里已经默认处理好了。

4.4 SeaTunnel任务执行后无响应

SeaTunnel任务在DolphinScheduler里显示运行中,但一直不结束,这种情况多半是SeaTunnel的Zeta引擎启动有问题,或者配置里的任务名称和实际提交任务不一致。

我在排查时用的方法是:先到Worker节点上手动执行一下同样的命令,看看能不能正常跑完,如果可以,说明问题出在DolphinScheduler和SeaTunnel的交互上,比如环境变量、用户权限;如果手动也卡住,就把SeaTunnel的日志级别调到DEBUG,看具体卡在哪个环节。

调试日志通常在SeaTunnel的logs目录下,文件名类似seatunnel-engine-xxx.log。看到卡在Source阶段,一般是数据库连接问题;卡在Sink阶段,一般是写入目标库有问题;卡在Transform阶段,就要看看是不是字段类型转换出错了。

4.5 ARM架构镜像问题

公司内部可能有arm架构的服务器,如果你拿的是x86架构的镜像,Pod会一直CrashLoopBackOff,日志里报exec format error。这个报错很直接:我的AppArmor,哦不是,是CPU架构不匹配。

解决方法有两种:

  • 在联网机器上docker pull时,用--platform指定arm64
  • 用manifest工具把多架构镜像打包成fat manifest,这样k8s会根据节点架构自动选择对应架构的镜像

第二种方案更通用,因为如果你部署的集群是混合架构,x86和arm节点都有,用fat manifest就不会出现“镜像只支持一种架构”的尴尬。

4.6 共享存储权限问题

SeaTunnel放在NFS共享存储里,如果NFS的权限设置不对,Worker容器没有读写权限,执行任务时就会报Permission denied。

这个问题排查起来也简单:

  • 查看NFS目录的属主和属组
  • 确认Worker容器运行的用户ID
  • 将NFS目录的属主调整为该用户ID

但这里有个麻烦:DolphinScheduler官方镜像里的Worker进程默认是用uid 1000跑的,而NFS的root_squash机制可能不认这个uid。我是直接把NFS目录的属主改成了uid 1000,再配合mount option里加入no_root_squash,搞定。这种底层文件系统权限的坑,不遇到一次很难想到。

5. 配置最佳实践与调优建议

5.1 DolphinScheduler调度参数调优

DolphinScheduler的Master和Worker线程数、心跳超时、任务重试机制等配置,在生产环境需要结合实际并发量来调。

我总结的调优思路如下:

  • 任务量多,并发高,加大worker.execution.threads,同时提高Worker的内存限制
  • 如果Master频繁失联,调大master.heartbeat.interval和监听端口超时时间
  • 失败重试次数设置:普通离线任务设为2-3次,实时性要求高的任务设为0次,因为重试只会让数据更乱

对于调度,我一般会设置一个“重跑保护”:工作日跑批的任务,失败重试2次,如果还失败,就不自动重跑,而是发告警出来人工介入。这样避免出现“任务失败然后自动重跑,重跑又占用资源导致其他任务失败”的连锁雪崩,这个在大数据平台里特别容易发生。

5.2 SeaTunnel资源并发度设置

SeaTunnel的配置文件里有一个execution.parallelism参数,直接影响同步任务的并发度。并行度并不是越大越好,因为目标数据库的写入能力是有限的。如果你并行度设为10,但目标MySQL只能承受5个并发写入,那么性能和稳定性都会出问题。

我这里的一个推荐做法是:先按同步数据量的复杂度估算,初始并行度设为2或3,然后通过任务运行时间对比,逐步往上加,直到找到一个稳定的并发上限。你还得注意source端的连接池,SeaTunnel的MySQL Source会创建多个连接,连接数不要超过数据库max_connections的一半,不然会影响其他业务。

5.3 数据一致性保障

离线同步最怕的是数据重复或者数据丢失。SeaTunnel的JDBC Sink支持写入模式:append、upsert和delete+insert。业务上如果要求数据不重复,一定要用upsert模式,并且指定主键字段。如果目标表没有主键,建议加一个自增主键或者业务主键,否则同步数据出现问题后,很难做幂等恢复。

我自己在跑订单表同步时,就是用了upsert模式,source端是MySQL的CDC,sink端还是MySQL,主键设为订单号。就算同一个订单被更新了多次,同步结果也是最终一致的,不会出现重复数据。这一个坑我踩得很深,最开始用的append模式,结果源库update一条订单,目标库多出一行,统计报表全错了。

5.4 监控与告警配置

一个数据平台如果只有调度没有监控,那就是在裸奔。DolphinScheduler自带的告警机制已经很完善了,支持邮件、钉钉、企业微信等。我建议配置两套告警:

  • 任务级告警:每个关键工作流都绑一个告警组,任务失败、超时都要通知
  • 全局告警:DolphinScheduler本身的服务状态异常,也要有Master、ApiServer的存活探针,配合k8s自身的Prometheus监控

我实际的经验是:任务级告警里,邮件和钉钉都发,邮件是给领导看的日报,钉钉是给开发实时看的。这样生产有问题,开发能第一时间感知,日报也能留档。

6. 扩展思考与二次开发建议

6.1 如何对接更多数据源

SeaTunnel的连接器本身已经覆盖了很多主流数据源,但在实际工作中,总会遇到一些“有点冷门”的数据源,或者需要对接内部自研系统的情况。

我的建议是:先看SeaTunnel连接器列表里有没有对应的连接器;没有的话,考虑是否可以用JDBC通用连接器加自定义SQL解决;还不行,就需要基于SeaTunnel的API做二次开发,写自己的Source或者Sink插件。SeaTunnel的插件机制设计得还是很清晰的,写一个自定义插件,本质上就两件事:实现接口,打jar包放到connectors目录。做一次之后,后面扩展就容易了。

6.2 从离线到云原生的演进路径

DolphinScheduler和SeaTunnel这套组合,跑在k8s上,其实已经算是云原生架构了。但你可以做得更进一步:

  • 通过KEDA或HPA,让SeaTunnel的Worker数量根据同步任务的积压量自动伸缩
  • 用Argo Workouts或者GitOps的方式管理DolphinScheduler的工作流定义
  • 考虑到DolphinScheduler API Server已经是无状态的,直接水平扩展即可

这些扩展方向,内网环境也是完全可以推进的,只要前期把基础镜像和依赖下载好,后续滚动升级的风险总体可控。

6.3 团队协作规范

数据平台的稳定运行,靠的不仅仅是部署,还有日常的规范。我和团队后来约定了几条不占纸面的“规矩”:

  • SeaTunnel配置文件的命名统一:数据源_目标源_业务名称.conf,比如mysql_mysql_order.conf
  • 同步作业提交时,必须写清owner和告警组
  • DolphinScheduler工作流命名按业务域隔离,前缀统一,比如ods_、dwd_、ads_
  • 每次修改SeaTunnel配置,必须先在测试环境跑一次,确认没问题才能上生产

这些规矩能避免很多低级失误。特别是在多人协作的时候,一个命名不规范的配置文件,正好又在你凌晨排查问题时出现,那真的会让人血压升高。

6.4 离线部署包版本管理

最后一件事,建议把部署包和配置文件纳入版本管理,用Git维护。很多人觉得内网环境不需要Git,但问题恰恰出在这里:如果哪天同事改了values.yaml,或者换了SeaTunnel连接器包,你根本不知道改了什么,排错的时候非常被动。

我把部署相关的文件全部放在一个自建的Git仓库里,包括:

  • Helm Chart的values.yaml
  • SeaTunnel配置文件
  • 初始化SQL脚本
  • 部署操作文档

每次改动都有记录,回滚非常方便。配合一个简单的发布检查单(先计划、再变更、后验证),整个数据平台的迭代会轻松非常多。

7. 最后的小建议

这套组合在内网环境里跑稳定之后,其实还可以多做一些事情。比如把数据同步任务的执行数据收集起来,做一个简单的数据血缘页面,让业务看到数据从哪来到哪去。或者把SeaTunnel的同步任务做成标准的模板,业务需求来了,直接填参数就能生成配置,不用每次从头写。

我个人的体会是:离线部署最大的难点从来不在技术本身,而在于环境的不确定性。同一套镜像,在不同节点组上拉取结果可能不同;同一个脚本,在不同库版本上行为也可能不同。所以关键的配置和操作,一定要记录下来,形成一个团队内部的“离线部署手册”,后续再有人接手就不用重新踩一遍坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询