ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

K8s 离线部署 DolphinScheduler 与 SeaTunnel 全流程实战

K8s 离线部署 DolphinScheduler 与 SeaTunnel 全流程实战

做数据平台的同学应该都有过这种经历:生产环境在一个完全隔离的网段里,外部网络连不进去,机器上连个镜像都拉不了,一行docker pull都会卡半天直到超时。这时候如果还要在 k8s 上部署 DolphinScheduler 和 SeaTunnel 这套“调度+数据集成”的组合,很多标配操作就全都失效了,Helm 在线仓库拉不下来、镜像仓库连不上、初始化脚本没网络跑不了,每一步都像是戴着镣铐跳舞。

我最近正好在某个客户的离线项目里把这一整套流程完整走了一遍,从物料准备、镜像搬运、Helm 离线安装,到 DolphinScheduler 接入 SeaTunnel、实际跑通一条数据同步任务,前前后后踩了不少坑,也沉淀了一些可以复用的经验。这篇文章就把整个离线的部署链路、关键配置和排查思路整理出来,给后面要搞同样架构的同学留一份参考。适合正要接手离线集群、或者已经把 DS 装好但不知道怎么和 SeaTunnel 打通的人看。

1. 离线部署的整体思路与架构取舍

1.1 先理清 DolphinScheduler 和 SeaTunnel 各自承担什么角色

很多人上来就装,装完才发现这两个东西的关系没搞清楚,后面配置全乱了。简单说,DolphinScheduler 是总调度,负责编排工作流、管理任务实例、展示 DAG、做告警;SeaTunnel 是真正的数据搬运工,负责把源端的数据拉出来、做必要的转换、再写到目标端。DS 通过自带的 SeaTunnel 任务节点,把作业脚本提交给 SeaTunnel 的常驻集群去执行。打个比方,DS 是工厂里的生产计划科,SeaTunnel 是厂区里的运输队,计划科把运输单子派给运输队,运输队才真正开着车把货从 A 仓拉到 B 仓。

正因为这个依赖关系,部署顺序应该是先部署一个能用的 DolphinScheduler,再让 SeaTunnel 集群就位,最后在 DS 里配置任务节点去对接 SeaTunnel。如果反过来,你先装 SeaTunnel 再装 DS,中间还得反复回头确认 IP、端口、服务名,很容易搞乱。

这个组合里真正复杂的地方在离线本身。离线的意思不只是“没有外网”,很多时候是连软件源、apt/yum 源都没有,只有一台跳板机或者一台能联网的开发机能帮你把物料准备好。所以整个项目的第一步,其实是在有网的环境里把所有需要的东西打成“包裹”,再运到离线环境里拆开。这不是多装几个软件的问题,而是整个工作方式都变了。

1.2 方案选型:Helm、镜像仓库、外部数据库怎么取舍

在 k8s 上部署 DolphinScheduler 有几种方式,纯手工写 YAML、用官方 Helm chart、或者用一些定制化的 Operator。在这次离线场景里,我强烈建议用官方 Helm chart,然后离线把 chart 包下载下来,改成离线 values 再安装。原因很简单:DolphinScheduler 在 k8s 上要部署的可不只一个镜像,api-server、master、worker、alert 每个角色都是独立的 Deployment 或 StatefulSet,手工写 YAML 不仅量大,而且很容易漏掉环境变量、健康检查、服务端口这些细节。Helm chart 把这些都打包好了,你要改的核心参数基本都收敛在 values.yaml 里,离线部署时工作量会小很多。

SeaTunnel 就不太一样。SeaTunnel 在 k8s 上更常见的是部署一个常驻的 Zeta 集群,用几个 StatefulSet 节点组一个小集群,由 DolphinScheduler 把作业提交过来执行。当然 SeaTunnel 也能以“每任务拉起临时 Pod”的方式跑,但那种模式下每个任务都要重新初始化容器、拉插件、建集群,在离线环境里资源开销太大,而且容器拉起速度慢,完全没有常驻集群划算。所以我这边采用的方式是:Zeta 集群常驻,DS 只负责提交作业和跟踪状态。

