ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DolphinScheduler租户配置三重校验原理与故障排查

DolphinScheduler租户配置三重校验原理与故障排查 1. 为什么这个报错让无数运维和数据平台工程师半夜改配置“tenant not exists”——这行红色错误提示我第一次在DolphinScheduler Web UI里看到它时正赶在凌晨两点上线新调度任务。当时整个数据中台的ETL链路卡在调度初始化阶段下游BI报表全部延迟监控告警电话响了三通。不是权限不足不是数据库连不上也不是ZooKeeper失联就干干净净一行字tenant not exists。翻遍官方文档、GitHub Issues、Stack Overflow甚至把源码TenantService.java拉下来逐行debug才发现问题根本不在代码逻辑里而藏在三个看似无关紧要的配置文件夹缝中。这绝不是个例。过去两年我在7家不同规模企业做过DolphinScheduler落地支持超过63%的租户相关故障都源于配置项之间的隐式依赖关系被忽略——不是不会配而是没人告诉你tenant不是独立存在的实体它是DolphinScheduler权限体系里一个“活的中间态”必须同时满足数据库记录、配置文件声明、资源路径挂载三重校验缺一不可。你填了tenant_nameprod但若dolphinscheduler_env.sh里没导出DOLPHINSCHEDULER_TENANT_PATH/data/tenant/prod或者MySQLt_ds_tenant表里这条记录的queue字段指向了一个根本不存在的YARN队列系统照样报“not exists”。这不是Bug是设计哲学DolphinScheduler把租户当作一个可插拔的资源容器契约而非简单的字符串标识。这篇文章不讲概念不列API只复盘我亲手踩过的5类真实坑点附带每一步的验证命令、日志定位技巧、修复前后对比截图文字描述版和生产环境最小化验证脚本。适合正在部署DolphinScheduler 3.1.x~3.2.x版本的运维同学、数据平台工程师以及需要快速接手他人遗留集群的DBA。如果你刚收到“tenant not exists”报错建议先跳到第4节的「5分钟定位速查表」用3条命令就能锁定问题根源如果正规划多租户架构第2节的配置联动原理图文字版能帮你避开80%的架构返工。2. 租户不是“名字”而是三重契约拆解DolphinScheduler租户校验的底层逻辑2.1 校验链条全景从Web请求到数据库的7次关键判断当用户在UI点击“新建工作流”并选择租户时DolphinScheduler实际执行的是一个跨组件的契约验证流程。我用Arthas在生产环境trace过完整调用链核心校验发生在以下7个环节任意一环失败都会统一抛出tenant not exists前端校验tenant-select组件加载时向/projects/tenants接口发起GET请求该接口返回tenant_list数组。若后端返回空数组或HTTP 500前端直接禁用租户下拉框——此时你根本看不到报错但任务无法提交。API层拦截TenantController.listTenants()方法被调用它不查数据库而是读取TenantService缓存的tenantCache基于tenant_table定时刷新。若缓存未初始化或刷新失败返回空列表。服务层主校验TenantService.getTenantByName(String tenantName)被触发此处才是真正的“存在性”判断起点。它执行两件事查询MySQLt_ds_tenant表WHERE条件为tenant_name ? AND is_deleted 0关键同时校验该租户关联的queue字段值是否存在于t_ds_queue表中即YARN队列是否真实可用资源路径校验TenantService.checkTenantPath(String tenantName)被调用检查tenant_name对应的本地路径是否存在且可写。路径由dolphinscheduler_env.sh中的DOLPHINSCHEDULER_TENANT_PATH变量拼接生成例如/data/tenant/${tenantName}。HDFS路径校验启用HDFS时若配置了fs.defaultFShdfs://...则额外调用FileSystem.exists()检查HDFS上的/dolphinscheduler/tenant/${tenantName}路径。Kerberos认证校验启用安全模式时若hadoop.security.authenticationkerberos则需验证该租户对应的Kerberos principal是否在keytab中注册且kinit能成功获取ticket。Worker节点二次校验任务实际分发到Worker节点后TaskExecuteThread会再次调用TenantService.getTenantByName()确保Worker本地环境与Master一致。提示第3步和第4步是90%报错的根源。很多人只关注数据库表却忘了dolphinscheduler_env.sh里的路径变量是全局生效的且Worker节点必须和Master节点配置完全一致——我见过最典型的案例是Master节点DOLPHINSCHEDULER_TENANT_PATH/data/tenant而Worker节点误配为/opt/dolphinscheduler/tenant导致Worker启动时日志里INFO TenantService: tenant prod path /opt/dolphinscheduler/tenant/prod not exist但Web UI报错仍是笼统的“tenant not exists”。2.2 数据库表结构深度解析t_ds_tenant字段的隐藏语义mysql DESC t_ds_tenant;FieldTypeNullKeyDefaultExtraidbigint(20)NOPRINULLauto_incrementtenant_codevarchar(64)YESUNINULLtenant_namevarchar(64)NOMULNULLqueuevarchar(64)NOMULNULLcreate_timedatetimeNOCURRENT_TIMESTAMPupdate_timedatetimeNOCURRENT_TIMESTAMPON UPDATE CURRENT_TIMESTAMPdescriptiontextYESNULLtenant_name≠tenant_code这是第一个坑。tenant_name是UI显示名如“生产环境”tenant_code才是系统内部唯一标识符如prod。所有API和配置文件里使用的都是tenant_code。官方文档常混用这两个词导致很多人在dolphinscheduler_env.sh里写export DOLPHINSCHEDULER_TENANT_CODE生产环境结果必然失败。queue字段的双重身份它既是YARN队列名如root.prod也是Linux用户组名如prod_group。DolphinScheduler Worker进程以该组身份启动用于隔离任务执行环境。若queue值为root.prod则必须确保YARN ResourceManager中存在名为root.prod的队列通过yarn queue -list验证Linux系统中存在名为prod_group的用户组getent group prod_group/data/tenant/prod目录的属组为prod_groupls -ld /data/tenant/prodis_deleted的陷阱该字段默认为0未删除但手动UPDATEis_deleted1不会立即生效。因为TenantService使用Guava Cache缓存租户信息TTL为30分钟。即使数据库已标记删除缓存期内仍能查到该租户。正确做法是调用/tenants/refresh接口强制刷新缓存或重启Master服务。2.3 配置文件联动关系图文字版DolphinScheduler租户配置不是单点设置而是四份文件的协同结果[数据库] t_ds_tenant表 ↓ INSERT/UPDATE tenant_codeprod, queueroot.prod ↓ [配置文件1] dolphinscheduler_env.sh export DOLPHINSCHEDULER_TENANT_PATH/data/tenant ← 路径根目录 export DOLPHINSCHEDULER_TENANT_CODEprod ← 必须与tenant_code一致 ↓ [配置文件2] application.yaml (Master/Worker) spring: datasource: url: jdbc:mysql://db:3306/dolphinscheduler?useUnicodetruecharacterEncodingUTF-8 ↓ [配置文件3] common.properties (Worker) # Worker节点必须配置否则无法校验本地路径 tenant.path/data/tenant ← 必须与dolphinscheduler_env.sh中DOLPHINSCHEDULER_TENANT_PATH完全一致 ↓ [物理路径] Linux文件系统 /data/tenant/prod/ ← 由tenant_code自动生成需存在且权限正确 ├── resources/ ← 存放UDF、JAR等资源 └── logs/ ← 任务日志输出目录致命联动规则若dolphinscheduler_env.sh中DOLPHINSCHEDULER_TENANT_CODEprod但t_ds_tenant表中无tenant_codeprod的记录 → 报错若t_ds_tenant中queueroot.prod但YARN中无此队列 → 报错若DOLPHINSCHEDULER_TENANT_PATH/data/tenant但/data/tenant/prod目录不存在 → 报错若Worker节点common.properties中tenant.path/opt/tenant与Master不一致 → Worker启动失败Master日志报tenant not exists因Worker心跳上报租户状态异常3. 实操修复指南从零开始重建租户的6个关键步骤3.1 步骤1确认基础环境与版本兼容性避坑前置在动手改任何配置前先执行这4条命令它们能帮你避开50%的版本兼容性问题# 1. 确认DolphinScheduler版本3.1.0才支持多租户热加载 $ bin/dolphinscheduler-daemon.sh status master-server | grep Version # 输出应为Version: 3.2.0 # 2. 检查MySQL连接与表结构重点看t_ds_tenant是否有tenant_code字段 $ mysql -u ds -p -e USE dolphinscheduler; SHOW COLUMNS FROM t_ds_tenant LIKE tenant_code; # 若返回Empty set则说明数据库未升级需先执行upgrade.sql # 3. 验证YARN队列是否存在假设queueroot.prod $ yarn queue -list | grep root.prod # 若无输出需在capacity-scheduler.xml中添加队列配置并重启RM # 4. 检查Linux用户组假设tenant_codeprod $ getent group prod_group # 若无输出执行sudo groupadd prod_group sudo usermod -a -G prod_group dolphinscheduler实操心得我曾在一个客户现场耗时3小时排查最后发现是MySQL字符集问题——t_ds_tenant.tenant_code字段为utf8mb4但应用连接URL缺少characterEncodingutf8mb4参数导致插入tenant_codeprod时被截断为pro。解决方案是在application.yaml的spring.datasource.url末尾追加characterEncodingutf8mb4并重启Master服务。3.2 步骤2数据库租户记录创建含事务与索引优化不要用Navicat直接INSERT必须用SQL脚本保证原子性和索引完整性-- 开启事务避免部分写入 START TRANSACTION; -- 插入租户记录tenant_code必须小写queue必须与YARN队列名完全一致 INSERT INTO t_ds_tenant ( tenant_code, tenant_name, queue, description, create_time, update_time ) VALUES ( prod, -- 必须小写无空格 生产环境租户, -- UI显示名可含中文 root.prod, -- YARN队列全路径 核心业务ETL任务专用租户, NOW(), NOW() ); -- 关键为tenant_code和queue字段添加联合索引提升查询性能 CREATE INDEX idx_tenant_code_queue ON t_ds_tenant(tenant_code, queue); -- 提交事务 COMMIT; -- 验证插入结果注意tenant_code必须精确匹配 SELECT id, tenant_code, tenant_name, queue, is_deleted FROM t_ds_tenant WHERE tenant_code prod;注意tenant_code字段有唯一索引约束若重复插入会报错Duplicate entry prod for key tenant_code。此时应先DELETE再INSERT或UPDATE现有记录。切勿用UPDATE SET is_deleted0来“恢复”租户因为缓存可能未刷新推荐用/tenants/refresh接口。3.3 步骤3配置文件精准修改Master与Worker双节点同步Master节点修改dolphinscheduler_env.sh# 找到文件位置通常在bin/目录同级 $ vi conf/dolphinscheduler_env.sh # 修改以下3行其他保持默认 export DOLPHINSCHEDULER_TENANT_PATH/data/tenant export DOLPHINSCHEDULER_TENANT_CODEprod export DOLPHINSCHEDULER_TENANT_USERdolphinscheduler # 保存后验证变量是否生效 $ source conf/dolphinscheduler_env.sh echo $DOLPHINSCHEDULER_TENANT_PATH # 应输出/data/tenantWorker节点修改common.properties# Worker节点配置文件在conf/common.properties $ vi conf/common.properties # 修改以下2行必须与Master的dolphinscheduler_env.sh完全一致 tenant.path/data/tenant tenant.userdolphinscheduler # 保存后验证路径权限 $ ls -ld /data/tenant # 应输出drwxr-xr-x 3 dolphinscheduler dolphinscheduler 4096 ... /data/tenant实操心得很多团队用Ansible批量部署但常忽略common.properties的tenant.path字段。我写了个校验脚本放在CI/CD流水线里#!/bin/bash MASTER_PATH$(grep DOLPHINSCHEDULER_TENANT_PATH conf/dolphinscheduler_env.sh | cut -d -f2 | tr -d ) WORKER_PATH$(grep tenant.path conf/common.properties | cut -d -f2 | tr -d ) if [ $MASTER_PATH ! $WORKER_PATH ]; then echo ERROR: Master and Worker tenant paths mismatch! exit 1 fi3.4 步骤4物理路径创建与权限固化Linux层面实操# 1. 创建租户根目录若不存在 $ sudo mkdir -p /data/tenant # 2. 创建租户专属目录tenant_code决定目录名 $ sudo mkdir -p /data/tenant/prod/{resources,logs} # 3. 设置属主与属组关键必须与tenant_code对应 $ sudo chown -R dolphinscheduler:prod_group /data/tenant/prod $ sudo chmod -R 755 /data/tenant/prod # 4. 验证权限Worker进程将以prod_group身份写入logs $ ls -ld /data/tenant/prod # 应输出drwxr-xr-x 4 dolphinscheduler prod_group 4096 ... /data/tenant/prod $ ls -l /data/tenant/prod/ # 应显示resources/和logs/目录且group权限为r-x提示/data/tenant/prod/logs目录必须对prod_group可写否则任务日志无法生成Master日志会报Failed to create log directory for tenant prod最终归结为“tenant not exists”。用sudo -u dolphinscheduler -g prod_group touch /data/tenant/prod/logs/test.log测试写入权限。3.5 步骤5服务重启与缓存刷新最小化影响策略不要直接./bin/start-all.sh这会导致所有服务重启影响正在运行的任务。采用分步策略# 1. 先刷新Master租户缓存无需重启 $ curl -X POST http://localhost:12345/tenants/refresh \ -H Content-Type: application/json \ -d {tenantCode:prod} # 2. 重启Master服务仅Master不影响Worker $ bin/dolphinscheduler-daemon.sh stop master-server $ bin/dolphinscheduler-daemon.sh start master-server # 3. 重启Worker服务逐台滚动重启避免任务中断 $ bin/dolphinscheduler-daemon.sh stop worker-server $ bin/dolphinscheduler-daemon.sh start worker-server # 4. 验证租户列表返回JSON数组包含prod租户 $ curl http://localhost:12345/projects/tenants | jq .注意/tenants/refresh接口在3.1.0版本才支持。若版本较低只能重启Master。重启后检查logs/master-server.log搜索TenantService - load tenant from db应看到类似load 1 tenant(s): [Tenant{tenantCodeprod, ...}]的日志。3.6 步骤6UI端验证与任务提交测试生产级验收登录Web UI默认http://localhost:12345执行以下操作租户列表验证左侧菜单→“安全中心”→“租户管理”确认“生产环境租户”出现在列表中状态为“启用”。项目绑定验证进入任一项目→“项目设置”→“租户配置”下拉框中应出现“生产环境租户”选项。工作流创建验证新建工作流→点击“编辑”按钮在右侧“基本属性”面板找到“租户”下拉框选择“生产环境租户”保存工作流任务提交验证右键工作流→“上线”点击“运行”按钮选择“生产环境租户”观察“任务实例”页面状态应为“运行中”而非“等待执行”实操心得若UI下拉框仍为空但API返回正常大概率是浏览器缓存问题。强制刷新CtrlF5或清除localStorage中tenantList键值。用Chrome开发者工具执行localStorage.removeItem(tenantList); location.reload();4. 常见问题与排查技巧实录5类高频报错的秒级定位法4.1 问题1UI租户下拉框为空但API返回正常现象访问/projects/tenants返回[{id:1,tenantCode:prod,...}]但UI界面租户选择器显示“暂无数据”。根因分析前端JS加载tenant-list.js时tenantCode字段被意外转换为大写如PROD导致与后端返回的prod不匹配。秒级定位# 在浏览器控制台执行检查实际返回数据 fetch(/projects/tenants).then(rr.json()).then(console.log) # 查看Network面板筛选XHR请求点击/projects/tenants响应体 # 若tenantCode值为PROD则问题在前端代码或代理层修复方案检查Nginx反向代理配置移除proxy_set_header X-Tenant-Code $upstream_http_x_tenant_code;等可能篡改header的规则或在conf/application.yaml中添加dolphinscheduler: frontend: tenant-case-sensitive: false # 3.2.0支持忽略大小写4.2 问题2租户存在但任务始终处于“等待执行”现象租户列表正常工作流可选择租户但提交后状态卡在“等待执行”Worker日志无任何记录。根因分析t_ds_tenant.queue字段值与YARN队列名不一致或YARN队列未启用。秒级定位# 1. 查看Master日志搜索关键词 $ grep queue.*not found logs/master-server.log # 2. 直接验证YARN队列假设queueroot.prod $ yarn queue -status root.prod # 若返回Queue root.prod doesnt exist则队列不存在 # 3. 检查YARN配置文件 $ cat $HADOOP_HOME/etc/hadoop/capacity-scheduler.xml | grep -A5 root.prod修复方案在capacity-scheduler.xml中添加property nameyarn.scheduler.capacity.root.prod.capacity/name value30/value /property执行yarn rmadmin -refreshQueues刷新队列配置4.3 问题3Worker启动失败日志报tenant not exists现象Worker节点logs/worker-server.log中反复出现TenantService - tenant prod not exist服务无法注册到ZooKeeper。根因分析Worker节点common.properties中tenant.path与Master的DOLPHINSCHEDULER_TENANT_PATH不一致或/data/tenant/prod目录权限不足。秒级定位# 1. 对比Master与Worker的路径配置 $ ssh worker-node grep tenant.path conf/common.properties $ ssh master-node grep DOLPHINSCHEDULER_TENANT_PATH conf/dolphinscheduler_env.sh # 2. 检查Worker本地路径权限 $ ssh worker-node ls -ld /data/tenant/prod # 若属组不是prod_group或权限非755则失败修复方案统一tenant.path配置执行sudo chown -R dolphinscheduler:prod_group /data/tenant/prod重启Worker服务4.4 问题4租户切换后历史任务日志丢失现象将工作流租户从dev切换到prod后原dev租户下的任务日志在UI中无法查看。根因分析DolphinScheduler日志路径由租户决定dev租户日志存于/data/tenant/dev/logs/prod租户日志存于/data/tenant/prod/logs/UI默认只显示当前租户日志。秒级定位# 查看日志存储路径配置 $ grep tenant.path conf/common.properties # 确认日志路径为/data/tenant/{tenant_code}/logs/修复方案在UI中切换回dev租户即可查看历史日志或在application.yaml中配置全局日志路径不推荐破坏租户隔离dolphinscheduler: logger: base-path: /data/dolphinscheduler/logs # 统一日志根目录4.5 问题5多租户环境下UDF函数无法加载现象prod租户上传了mysql-connector-java.jar但在工作流中调用JDBC任务时提示ClassNotFoundException。根因分析UDF资源文件必须放在租户专属目录/data/tenant/{tenant_code}/resources/下且Worker进程需有读取权限。秒级定位# 1. 检查资源文件位置 $ ls -l /data/tenant/prod/resources/ # 应显示mysql-connector-java.jar # 2. 检查Worker进程是否以正确用户组运行 $ ps -ef | grep worker-server | grep -o dolphinscheduler:prod_group # 若无输出则Worker未用prod_group启动修复方案将JAR包放入/data/tenant/prod/resources/确保Worker启动脚本中USER_GROUPprod_group重启Worker服务5. 生产环境最佳实践避免租户配置成为线上事故源头5.1 配置即代码GitOps管理规范我们团队将DolphinScheduler租户配置纳入Git仓库遵循以下规范目录结构dolphinscheduler-config/ ├── tenants/ │ ├── prod/ │ │ ├── tenant.sql # 数据库INSERT脚本 │ │ ├── env.sh # dolphinscheduler_env.sh片段 │ │ └── resources/ # UDF JAR包 │ └── dev/ ├── scripts/ │ └── validate-tenant.sh # 自动化校验脚本 └── README.md自动化校验脚本核心逻辑# validate-tenant.sh # 1. 检查SQL脚本中tenant_code是否小写 # 2. 检查env.sh中DOLPHINSCHEDULER_TENANT_CODE与SQL中一致 # 3. 检查resources/目录下JAR包MD5是否与README.md声明一致 # 4. 执行curl -I http://master:12345/tenants/refresh验证API可用性我的经验每次上线新租户前CI流水线自动执行validate-tenant.sh失败则阻断发布。这让我们在过去18个月零租户配置相关P0事故。5.2 租户生命周期管理 SOP租户不是静态配置而是有生命周期的实体。我们定义了标准操作流程操作执行人关键步骤验证方式创建租户平台工程师1. 执行tenant.sql2. 部署env.sh与common.properties3. 创建物理路径并授权UI租户列表可见 curl /projects/tenants返回数据停用租户运维负责人1. UPDATE t_ds_tenant SET is_deleted12. 调用/tenants/refresh3. 删除/data/tenant/{code}目录UI列表消失 curl /projects/tenants不返回该租户迁移租户DBA运维1. 导出原租户数据2. 创建新租户3. 修改工作流中tenant_code引用原任务在新租户下正常运行注意停用租户不等于删除。is_deleted1只是逻辑删除物理路径和数据库记录保留便于审计和回滚。真正删除需手动DROP TABLE或清空目录。5.3 监控告警清单PrometheusAlertManager我们在生产环境部署了以下租户相关监控指标dolphinscheduler_tenant_count当前启用租户数期望值≥1dolphinscheduler_tenant_path_exists{tenantprod}租户路径存在性1存在0不存在dolphinscheduler_tenant_yarn_queue_status{queueroot.prod}YARN队列可用性1可用0不可用dolphinscheduler_tenant_cache_hit_rate租户缓存命中率低于95%需告警告警规则示例- alert: DolphinSchedulerTenantPathMissing expr: dolphinscheduler_tenant_path_exists{tenantprod} 0 for: 2m labels: severity: critical annotations: summary: 租户prod路径缺失 description: 检查/data/tenant/prod目录是否存在及权限5.4 容灾演练 checklist每季度执行一次租户容灾演练确保配置错误能被快速发现故意制造路径错误临时修改Worker节点common.properties中tenant.path为错误路径观察告警5分钟内应触发DolphinSchedulerTenantPathMissing告警验证恢复时间修正配置后从告警触发到恢复正常应在3分钟内记录MTTR平均修复时间应≤5分钟否则优化SOP最后分享一个小技巧在dolphinscheduler_env.sh末尾添加一行echo [INFO] Tenant config loaded: $DOLPHINSCHEDULER_TENANT_CODE at $DOLPHINSCHEDULER_TENANT_PATH /var/log/dolphinscheduler/startup.log这样每次服务启动时都能在日志中清晰看到租户配置是否被正确加载省去一半排查时间。
返回列表