一次“没有运行日志”的DolphinScheduler任务失败排查

引子:失败了,却没有一行运行日志

生产调度中最让人困惑的场景之一,是任务页面明确显示"失败",告警也已经生成,但任务的开始时间、执行主机、执行目录和日志路径全部为空。没有Worker日志,没有YARN Application ID,更没有Spark Driver、Executor或MapReduce Container日志。

面对这种现象,很多排查会本能地从Worker、脚本路径、YARN和网络连通性入手。但本案例最终证明:任务甚至还没有进入Worker,故障发生在Master构造任务执行上下文的阶段。真正的根因不是脚本,也不是虚拟IP,而是工作流没有绑定有效租户。

核心结论:当 task_instance 已是失败状态,但 host、start_time、log_path、execute_path 全部为 NULL 时,应优先排查 Master 提交与分发阶段,而不是继续寻找不存在的任务运行日志。

1.故障现场:前端失败、告警失败、日志为空

案例中的批量任务每天14:15触发。调度前端显示首个Shell节点失败,后续节点未执行。告警表中已经产生"scheduler failed"记录,但告警发送日志又出现"no bind plugin instance"。

从 t_ds_alert 反查最近一条失败告警:

复制代码
SELECT id, title, content, alert_status, warning_type,
log, alertgroup_id, create_time,
process_instance_id
FROM t_ds_alert
ORDER BY create_time DESC
LIMIT 1;

告警内容里包含工作流实例ID、任务Code、任务名称、任务类型和失败状态,因此可以用它作为整条证据链的入口。但这里必须区分两个问题:任务执行失败与告警发送失败是相互独立的。

排查原则:不要把"告警发送失败"当成任务失败原因。先定位任务在哪个生命周期阶段失败,再单独处理告警路由。

2.第一条证据:元数据表证明任务从未进入Worker

通过告警中的 process_instance_id 和 taskCode,可以精确查询 t_ds_task_instance。结果显示任务实例已创建,状态为6(FAILURE),但除了 submit_time 外,其余运行字段均为空。

定位任务实例的生命周期字段:

复制代码
SELECT id AS task_instance_id, name, state, host,
submit_time, start_time, end_time,
log_path, execute_path, app_link
FROM t_ds_task_instance
WHERE process_instance_id = <process_instance_id>
AND task_code = <task_code>
ORDER BY id DESC;

到这里可以做出第一个关键判断:任务失败发生在Worker执行之前。继续SSH到Worker寻找日志、执行 yarn logs 或分析Spark Executor没有意义,因为对应的执行实体根本没有创建。

3.一个容易误判的支线:Master为什么显示成另一个IP

排查过程中,Master节点的主IP与调度前端显示的Master地址不一致。SSH到前端显示地址后,主机名和Shell提示符仍然显示主IP,一度怀疑发生了错误路由或地址注册异常。

同时确认SSH连接四元组、主机名和全部网卡地址:

复制代码
echo "$SSH_CONNECTION"
hostname -f
ip -br addr

ip -br addr 最终显示主IP以 /24 绑定,另一个地址以 /32 绑定在同一块 eth0 上。后者解析为EMR虚拟主机名,因此它不是另一台机器,而是同一节点的辅助/虚拟服务IP。

仅在Master本机SSH虚拟IP,只能证明本机可达。为了验证调度通信是否真的正常,必须从一台实际Worker节点测试Master端口。

从实际Worker节点测试两个Master地址:

复制代码
nc -vz -w 3 <master-primary-ip> 5678
nc -vz -w 3 <master-virtual-ip> 5678

两个地址的5678端口都能连通;Master心跳日志也持续成功写入 /nodes/master/:5678。至此可以排除Master注册地址不可达。

经验:前端显示IP与主机主IP不一致,不等于地址错误。先确认是否为同机辅助IP/VIP,再从真实通信对端验证业务端口;不要只做本机自连接测试。

3.决定性证据:Master日志中的 Tenant does not exists

既然任务从未进入Worker,真正的日志应当在Master。使用工作流实例ID、任务实例ID、任务Code和任务名称检索Master日志后,故障链路被精确还原。

使用多个稳定标识关联同一次调度:

复制代码
grep -R -nE 'WorkflowInstance-<id>|TaskInstance-<id>|<task_code>|<task_name>' /var/log/.../master-server/

故障发生在几毫秒内,尚未真正分发到Worker:

复制代码
14:15:00.650 Task is ready to dispatch to worker
14:15:00.651 Tenant does not exists
14:15:00.651 Task state changes to FAILURE
14:15:00.652 Get taskExecutionContext fail
14:15:00.653 Dispatch standby task failed
14:15:00.690 Workflow state changes to FAILURE

日志对象进一步给出决定性字段:processDefinition.tenantId=-1、tenantCode=null;processInstance.tenantId=-1、tenantCode=null、queue=null。Master在构造TaskExecutionContext时必须确定执行租户,而当前工作流没有任何有效租户,因此任务直接失败。

根因:工作流定义与工作流实例的 tenantId 均为 -1,tenantCode 为空。Master无法生成任务执行上下文,任务在分配Worker之前被置为FAILURE。

5.为什么没有日志:理解DolphinScheduler任务生命周期

DolphinScheduler的任务日志不是在TaskInstance写入数据库时产生,而是在Master成功构造上下文、选中Worker并由Worker创建执行目录后才会拥有log_path。理解这个顺序,是处理"失败但无日志"的关键。

