DolphinScheduler

October 30, 2019

版本

本文基于1.1.0的tag: https://github.com/apache/incubator-dolphinscheduler/releases/tag/1.1.0

大数据环境基于Ambari 2.6.3.0-235。

运行

单docker快速体验

docker run --name dolphinscheduler -d -p 8888:8888 gary0416/dolphinscheduler:1.1.0

docker包含了zk、mysql及DS的组件。

多docker支持多节点高可用

依赖:本机需要在ambari上安装过client,需要其中的spark2-client。

docker构建基于:https://github.com/gary0416/incubator-dolphinscheduler/tree/test-docker

目前限制:同一台服务器上不支持起两个worker,因为grpc端口冲突,代码里写死的,不可配置。 其他组件理论上支持同节点起多个相同组件,未详细验证,但还是建议HA时分布在不同节点上,避免单点故障。

此版本docker只是满足使用,没有最优(例如:1.基础镜像是否可以不用ubuntu18。2.通过环境变量前缀约定,实现修改对应配置文件,类似https://github.com/gary0416/docker-kafka/blob/master/start-kafka.sh#L102。3.一些其他细节),所以未提交PR,待DS版本继续完善再跟进。

worker-extra-init.sh可以做额外初始化工作,例如:

#!/bin/bash
# support spark,mount hdp spark-client dir with ro mode
# to prevent changing JAVA_HOME var in spark-env.sh,manually setting env.
# load-spark-env.sh need SPARK_ENV_LOADED
# .escheduler_env.sh content:
#export HADOOP_HOME=${HADOOP_HOME:-/usr/hdp/2.6.3.0-235/hadoop}
#export HADOOP_CONF_DIR=/opt/escheduler/conf
#export SPARK_HOME1=/opt/soft/spark1
#export SPARK_HOME2=/opt/spark2-client
#export PYTHON_HOME=/opt/soft/python
#export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
#export HIVE_HOME=/opt/soft/hive
#export FLINK_HOME=/opt/soft/flink
#export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$SPARK_HOME2/bin:$PYTHON_HOME:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH:$FLINK_HOME/bin:$PATH
## new:
#export HDP_VERSION=2.6.3.0-235
#export SPARK_CONF_DIR=${SPARK_HOME}/conf
#export SPARK_ENV_LOADED=1
#export SPARK_LOG_DIR=/var/log/spark2
#export SPARK_PID_DIR=/var/run/spark2
#export SPARK_DAEMON_MEMORY=1024m
#export SPARK_IDENT_STRING=$USER
#export SPARK_NICENESS=0
ln -fs /opt/spark2-client/bin/spark-submit /usr/bin/spark-submit && mkdir -p /usr/hdp/2.6.3.0-235/hadoop/lib

# 自动创建测试租户用户
useradd gary && mkdir -p /opt/test/gary && chown gary /opt/test/gary

使用时需替换下面环境变量中的IP,指向实际的节点IP。Zookeeper和MySQL可根据实际情况修改。

version: '3'

networks:
  dolphinscheduler:
    driver: bridge

services:
  mysql:
    container_name: dolphinscheduler-mysql
    image: mysql:5.7.25
    restart: always
    networks:
      - dolphinscheduler
    environment:
      MYSQL_DATABASE: escheduler
      MYSQL_ROOT_PASSWORD: root@123
    command: [
      '--bind-address=0.0.0.0',
      '--character-set-server=utf8mb4',
      '--collation-server=utf8mb4_unicode_ci',
      '--default-time-zone=+8:00'
    ]
    ports:
      - 3306:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
  zoo1:
    container_name: dolphinscheduler-zoo1
    image: zookeeper:3.4.14
    restart: always
    hostname: zoo1
    networks:
      - dolphinscheduler
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=0.0.0.0:2888:3888
    volumes:
      - ./data/zoo1/data:/data
      - ./data/zoo1/datalog:/datalog
  dolphinscheduler-ui:
    container_name: dolphinscheduler-ui
    image: gary0416/dolphinscheduler-ui:test-20191011-1831
    restart: always
    networks:
      - dolphinscheduler
    environment:
      DS_PROXY: 192.168.xx.xx:12345
    ports:
      - 8888:8888
  dolphinscheduler-api:
    container_name: dolphinscheduler-api
    image: gary0416/dolphinscheduler-api:test-20191011-1831
    restart: always
    networks:
      - dolphinscheduler
    depends_on:
      - mysql
      - zoo1
    environment:
      ZK_HOST: 192.168.xx.xx
      ZK_PORT: 2181
      MYSQL_HOST: 192.168.xx.xx
      MYSQL_PORT: 3306
      MYSQL_ROOT_PWD: root@123
      ESZ_DB: escheduler
    command: api
    ports:
      - 12345:12345
    volumes:
      - ./data/logs:/opt/escheduler/logs
      - ../../conf/escheduler/conf:/opt/escheduler/conf:ro
  dolphinscheduler-master:
    container_name: dolphinscheduler-master
    image: gary0416/dolphinscheduler-common:test-20191011-1831
    restart: always
    networks:
      - dolphinscheduler
    depends_on:
      - dolphinscheduler-api
    environment:
      ZK_HOST: 192.168.xx.xx
      ZK_PORT: 2181
    command: master
    ports:
      - 5566:5566
    volumes:
      - ./data/logs:/opt/escheduler/logs
      - ../../conf/escheduler/conf:/opt/escheduler/conf:ro
  dolphinscheduler-worker:
    container_name: dolphinscheduler-worker
    image: gary0416/dolphinscheduler-common:test-20191011-1831
    restart: always
    network_mode: host
    depends_on:
      - dolphinscheduler-api
    environment:
      ZK_HOST: 192.168.xx.xx
      ZK_PORT: 2181
    command: worker
    ports:
      - 7788:7788
      - 50051:50051
    volumes:
      - ./data/logs:/opt/escheduler/logs
      - ../../conf/escheduler/conf:/opt/escheduler/conf:ro
      - ../../multi/docker-compose/worker/worker-extra-init.sh:/opt/escheduler/script/worker-extra-init.sh
      - /usr/hdp/current/spark2-client:/opt/spark2-client:ro
      - /etc/spark2/2.6.3.0-235/0:/etc/spark2/2.6.3.0-235/0:ro
  dolphinscheduler-alert:
    container_name: dolphinscheduler-alert
    image: gary0416/dolphinscheduler-common:test-20191011-1831
    restart: always
    networks:
      - dolphinscheduler
    environment:
      ALERT_SERVER_STARTUP_DELAY: 180
    command: alert
    ports:
      - 7789:7789
    volumes:
      - ./data/logs:/opt/escheduler/logs
      - ../../conf/escheduler/conf:/opt/escheduler/conf:ro

初始化

HDFS:

su - hdfs
hdfs dfs -mkdir /escheduler
hdfs dfs -chown escheduler:escheduler /escheduler

common.properties:

  • res.upload.startup.type需设置成HDFS(默认NONE不支持文件上传,fs没有初始化,而ResourcesService.verifyResourceName调用HadoopUtils.exists,会NPE)

nginx - default.conf:

  • client_max_body_size 1024m;否则上传大的fat jar时报错413 Request Entity Too Large

使用备忘

  • 架构图https://user-images.githubusercontent.com/48329107/62609545-8f973480-b934-11e9-9a58-d8133222f14d.png
  • 本机测试可以不起logger(界面点击查看日志,api服务器通过gRPC访问worker节点取日志)和alert(邮件通知)服务
  • 新建队列(对应yarn队列)、worker分组、租户、项目、给用户授权项目
  • 安全性:项目、UDF、数据源,默认可见自己租户的,用admin授权给其它租户(新版已删除),授权后可删改,无只读。
  • 执行SQL一般用于修改数据,如果是select,则会邮件形式发送查询结果
  • hive数据源,zk方式,端口需2181。官方文档截图的10000有误
  • shell执行用户为租户code,会自动新建租户同名用户(组为root)。如果有写文件,需确保有权限
  • worker分组Default表示可以在任一worker上执行
  • hdfs HA,俩xml放conf下
  • 租户即大数据的用户
  • 使用时间参数需要先在全局里定义,不能直接使用。其实allParamMap是有默认3个时间参数的,cn/escheduler/common/utils/ParameterUtils.java:140判断如果是$开头的,才会放到resolveMap里,最后作为实际参数。所以需要重命名或转换下,让值是$开头,才能在任务中获取到参数。
  • 同一父节点的两个子流程不是并行的
  • $[MMdd-5]这种依赖于system.datetime,否则是当前时间(ParameterUtils.java:60),可通过自定义参数system.datetime=${system.biz.date}0000000赋值成业务时间。参数这里做了些易用性的修改,见https://github.com/apache/incubator-dolphinscheduler/pull/913,暂未merge
  • Hive SQL里不支持$[]的替换,需要写在自定义参数中,例如param.dd in varchar $[dd]
  • Spark目前只有程序参数支持时间参数,–conf等其他参数不支持,修改方法在分支spark-other-params-missing-local-param,使用场景较少,留做备用,未提交PR
  • 任务是否可以执行,会检查系统资源(OSUtils.java:277),master和worker目前相同:load不超过cpu核数乘以2(master.max.cpuload.avg=10)、可用内存大于总内存十分之一(master.reserved.memory=1)。否则等下一个轮寻间隔(1s)
  • 对单个节点补数,会依次执行后续子节点
  • DAG图太大(几十M时,一般几K),浏览器会打不开。用子流程拆开,缺点是流程实例里看不到每个环节的甘特图
  • 流程依赖只能算是一项额外检查,不满足条件时不运行,用子流程才能实现当A运行后,立即执行B。需要AB都上线,在A上设定时

补数

目前只是按天补,对于小时级的任务,无法通过cron表达式推算出运行时间

cn/escheduler/server/master/runner/MasterExecThread.java:202 目前是scheduleDate = DateUtils.getSomeDay(scheduleDate, 1)

cn/escheduler/common/utils/placeholder/BusinessTimeUtils.java:43 获取下一次运行时间schedulerService.previewSchedule(loginUser, projectName, schedule)

https://github.com/apache/incubator-dolphinscheduler/issues/245

对其中一个环节补2019-09-23到25号的过程:

curl 'http://localhost:8888/escheduler/projects/dev-test/executors/start-process-instance' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Origin: http://localhost:8888' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json, text/plain, */*' -H 'Referer: http://localhost:8888/' -H 'Cookie: sessionId=aff85882-0331-4e1f-b64d-409f267cbf1d; grafana_session=76d33dde4c53c0e6350724c0b6f7aed1; language=zh_CN' -H 'Connection: keep-alive' -H 'DNT: 1' --data 'processDefinitionId=2&scheduleTime=2019-09-23+00%3A00%3A00%2C2019-09-25+00%3A00%3A00&failureStrategy=CONTINUE&warningType=NONE&warningGroupId=0&execType=COMPLEMENT_DATA&startNodeList=echo+date+param&taskDependType=TASK_POST&runMode=RUN_MODE_SERIAL&processInstancePriority=MEDIUM&receivers=&receiversCc=&workerGroupId=-1' --compressed

会返回{"code":0,"msg":"success","data":null}

实际
INSERT INTO `escheduler`.`t_escheduler_command`(`id`, `command_type`, `process_definition_id`, `command_param`, `task_depend_type`, `failure_strategy`, `warning_type`, `warning_group_id`, `schedule_time`, `start_time`, `executor_id`, `dependence`, `update_time`, `process_instance_priority`, `worker_group_id`) VALUES (1, 5, 2, '{\"StartNodeNameList\":\"echo date param\",\"complementEndDate\":\"2019-09-25 00:00:00\",\"complementStartDate\":\"2019-09-23 00:00:00\"}', 2, 1, 0, 0, NULL, '2019-09-25 11:59:53', 2, NULL, '2019-09-25 11:59:53', 2, -1);

然后master每1秒轮寻,创建流程实例
cn/escheduler/server/master/runner/MasterSchedulerThread.java:87
插入t_escheduler_process_instance表

master查到后拆成task,前面只选了一个环节,所以是1个task,同时删除t_escheduler_command,错误的会转存到t_escheduler_error_command表,message列有错误原因,如变量循环引用。
插入t_escheduler_task_instance表

task少量信息放queue(存在zk里/escheduler/tasks_queue/2_1_2_1_-1,无数据,文档写压测过百万数据)

worker cn/escheduler/server/worker/runner/FetchTaskThread.java:146从队列取,加锁
执行cn/escheduler/server/worker/runner/TaskScheduleThread.java:92

常见错误

无法上传UDF

后台空指针cn.escheduler.api.service.ResourcesService.verifyResourceName(ResourcesService.java:445),因为tenantCode为空。

需先新建租户tenant-dev,然后在用户管理里,修改租户、队列,或直接新建用户。

无法查看执行日志

其实在/opt/escheduler/logs/3/1下,新版已修复(注意1.1.0时,dev和1.1.0tag的配置不一致,需修改Logback)

日志时区错误

已修改dockerfile,注意同时修改timezone和localtime。

遗留问题

权限不严格

  • 在保存dag时,可选其它租户。
  • 选流程依赖时,可选其他用户的项目。

不能重复

  • 不同租户,UDF文件名不能重复,尽管在不同的HDFS目录下。
  • 不同租户,UDF函数名不能重复,尽管是临时函数。

即,测试环境的SQL,与正式环境的SQL不一致,需修改函数名,除非两套环境。

任务kill无效

kill后仍在运行。