镜像仓库这块,离线 k8s 集群内部一般会有一个私有 Registry,比如 Harbor 或者最简单的registry:2。如果你连私有仓库都没有,只在一台机器上做实验,那可以用docker save和docker load的方式把镜像直接导入到每个节点;但如果是一个正经的多节点集群,强烈建议还是搞一个私有仓库。原因不难理解:镜像不推到仓库里,每个节点都得手动 load 一遍,节点多了就变成了灾难,而且后续升级镜像、加节点都非常痛苦。

数据库这里也要提前想好。DolphinScheduler 的元数据默认可以存在它自己 chart 里自带的 PostgreSQL 里,但生产环境我一般会用外部的 MySQL 或者已有的数据库实例。离线环境里通常有现成的 MySQL 在跑,直接复用是性价比最高的做法。如果让 chart 自己再拉起一个 PostgreSQL,不仅多了一套要维护的中间件,而且它的数据卷如果不持久化,Pod 一旦重建,整个调度系统的元数据可能就没了,这个风险在离线环境里更难接受。

存储和网络方面,存储类要看集群有没有提供默认 StorageClass,没有的话得在 values 里指定一个可用的存储类,否则 Pod 一直 Pending 起不来。网络暴露我一般直接用 NodePort,因为离线环境大多在局域网内,Ingress 不是一定有的,NodePort 最省事,改一下 service type 就能访问到 Web UI 和 API。

2. 离线环境前置准备与镜像物料清单

2.1 离线物料清单:除了镜像,你还得带这些

先把需要准备的物料列出来,别到了现场才发现少了东西。有些人只拉了两个主镜像就过去了,结果部署时发现 chart 里还依赖一堆 init 容器、busybox、甚至数据库客户端镜像,全部卡在 ImagePullBackOff。

物料说明备注
DolphinScheduler 各角色镜像api-server、master、worker、alert 对应镜像同一个版本 tag,官方 chart 会自动拼接
SeaTunnel 镜像用于常驻 Zeta 集群需要包含常用 connector 插件
DolphinScheduler Helm chart 包.tgz文件在有网环境用 helm pull 获取
SeaTunnel 部署模板chart 或自写 YAML根据你对 helm 的熟悉程度选
MySQL 客户端工具初始化/检查数据库用离线机器的 yum 源未必有
kubectl、helm 二进制离线机器不一定自带提前拷贝好对应版本
常用 linux 工具包(可选)tar、vim、telnet、curl排查网络问题时必须用

这里有个小经验:DolphinScheduler 官方 chart 包的 values 里通常会有一个image.repository和image.tag,它会用同一个 tag 去拉所有角色的镜像。所以你需要把该版本涉及的每个角色镜像都推到私有仓库里,不能只推一个dolphinscheduler-api。检查办法很简单,用helm install之前先helm template渲染一下,把 pod 里的 image 全部列出来,看看有哪些镜像没准备。

2.2 在有网环境准备镜像并同步到离线仓库

在有网的开发机上,准备工作其实就两个动作:拉镜像、推镜像。假设我这边用 Harbor 作为私有仓库,地址是192.168.x.x:5000。先把镜像拉下来:

docker pull apache/dolphinscheduler:3.2.1 docker pull apache/seatunnel:2.3.8

注意版本号不要乱搭配。DolphinScheduler 和 SeaTunnel 的兼容关系在官方的文档里有对应表,如果 DS 某个版本要求的 SeaTunnel 接口和你装的 SeaTunnel 版本对不上,后面提交作业时会报各种奇怪的客户端错误。所以下镜像前先去看一眼兼容性说明,用推荐的组合版本。

拉完给镜像打上和私有仓库匹配的 tag,然后推送:

docker tag apache/dolphinscheduler:3.2.1 192.168.x.x:5000/library/dolphinscheduler:3.2.1 docker push 192.168.x.x:5000/library/dolphinscheduler:3.2.1 docker tag apache/seatunnel:2.3.8 192.168.x.x:5000/library/seatunnel:2.3.8 docker push 192.168.x.x:5000/library/seatunnel:2.3.8

如果你没有私有仓库,只能往离线机器上拷镜像,那就用docker save打成 tar 包,到目标机器上再docker load。这个办法适合单机或者节点很少的场景,但别忘了每个节点都要 load。我曾经在一个三节点集群上这么干过,忘了其中一台,结果那个节点上的 Pod 死活起不来,查了半天才发现是镜像缺失。后来我还是坚持先搭一个简单的 Registry,一劳永逸。

