K8s离线环境部署DolphinScheduler+SeaTunnel完整指南
k8s离线环境部署DolphinSchedulerSeaTunnel这个组合我前前后后折腾了一个多月踩了不少坑也沉淀了不少经验。先说清楚这套东西是干嘛的DolphinScheduler做任务编排调度SeaTunnel做数据同步抽取两者搭在一起就是一个完整的、可视化的数据集成调度平台。很多公司内部环境是隔离的没有外网这就逼着你必须走离线部署这条路。这篇文章就是把我在离线环境里从零到一部署这套组合的完整过程、关键配置和排坑记录都写出来给同样被困在内网的同学一个参考。1. 整体方案设计与前置准备1.1 “离线”到底离到什么程度做离线部署第一件事不是下载安装包而是搞清楚“离线”的边界。我这次的环境是k8s集群本身是好的但是所有节点都不能访问外网内部有一个私有的镜像仓库可以接收我上传的镜像。软件包、依赖、镜像全部要靠u盘或者内部文件服务器搬运进去。这里有个很容易忽略的问题离线不代表完全不让动网络而是分三种情况纯离线所有节点无外网内部有私有镜像仓库和yum源半离线部分节点有外网可以下载但生产环境隔离仅k8s离线应用层完全隔离但镜像仓库、对象存储等都是内网服务确认清楚边界再动手不然方案设计会跑偏。我这次属于第一种纯离线所有安装包都得靠自己的“人肉快递”。先列一下我这次环境的基本信息k8s版本1.24.x存储方案NFS作为默认StorageClass镜像仓库Harbor私有仓库DolphinScheduler版本3.1.9SeaTunnel版本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 AirflowSeaTunnel对标的还有DataX、Flink CDC为啥我偏偏选了这两个DolphinScheduler的优势在于自带工作流可视化编排界面业务人员也能看懂依赖关系支持定时调度、依赖触发、失败重跑这些是生产级调度平台的基本盘部署方式灵活可以源码部署也可以k8s部署SeaTunnel的优势在于不用写代码通过配置文件就能定义数据源和同步逻辑支持MySQL、PostgreSQL、Oracle、ClickHouse、Hive、Kafka等几十种数据源基于Apache Zeta引擎批流一体性能比第一代版本有了明显提升这两个组合起来DolphinScheduler负责“什么时候干什么活”SeaTunnel负责“怎么把数据从A挪到B”边界清晰互不干扰比在一个系统里强行揉在一起要好维护得多。1.3 离线安装包清单这一步非常关键做少了后面就得反复折腾做多了u盘装不下。我列一下我整理的清单DolphinScheduler镜像apache/dolphinscheduler3.1.9及相关组件镜像SeaTunnel镜像apache/seatunnel2.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.8MySQL驱动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镜像启动一个PodPod里通过命令行执行配置好的同步作业这是最简单的方式适合一次性的数据同步任务。第二种是把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的内网环境里通过跳板机把镜像传进去。具体步骤如下在能联网的机器上用docker pull拉取需要的官方镜像用docker save把镜像打成tar包最好是gzip压缩体积更小把tar包拷贝到内网跳板机在内网跳板机上用docker load把镜像加载进来用docker tag打上内网Harbor的地址标签用docker push推送到内网Harbor所有k8s节点配置/etc/docker/daemon.json把Harbor地址加入insecure-registries注意一下很多人在第4步docker load之后直接忘了打tag导致推送的时候就镜像名还是原来的docker.io/apache/dolphinscheduler3.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.yamlHelm 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中国标准时间其实这里是UTC8就得在jdbc连接串里加上serverTimezoneAsia/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 databasePod启动时报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里数据库连接串加serverTimezoneAsia/Shanghai同时DolphinScheduler本身的时区也要设置成Asia/Shanghai。有的老版本问题更多需要在启动参数里加-Duser.timezoneGMT8好在新版本里已经默认处理好了。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和deleteinsert。业务上如果要求数据不重复一定要用upsert模式并且指定主键字段。如果目标表没有主键建议加一个自增主键或者业务主键否则同步数据出现问题后很难做幂等恢复。我自己在跑订单表同步时就是用了upsert模式source端是MySQL的CDCsink端还是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.yamlSeaTunnel配置文件初始化SQL脚本部署操作文档每次改动都有记录回滚非常方便。配合一个简单的发布检查单先计划、再变更、后验证整个数据平台的迭代会轻松非常多。7. 最后的小建议这套组合在内网环境里跑稳定之后其实还可以多做一些事情。比如把数据同步任务的执行数据收集起来做一个简单的数据血缘页面让业务看到数据从哪来到哪去。或者把SeaTunnel的同步任务做成标准的模板业务需求来了直接填参数就能生成配置不用每次从头写。我个人的体会是离线部署最大的难点从来不在技术本身而在于环境的不确定性。同一套镜像在不同节点组上拉取结果可能不同同一个脚本在不同库版本上行为也可能不同。所以关键的配置和操作一定要记录下来形成一个团队内部的“离线部署手册”后续再有人接手就不用重新踩一遍坑。