容器化开发调试

    • Docker: 一种容器化技术,用于支创建和使用 Linux 容器;
    • Kubernetes: 一种可自动部署和管理 Linux 容器的开源平台,Kubernetes 还整合了网络、存储、安全性、遥测和其他服务,提供了全面的基于容器的基础设施;
    • KinD: 一个使用Docker容器作为 “Kubernetes节点” 来运行本地 Kubernetes 集群的工具;
    • Helm: Kubernetes 上一个开源的包管理工具, 通过 Helm 命令行工具和安装包(Chart)来管理 Kubernetes 上的用户资源;

    • Docker, 最低版本 v20.10.8+

    • , 最低版本 v1.21.0+
    • Helm, 最低版本 v3.0.0+.
    • , 最低版本 v0.11.0+.

    Helm 是 Kubernetes 上一个开源的包管理工具,Helm 最初的目标是为用户提供一种更好的方式来管理在 Kubernetes 上创建的所有 Kubernetes YAML 文件。Helm 使用 Charts 这个方式来解决上述问题,Chart 是一组文本文件,使用 Helm 模版语言编写,用来描述一个或者多个 Kubernetes 资源文件,Chart 直接依赖或者引用其他 Chart. 在使用 Charts 时,用户需要提供一个 变量文件,Helm 使用这个变量文件中定义的变量来渲染相应的 Chart, 生产 Kubernetes YAML 文件, 然后调用 Kubernetes api 提交到 Kubernetes 上。每一个发布到 Kubernetes 的 Charts 被称为 Release,一个 Chart 通常可以被多次安装到同一个集群中,而每次安装时,都会创建一个新的 Release。

    Helm 的安装方式比较简单,请参考官方文档进行安装: Installing Helm

    在本地创建一个 Kubernetes 测试环境是一个非常普遍的需求,Kubernetes 社区提供了多种解决方案,如 MiniKube 或 MicroK8s 等,KinD 是一个相对较新的工具,KinD 是 Kubernetes IN Docker 的缩写,顾名思义,它使用 Docker 托管节点来创建一个面向测试的 Kubernetes 集群。

    KinD 系统架构

    部署 KinD 也非常简单,请参考官方部署文档: , 部署 KinD 前请先安装 Docker .

    • linkis: Linkis 服务镜像,镜像中包含了 Apache Linkis 的所有组件的二进制包和各类脚本。
    • linkis-web: Linkis Web 控制台镜像,镜像中包含了 Apache Linkis Web 控制台的的二进制包和各类脚本,本镜像使用 nginx 作为 Web 服务器。
    • linkis-ldh: LDH 是一个面向测试用途的镜像,LDH 镜像提供了一套完整的、伪分布式模式的 Apache Hadoop 运行环境,包含了 HDFS, YARN, HIVE, Spark, Flink 和 Zookeeper, 可以很方便的在开发环境中拉起一个全真的 Hadoop 环境用来测试 Linkis 的功能。

    具体请参考: Linkis Docker 镜像打包.

    Linkis Helm Chart 是遵循 Helm Chart 规范开发的 Helm 安装包,在 linkis-dist/helm 目录下. 模块目录结构如下:

    本项目提供了一组工具脚本,用于快速创建一个用于开发测试的 Linkis 环境。在生产部署中,需要根据集群的实际情况,修改 values.yaml 文件,再使用 Helm CLI 进行部署。使用 Helm CLI 进行部署时,通常有如下两种比较常见的做法:

    1. 使用 helm install 命令直接部署。适用于非定制化的部署方式;
    2. 使用 helm template 命令选生成 Kubernetes YAML 文件,然后手动修改这些文件,添加自定义配置,然后使用kubectl apply命令进行部署。适用于需要定制 Linkis Helm Charts 不支持的 Kubernetes 特性的高阶用户, 如需要使用特定的 StorageClass 或者 PV 等;

    LDH 是一个面向测试用途的 Hadoop 集群镜像,它提供了一个伪分布式的 hadoop 集群,方便快速测试 On Hadoop 的部署模式。 这个镜像包含以下多个 hadoop 组件,LDH 中引擎的默认模式是 on-yarn 的。

    • Hadoop 2.7.2 , 包括 HDFS 和 YARN
    • Hive 2.3.3
    • Spark 2.4.3
    • Flink 1.12.2
    • ZooKeeper 3.5.9

    LDH 启动时会进行一些初始化操作,比如 format hdfs, 在 HDFS 上创建初始化目录等,这些操作定义在linkis-dist/docker/scripts/entry-point-ldh.sh这个文件中,添加、修改、删除一些初始化操作需要重新制作 LDH 镜像才能生效。

    另外,LDH 中的 Hive 组件依赖外部的 MySQL 实例,需要先部署 MySQL 实例才能使用 LDH 中的 Hive 组件。

    1. # 创建 KinD 集群,并部署 Linkis 和 LDH 实例
    2. $> sh ./scripts/create-kind-cluster.sh \
    3. && sh ./scripts/install-mysql.sh \
    4. && sh ./scripts/install-ldh.sh
    5. # 快速体验 LDH
    6. $> kubectl exec -it -n ldh $(kubectl get pod -n ldh -o jsonpath='{.items[0].metadata.name}') -- bash
    7. [root@ldh-96bdc757c-dnkbs /]# hdfs dfs -ls /
    8. Found 4 items
    9. drwxrwxrwx - root supergroup 0 2022-07-31 02:48 /completed-jobs
    10. drwxrwxrwx - root supergroup 0 2022-07-31 02:48 /spark2-history
    11. drwxrwxrwx - root supergroup 0 2022-07-31 02:49 /tmp
    12. drwxrwxrwx - root supergroup 0 2022-07-31 02:48 /user
    13. [root@ldh-96bdc757c-dnkbs /]# beeline -u jdbc:hive2://ldh.ldh.svc.cluster.local:10000/ -n hadoop
    14. Connecting to jdbc:hive2://ldh.ldh.svc.cluster.local:10000/
    15. Connected to: Apache Hive (version 2.3.3)
    16. Driver: Hive JDBC (version 2.3.3)
    17. Transaction isolation: TRANSACTION_REPEATABLE_READ
    18. Beeline version 2.3.3 by Apache Hive
    19. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> create database demo;
    20. No rows affected (1.306 seconds)
    21. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> use demo;
    22. No rows affected (0.046 seconds)
    23. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> create table t1 (id int, data string);
    24. No rows affected (0.709 seconds)
    25. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> insert into t1 values(1, 'linikis demo');
    26. WARNING: Hive-on-MR is deprecated in Hive 2 and may not be available in the future versions. Consider using a different execution engine (i.e. spark, tez) or using Hive 1.X releases.
    27. No rows affected (5.491 seconds)
    28. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> select * from t1;
    29. +--------+---------------+
    30. | t1.id | t1.data |
    31. +--------+---------------+
    32. | 1 | linikis demo |
    33. +--------+---------------+
    34. 1 row selected (0.39 seconds)
    35. 0: jdbc:hive2://ldh.ldh.svc.cluster.local:100> !q
    36. [root@ldh-96bdc757c-dnkbs /]# spark-sql
    37. 22/07/31 02:53:18 INFO hive.metastore: Trying to connect to metastore with URI thrift://ldh.ldh.svc.cluster.local:9083
    38. 22/07/31 02:53:18 INFO hive.metastore: Connected to metastore.
    39. ...
    40. 22/07/31 02:53:19 INFO spark.SparkContext: Running Spark version 2.4.3
    41. 22/07/31 02:53:19 INFO spark.SparkContext: Submitted application: SparkSQL::10.244.0.6
    42. ...
    43. 22/07/31 02:53:27 INFO yarn.Client: Submitting application application_1659235712576_0001 to ResourceManager
    44. 22/07/31 02:53:27 INFO impl.YarnClientImpl: Submitted application application_1659235712576_0001
    45. 22/07/31 02:53:27 INFO cluster.SchedulerExtensionServices: Starting Yarn extension services with app application_1659235712576_0001 and attemptId None
    46. 22/07/31 02:53:28 INFO yarn.Client: Application report for application_1659235712576_0001 (state: ACCEPTED)
    47. ...
    48. 22/07/31 02:53:36 INFO yarn.Client: Application report for application_1659235712576_0001 (state: RUNNING)
    49. ...
    50. Spark master: yarn, Application Id: application_1659235712576_0001
    51. 22/07/31 02:53:46 INFO thriftserver.SparkSQLCLIDriver: Spark master: yarn, Application Id: application_1659235712576_0001
    52. spark-sql> use demo;
    53. 22/07/31 02:58:02 INFO thriftserver.SparkSQLCLIDriver: Time taken: 0.074 seconds
    54. spark-sql> select * from t1;
    55. ...
    56. 1 linikis demo
    57. 2 linkis demo spark sql
    58. Time taken: 3.352 seconds, Fetched 2 row(s)
    59. spark-sql> quit;
    60. [root@ldh-96bdc757c-dnkbs /]# zkCli.sh
    61. Connecting to localhost:2181
    62. Welcome to ZooKeeper!
    63. JLine support is enabled
    64. WATCHER::
    65. WatchedEvent state:SyncConnected type:None path:null
    66. [zk: localhost:2181(CONNECTED) 0] get -s /zookeeper/quota
    67. cZxid = 0x0
    68. ctime = Thu Jan 01 00:00:00 UTC 1970
    69. mZxid = 0x0
    70. mtime = Thu Jan 01 00:00:00 UTC 1970
    71. pZxid = 0x0
    72. cversion = 0
    73. dataVersion = 0
    74. aclVersion = 0
    75. ephemeralOwner = 0x0
    76. dataLength = 0
    77. numChildren = 0
    78. [zk: localhost:2181(CONNECTED) 1] quit
    79. # 以 per-job cluster 模式启动 Flink 作业
    80. [root@ldh-96bdc757c-dnkbs /]# HADOOP_CLASSPATH=`hadoop classpath` flink run -t yarn-per-job /opt/ldh/current/flink/examples/streaming/TopSpeedWindowing.jar
    81. # 以 session 模式启动 Flink 作业,
    82. # Flink session 在 LDH Pod 启动时会被启动了一个.
    83. [root@ldh-96bdc757c-dnkbs /]# flink run /opt/ldh/current/flink/examples/streaming/TopSpeedWindowing.jar
    84. Executing TopSpeedWindowing example with default input data set.
    85. Use --input to specify file input.
    86. Printing result to stdout. Use --output to specify output path.
    87. ...

    Linkis 项目默认使用的 KinD 集群描述文件是linkis-dist/helm/scripts/resources/kind-cluster.yaml, 默认会创建一个包含一个节点的 KinD 集群。打开文件中注释的内容可以添加多个节点。

    ⚠️注意,KinD 集群仅用于测试用途。

    1. # linkis-dist/helm/scripts/resources/kind-cluster.yaml
    2. kind: Cluster
    3. apiVersion: kind.x-k8s.io/v1alpha4
    4. nodes:
    5. - role: control-plane
    6. extraMounts:
    7. - hostPath: ${KIND_CLUSTER_HOST_PATH} # 指向开发机上的一个目录。这个目录会被映射到 Kind Node 容器中的 `/data` 目录,
    8. # Linkis Helm Charts 默认会使用这个目录作为数据目录挂载到各个 Linkis 组件的
    9. # Pod 中。当 Linkis 使用 Local 模式进行部署时,所有组件实际上使用了开发机上的
    10. # 同一个目录,就和在同一台机器上一样,从而模拟了 Local 模式的行为。当使用
    11. # On-Hadoop 模式进行部署时,这个目录不会被使用。
    12. containerPath: /data
    13. # - role: worker # 打开注释可以添加2个 KinD 节点。添加 KinD 节点会增加加载 Docker 镜像到 KinD
    14. # # 集群的时间,所以默认不打开。
    15. # extraMounts:
    16. # - hostPath: ${KIND_CLUSTER_HOST_PATH}
    17. # containerPath: /data
    18. # - role: worker
    19. # extraMounts:
    20. # - hostPath: ${KIND_CLUSTER_HOST_PATH}
    21. # containerPath: /data

    下面介绍使用 Linkis 容器化组件开发调试的步骤(目前仅支持 Linux 和 MacOS)。在进行本步骤前请确认如下事项:

    1. 开发机上是否已经安装了 Docker 引擎
    2. 开发机上是否已经安装了 Helm
    3. 开发机上是否已经安装了 KinD
    4. 是否已经按照 所述的方式制作了 Linkis 镜像

    每个组件在容器内的 JVM 远程调试端口均为 5005, 这些端口会被映射到宿主机上的不同端口,具体如下:

    • mg-eureka: 5001
    • mg-gateway: 5002
    • ps-publicservice: 5004
    • cg-linkismanager: 5007
    • cg-entrance: 5008
    • cg-engineconnmanager: 5009
    • cg-engineplugin: 5010

    另外,Web Console 会被映射到宿主机上的 8087 端口,可以在浏览器上输入http://localhost:8087进行访问.

    1. $> ./scripts/remote-debug-proxy.sh start
    2. - starting port-forwad for [web] with mapping [local->8087:8087->pod] ...
    3. - starting port-forwad for [ps-publicservice] with mapping [local->5004:5005->pod] ...
    4. - starting port-forwad for [cg-linkismanager] with mapping [local->5007:5005->pod] ...
    5. - starting port-forwad for [cg-entrance] with mapping [local->5008:5005->pod] ...
    6. - starting port-forwad for [cg-engineconnmanager] with mapping [local->5009:5005->pod] ...
    7. - starting port-forwad for [cg-engineplugin] with mapping [local->5010:5005->pod] ..
    8. $> ./scripts/remote-debug-proxy.sh list
    9. user 10972 0.0 0.1 5052548 31244 s001 S 12:57AM 0:00.10 kubectl port-forward -n linkis pod/linkis-demo-cg-engineplugin-98bd6945-tsgjl 5010:5005 --address=0.0.0.0
    10. user 10970 0.0 0.1 5053060 30988 s001 S 12:57AM 0:00.12 kubectl port-forward -n linkis pod/linkis-demo-cg-engineconnmanager-659bf85689-ddvhw 5009:5005 --address=0.0.0.0
    11. user 10968 0.0 0.1 5054084 30428 s001 S 12:57AM 0:00.10 kubectl port-forward -n linkis pod/linkis-demo-cg-entrance-858f74c868-xrd82 5008:5005 --address=0.0.0.0
    12. user 10966 0.0 0.1 5053316 30620 s001 S 12:57AM 0:00.11 kubectl port-forward -n linkis pod/linkis-demo-cg-linkismanager-6f96f69b8b-ns6st 5007:5005 --address=0.0.0.0
    13. user 10964 0.0 0.1 5064092 31152 s001 S 12:57AM 0:00.10 kubectl port-forward -n linkis pod/linkis-demo-ps-publicservice-6bbf99fcd7-sc922 5004:5005 --address=0.0.0.0
    14. user 10962 0.0 0.1 5051012 31244 s001 S 12:57AM 0:00.12 kubectl port-forward -n linkis pod/linkis-demo-mg-gateway-68ddb8c547-xgvhn 5002:5005 --address=0.0.0.0
    15. user 10960 0.0 0.1 5053060 31312 s001 S 12:57AM 0:00.13 kubectl port-forward -n linkis pod/linkis-demo-mg-eureka-0 5001:5005 --address=0.0.0.0
    16. ...
    17. # 在调试完成后,可以使用如下命令停止端口转发
    18. $> ./scripts/remote-debug-proxy.sh stop
    19. - stopping port-forward for [web] with mapping [local->8087:8087->pod] ...
    20. - stopping port-forward for [mg-eureka] with mapping [local->5001:5005->pod] ...
    21. - stopping port-forward for [mg-gateway] with mapping [local->5002:5005->pod] ...
    22. - stopping port-forward for [ps-publicservice] with mapping [local->5004:5005->pod] ...
    23. - stopping port-forward for [cg-linkismanager] with mapping [local->5007:5005->pod] ...
    24. - stopping port-forward for [cg-entrance] with mapping [local->5008:5005->pod] ...
    25. - stopping port-forward for [cg-engineconnmanager] with mapping [local->5009:5005->pod] ...
    26. - stopping port-forward for [cg-engineplugin] with mapping [local->5010:5005->pod] ...

    在 IDE 上进行如下配置,开启远程调试:

    容器化开发调试 - 图2

    开启远程调试

    设置断点,提交一个作业,进行调试

    1. $> ./scripts/login-pod.sh mg-gateway
    2. - login [mg-gateway]'s bash ...
    3. bash-4.2$ ./bin/./linkis-cli -engineType shell-1 -codeType shell -code "echo \"hello\" " -submitUser hadoop -proxyUser hadoop
    4. =====Java Start Command=====
    5. exec /etc/alternatives/jre/bin/java -server -Xms32m -Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/linkis/logs/linkis-cli -XX:ErrorFile=/opt/linkis/logs/linkis-cli/ps_err_pid%p.log -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+DisableExplicitGC -classpath /opt/linkis/conf/linkis-cli:/opt/linkis/lib/linkis-computation-governance/linkis-client/linkis-cli/*:/opt/linkis/lib/linkis-commons/public-module/*: -Dconf.root=/etc/linkis-conf -Dconf.file=linkis-cli.properties -Dlog.path=/opt/linkis/logs/linkis-cli -Dlog.file=linkis-client..log.20220925171540947077800 org.apache.linkis.cli.application.LinkisClientApplication '-engineType shell-1 -codeType shell -code echo "hello" -submitUser hadoop -proxyUser hadoop'
    6. ...

    容器化开发调试 - 图4

    调试完成后,可以使用如下命令清理整个环境:

    1. $> kubectl logs -n linkis linkis-demo-cg-engineconnmanager-659bf85689-ddvhw -f
    2. + RUN_IN_FOREGROUND=true
    3. + /opt/linkis/sbin/linkis-daemon.sh start cg-engineconnmanager
    4. Start to check whether the cg-engineconnmanager is running
    5. Start server, startup script: /opt/linkis/sbin/ext/linkis-cg-engineconnmanager
    6. =====Java Start Command=====
    7. java -DserviceName=linkis-cg-engineconnmanager -Xmx512M -XX:+UseG1GC -Xloggc:/var/logs/linkis/linkis-cg-engineconnmanager-gc.log -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -cp /etc/linkis-conf:/opt/linkis/lib/linkis-commons/public-module/*:/opt/linkis/lib/linkis-computation-governance/linkis-cg-engineconnmanager/* org.apache.linkis.ecm.server.LinkisECMApplication --eureka.instance.prefer-ip-address=true --eureka.instance.instance-id=${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} 2>&1
    8. OpenJDK 64-Bit Server VM warning: If the number of processors is expected to increase from one, then you should configure the number of parallel GC threads appropriately using -XX:ParallelGCThreads=N
    9. Listening for transport dt_socket at address: 5005
    10. 16:32:41.101 [main] INFO org.apache.linkis.common.conf.BDPConfiguration$ - ******************* Notice: The Linkis configuration file is linkis.properties ! *******************
    11. 16:32:41.130 [main] INFO org.apache.linkis.common.conf.BDPConfiguration$ - *********************** Notice: The Linkis serverConf file is linkis-cg-engineconnmanager.properties ! ******************
    12. 16:32:41.222 [main] INFO org.apache.linkis.LinkisBaseServerApp - Ready to start linkis-cg-engineconnmanager with args: --eureka.instance.prefer-ip-address=true
    13. --eureka.instance.instance-id=${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    14. ...

    使用./scripts/login-pod.sh <组件名>可以进入组件的 Pod 打开一个 Bash 实例进行交互式操作,其中组件名可以为:

    • cg-engineconnmanager
    • cg-engineplugin
    • cg-entrance
    • cg-linkismanager
    • mg-eureka
    • mg-gateway
    • ps-publicservice
    • web
    1. $> ./scripts/login-pod.sh cg-engineconnmanager
    2. - login [cg-engineconnmanager]'s bash ...
    3. bash-4.2$ pwd
    4. /opt/linkis
    5. bash-4.2$ env |grep LINKIS
    6. LINKIS_DEMO_PS_PUBLICSERVICE_SERVICE_HOST=127.0.0.1
    7. LINKIS_DEMO_CG_LINKISMANAGER_PORT_9101_TCP_PROTO=tcp
    8. LINKIS_DEMO_WEB_PORT_8087_TCP_PORT=8087
    9. ...
    10. LINKIS_CLIENT_CONF_DIR=/etc/linkis-conf
    11. bash-4.2$ ps aux |grep linkis
    12. hadoop 1 0.0 0.0 11708 2664 ? Ss 16:32 0:00 /bin/bash /opt/linkis/sbin/linkis-daemon.sh start cg-engineconnmanager
    13. hadoop 10 0.0 0.0 11708 2624 ? S 16:32 0:00 sh /opt/linkis/sbin/ext/linkis-cg-engineconnmanager
    14. hadoop 11 0.0 0.0 11712 2536 ? S 16:32 0:00 sh /opt/linkis/sbin/ext/linkis-common-start
    15. hadoop 12 4.0 3.2 4146404 400084 ? Sl 16:32 0:35 java -DserviceName=linkis-cg-engineconnmanager -Xmx512M -XX:+UseG1GC -Xloggc:/var/logs/linkis/linkis-cg-engineconnmanager-gc.log -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -cp /etc/linkis-conf:/opt/linkis/lib/linkis-commons/public-module/*:/opt/linkis/lib/linkis-computation-governance/linkis-cg-engineconnmanager/* org.apache.linkis.ecm.server.LinkisECMApplication --eureka.instance.prefer-ip-address=true --eureka.instance.instance-id=${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    16. bash-4.2$ exit