2.3 Chart 包和依赖的离线搬运方式

在线环境安装多用helm repo add然后helm install,但离线环境最重要的变化是,你必须在有网的那台机器上先把 chart 下载下来,再把.tgz文件带到内网。命令很简单:

helm repo add apache https://dolphinscheduler.apache.org helm repo update helm pull apache/dolphinscheduler --version 3.2.1

这样你就得到一个类似dolphinscheduler-3.2.1.tgz的文件。把这个文件拷贝到离线环境后,安装时直接指向本地文件即可。要注意的是,如果 chart 本身有依赖其他子 chart,比如旧版 DS chart 会依赖 zookeeper、postgresql 这些,那helm pull下来的时候依赖不一定完整。稳妥的做法是先解压 tgz,检查charts/目录里有没有对应的依赖包,没有的话要在有网环境手动helm dependency update,把依赖子 chart 也下载下来一起带过去。

SeaTunnel 这边如果官方没有合适的 helm chart,你可以自己写一套 StatefulSet + Service 的 YAML,要注意的配置其实也不多,后面第 4 章会详细说。但如果你看到某个第三方 chart,第一件事是把它的 values 完整看一遍,搞清楚它到底创建了几个StatefulSet,挂载了哪些存储卷,有没有额外的initContainers,避免最后部署了一堆你不知道是干嘛的 Pod,离线查起来非常费劲。

3. DolphinScheduler 在 k8s 上的离线部署实操

3.1 修改 values.yaml 里的离线核心参数

用 Helm 部署时,我习惯先把 chart 的 values 全部过一遍,看清楚了再改。不要把官方默认配置直接拿来用,因为你离线环境没有外网仓库,默认值一定会去docker.io拉镜像。

我这里以某个 3.2.x 版本为例,把 values 里几个核心部分这样改:

image: registry: 192.168.x.x:5000 repository: library/dolphinscheduler tag: "3.2.1" postgresql: enabled: false externalDatabase: type: mysql driver: com.mysql.cj.jdbc.Driver host: 192.168.x.x port: "3306" database: dolphinscheduler username: ds password: "ds123456" params: characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai zookeeper: enabled: true master: replicas: 2 storage: persistence: enabled: true storageClass: "" accessModes: - ReadWriteOnce worker: replicas: 2 storage: persistence: enabled: true storageClass: "" accessModes: - ReadWriteOnce alert: replicas: 1

这里有几个容易忽略的点。第一,postgresql.enabled: false和externalDatabase是配套的,只改一个会导致 chart 仍然去拉 postgres 镜像,或者连不上数据库一直等。第二,params一定要把它加上,特别是useSSL=false和characterEncoding=utf8mb4,不然 MySQL 的 SSL 握手在某些环境会失败,或者中文数据写入后乱码。第三,master.replicas和worker.replicas如果设了大于 1,要确保你有对应的存储能力,虽然 DS 的日志存储不是必须持久的,但生产上最好还是挂到 PVC 上,不然 Pod 一重建,历史运行日志全丢了。

还有一点提醒一下,不同版本之间 values 字段名可能有差异,比如有些版本把alert.replicas叫做alertServer.replicas。拿到 chart 包后先看默认 values 的注释,对照你的版本做调整,别套用网上随便搜到的老旧配置。

3.2 执行 Helm 安装并完成基本验证

物料都齐了之后,安装命令其实非常短。可以直接用本地 chart 文件,也可以先把 tgz 解压到目录下再指定目录安装:

helm install dolphinscheduler ./dolphinscheduler-3.2.1.tgz \ -n>kubectl get pods -n>kubectl describe pod dolphinscheduler-master-0 -n>kubectl create secret docker-registry regcred \ --docker-server=192.168.x.x:5000 \ --docker-username=admin \ --docker-password=xxx \ --docker-email=admin@example.com

然后在 chart values 里配上imagePullSecrets,或者直接给 serviceaccount 关联 secret。