这套字段判断还能快速指导日志采集策略:host为空时查Master;host有值但start_time为空时查分发与Worker接收;log_path有值时查Worker日志;出现Application ID后才进入YARN日志采集。

6.Tenant到底承担什么角色

在DolphinScheduler中,Tenant不仅是一个界面上的分类字段,它通常关联任务的运行身份与资源队列。Master构造任务执行上下文时,需要从工作流定义、执行用户和租户表中解析出tenantCode与queue。缺少这组信息,Master无法安全地告诉Worker"用谁的身份、在哪个队列执行"。

在启用了Linux用户切换、HDFS权限、Kerberos或YARN队列隔离的环境中,租户配置还会进一步影响系统用户、HDFS目录、票据身份和资源队列。因此,修复租户后仍应验证对应OS用户和大数据平台权限。

7.用元数据确认租户关系

核对工作流、用户与租户的关联:

复制代码
SELECT pd.id, pd.code, pd.name, pd.version,
pd.user_id, u.user_name,
pd.tenant_id AS workflow_tenant_id,
u.tenant_id AS user_tenant_id,
t.id AS matched_tenant_id,
t.tenant_code, t.queue_id
FROM t_ds_process_definition pd
LEFT JOIN t_ds_user u ON u.id = pd.user_id
LEFT JOIN t_ds_tenant t ON t.id = pd.tenant_id
WHERE pd.code = <process_definition_code>;

确认租户是否存在,并检查历史工作流版本:

复制代码
SELECT * FROM t_ds_tenant ORDER BY id;

SELECT * FROM t_ds_user
WHERE id = <user_id>
OR user_name = '<user_name>';

SELECT code, name, version, tenant_id, release_state
FROM t_ds_process_definition_log
WHERE code = <process_definition_code>
ORDER BY version DESC;

本案例的预期异常结果是 workflow_tenant_id=-1,并且无法关联到 t_ds_tenant。若用户已有租户但工作流仍为-1,说明工作流创建、导入或版本迁移时没有正确继承租户;只修改用户并不一定会自动回填既有工作流。

8.修复步骤:不要直接UPDATE元数据库

推荐通过调度前端完成修复,避免绕过版本日志、缓存、权限关系与审计信息。标准处理步骤如下:

  1. 在安全中心的租户管理中创建或选择有效租户,并关联正确的YARN Queue。

  2. 在用户管理中为工作流负责人绑定该租户。

  3. 编辑故障工作流,重新选择有效租户并保存,生成新的工作流版本。

  4. 重新上线工作流与定时调度,确认最新 t_ds_process_definition.tenant_id 大于0。

  5. 从新版本发起一次全新的手动运行,不直接恢复旧的失败实例。

  6. 任务进入Worker后,再验证Linux用户、脚本权限、HDFS/Kerberos以及YARN Queue权限。

为什么不重跑旧实例:旧实例保存的是旧工作流版本快照,其中 tenantId 仍为 -1。直接恢复失败节点可能继续使用旧配置;发布新版本后应创建新实例验证。

9.修复后如何验收

修复后的验证不能只看页面变绿,还要确认任务确实跨过了原故障点。建议同时检查定义层、实例层和运行层。

  • 定义层:最新工作流版本 tenant_id 为有效正数,能够关联到 t_ds_tenant。

  • 实例层:新流程实例 tenantCode、queue 不再为空。

  • 分发层:TaskInstance.host 不为空,并能看到Worker接收记录。

  • 运行层:start_time、execute_path、log_path 正常生成。

  • 大数据作业层:若脚本提交Spark/MR,能够提取Application ID并查询YARN日志。

结语:没有日志,本身就是日志

在调度系统里,"没有日志"并不意味着没有线索。相反,host、start_time、execute_path、log_path 和 app_link 同时为空,已经非常明确地告诉我们:任务尚未进入执行层。沿着这个事实回到Master,几毫秒级的时间线最终指向 Tenant does not exists。

一次高质量排障,不是把所有组件都查一遍,而是用元数据划定边界,用日志建立时间线,用网络测试排除支线,再把任务失败与告警失败分别闭环。只有这样,才能从"经验式排查"走向可解释、可复用、可产品化的诊断体系。

一句话总结

TaskInstance失败且所有运行字段为NULL:先查Master;本案例的直接根因是工作流 tenantId=-1,Master无法构造TaskExecutionContext。

相关推荐
东风破_1 小时前
Vibe Coding 上头之后,我开始用 SDD 给 AI 编程加一张“施工图”
人工智能·程序员
专注API从业者1 小时前
告别人工盯品!借助 Open Claw 搭建电商商品自动化监控与数据分析系统(完整可运行源码)
大数据·运维·数据库·数据分析·自动化
weixin_403810131 小时前
AI智能体写安卓自动化脚本教程:Cursor/Trae+CLI 实战,自然语言生成代码
android·人工智能·自动化·跨境电商·安卓自动化脚本·多账户运营
深圳雨林凯AI2 小时前
雨林凯AI四方连图介质适配原理:像素密度、纱线纹理与颜色管理怎么协调
人工智能
viskaz2 小时前
数据库迁移流程学习
数据库
ZGIAI2 小时前
ZGI 混合检索:汇集候选并统一重排
人工智能·架构
ZGIAI2 小时前
ZGI 运行日志:还原任务与节点状态
人工智能·架构
Scott9999HH2 小时前
2026 中小企业如何破局 AI 搜索?轻量化 GEO 优化系统架构设计与 Python 实战落地
人工智能
auspicious航2 小时前
PostgreSQL写入慢全攻略:原因排查与性能调优实战
数据库·postgresql