这个位置特别容易踩坑的是,很多人记了 docker 的登录态却不创建 k8s secret,Pod 每次都因为认证失败被拒绝拉取镜像。在离线环境里这种错特别隐蔽,因为组件本身没问题,只是仓库不认你。

5.2 DolphinScheduler 一直连不上数据库

DS 的 master/worker 在启动时会拼命尝试连接配置的数据库。如果数据库连接不上,Pod 表现就是 CrashLoopBackOff,日志里可能有communications link failure、Access denied、Unknown database这类信息。

排查要点:第一,MySQL 是否允许来自 k8s 节点网段的连接。MySQL 默认绑定 127.0.0.1,如果只监听本地,那必须改配置绑定0.0.0.0或者业务网卡地址,否则别的机器就连不上。第二,账号权限要给足。DS 需要建表、改表,账号不能只给 SELECT,至少要给GRANT ALL ON dolphinscheduler.* TO 'ds'@'%'。第三,数据库字符集,建库时用 utf8mb4:

CREATE DATABASE dolphinscheduler DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

如果表已经建坏了,最简单的做法是删掉库重建,然后重启 DS Pod,让初始化逻辑重新执行。离线环境里没有外网的“重试一下就好了”这种好事,所以数据库初始化阶段的耐心检查很重要。

5.3 SeaTunnel 任务一直 Pending 或提交超时

如果你在 DS 里运行 SeaTunnel 任务,发现任务实例一直处于提交中或执行中,怎么等都等不到结果,优先怀疑两个地方:一个是 SeaTunnel 集群地址配置错误,DS 发出去的请求 Zeta 集群根本没收到;另一个是 Zeta 集群内部成员状态异常,收到请求但任务无法调度执行。

排查第一个,去 Zeta 集群 Pod 上看日志,看有没有接收到 DS 的 job 提交请求。排查第二个,看 Zeta 集群的节点数量是不是少于期望值,比如配置了 3 个副本,但只有一个 Running,那任务大概率会 Pending。

一个很有效的自测方法是手动进入 SeaTunnel 容器,把 DS 上配置的那段任务脚本保存为文件,然后用 SeaTunnel 的启动命令直接跑一次作业。如果直接在集群内部能跑通,说明引擎侧没问题,问题出在 DS 和 SeaTunnel 的通信链路上;如果手动跑也报错,就直接在容器日志里定位具体报错信息,比如缺少 connector、源库连不上、SQL 语法不兼容等等。这种方式能帮你快速把“DS 问题”和“SeaTunnel 问题”切分清楚。

5.4 时间同步、日志持久化和 DNS 解析几个容易漏的坑

离线环境里节点时间不同步是特别容易忽视但破坏力很大的问题。DolphinScheduler 的 master 在调度任务时非常依赖时间判断,如果不同节点间时间差过大,工作流可能不触发,任务实例可能一直处于等待状态。所以部署前先把每台机器的时间统一好,用 chrony 搭一个内网时间源,或者至少手动校准一次。

日志持久化是另一个容易被跳过但建议尽早做好的事。SeaTunnel 的作业日志在容器里,DS 的 worker 日志也在容器里,如果 Pod 被重新调度,这些日志就全没了。排查历史问题时没有日志非常痛苦。生产部署时宁可多配几块 PVC,也要把日志目录挂出来,这是性价比很高的投资。

DNS 解析也要多看两眼。k8s 集群内 Pod 之间访问,如果你在 DS 的配置里写的是 SeaTunnel 的短服务名,而两个 Pod 在不同 namespace 下,那服务名必须是完整的 FQDN 格式,类似seatunnel-zeta-svc.data-platform.svc.cluster.local。我遇到过不止一次因为短域名没解析,导致 DS 怎么也提交不到 SeaTunnel,而两边单独测试都正常的情况。

最后再说一句,离线部署这件事本身并不可怕,真正耗时间的是你没有预料到离线会切断那么多“隐性依赖”。镜像依赖、Helm 依赖、时间同步、DNS 解析,每一样都可能在关键时刻给你一个下马威。我现在的习惯是把所有物料预先列成清单,每一项都打钩确认后才进场。按照这条路子把 DS 和 SeaTunnel 搭起来之后,后续再扩充新的数据源、新的工作流就顺多了,整套体系也经得起上生产后的折腾。

返回列表