4

我想调试在AWS EMR集群上运行的Spark应用程序。如果我可以使用IntelliJ进行远程连接和调试,那将会很棒。我已经搜索,但发现很少。AWS EMR - IntelliJ远程调试Spark应用程序

是否有可能,如果有的话,有人可以指导我朝着正确的方向吗?

谢谢。

回答

5

首先,由于AWS EMR的众多错误和意外的使用情况,我会告诫你要做的事情基本上是不可能的。我强烈建议支付您可以运行工作的最大单一实例(它们的c4.8xlarge在负担得起,而x1.32xlarge为真正的疯狂!),并简单地在该实例中安装spark并运行您的工作。

先决条件

  • 您的VPC必须正确配置为允许任何连接到外部世界的。这意味着您的Internet网关工作正常。您可以通过启动具有EC2密钥对的群集,修改主服务器的安全组以允许来自您计算机的SSH连接(他们自然不会默认执行此操作)并尝试从您的计算机连接到主服务器来测试此情况。如果你不能这样做,你将无法进行调试。我甚至无法在没有额外配置的情况下在新的群集上满足这个先决条件!
  • 运行IntelliJ进行调试的机器必须可以从Internet访问。要测试此操作,请修改主实例的安全组以允许在端口5005上出站连接到您的机器。然后,在您的机器上运行nc -l 5005。 SSH到你的主人并尝试echo "test" | nc your_ip_address 5005。在您的机器终端上看到test之前,请勿继续。

的IntelliJ设置

创建一个新的远程配置。将调试器模式更改为侦听。命名配置并保存。当你点击调试时,它会等待连接。在该窗口中,您将看到“运行远程​​JVM的命令行参数”,读书是这样的:

-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=y 

您可以删除onthrowoncaught线像我一样。假设您的调试机器可以通过Internet访问24.13.242.141。假设它实际上是:

-agentlib:jdwp=transport=dt_socket,server=n,address=24.13.242.141:5005,suspend=y 

我们将用它来设置Spark进程的调试。

星火设置

有可调试两个过程:驾驶过程(运行在您的SparkContext实例化的代码)和执行过程。最终,您会将这些JVM选项传递给​​的特殊参数以使连接发生。为了调试驱动程序,使用

spark-submit --driver-java-options -agentlib:jdwp=transport=dt_socket,server=n,address=24.13.242.141:5005,suspend=y --class ... 

出于调试执行过程中,你可以使用一个配置选项:

spark-submit --conf "spark.executor.extraJavaOptions=-agentlib:jdwp=transport=dt_socket,server=n,address=24.13.242.141:5005,suspend=y" --class ... 

调试的执行者是多余的棘手,因为会有多个进程。你不能像你在IntelliJ中想象的那样真正地调试多个进程。此外,即使您声称可以,您也无法将AWS EMR中的执行程序数量限制为1。我相信如果其他执行程序会失败(当它们无法连接到您的调试会话时它们将会失败)。但这一步未经测试。

全部放在一起

可以修改参数​​既与SDK和Web控制台。请注意,在SDK中,您不应该尝试自己连接“参数” - 将它们作为数组项一样将它们传递给您。

为了调试驱动程序(同样用slave的安全组来调试执行程序),您需要修改主机的安全组。创建一个安全组,允许出站连接到您的调试器的IP地址和端口(即TCP Outbound到24.13.242.141:5005)。您应该使用该条目创建安全组,并使用AWS SDK(.withAdditionalMasterSecurityGroups(...))将其添加到主/从属作业流实例配置的安全组。我不确定如何从Web控制台执行此操作。

一些常见的问题

  • 确保使用摇篮生产与classpath "com.github.jengelman.gradle.plugins:shadow:1.2.4"插件阴影罐子。另外,启用Zip64。您会将:shadowJar任务的结果上载到S3,以便在AWS EMR上实际执行。
buildscript { 
    repositories { 
     mavenCentral() 
     maven { 
      url "https://plugins.gradle.org/m2/" 
     } 
    } 
    dependencies { 
     classpath "com.github.jengelman.gradle.plugins:shadow:1.2.4" 
    } 
} 

apply plugin: "com.github.johnrengelman.shadow" 

shadowJar { 
    zip64 true 
} 
  • 确保用--deploy-mode cluster--master yarn启动星火应用程序(基本上没有证件)。
  • 为了从EMR的驱动程序或执行程序内部访问S3,请执行而不是来修改sc.hadoopConfiguration()(例如,configuration.set("fs.s3n.impl", "org.apache.hadoop.fs.s3native.NativeS3FileSystem");)。不要配置这些属性! hadoop-aws默认情况下在EMR环境中正常工作,并具有自动设置的相应属性。
  • 将您的log4j日志选项设置为仅报告WARN及更高版本。在此SDK,你将有做到这一点:
.withConfigurations(new Configuration() 
    .withClassification("spark-log4j") 
    .addPropertiesEntry("log4j.rootCategory", "WARN, console")) 
  • 检查containers/applications_.../container.../stderr.gz日志中的错误你懒得调试之前!
  • 如果在您的容器日志中发现此错误,“WARN YarnClusterScheduler:初始作业未接受任何资源;请检查您的集群UI以确保工作人员已注册且具有足够资源”,请确保将配置属性maximizeResourceAllocationspark分类。
new Configuration() 
     .withClassification("spark") 
     .addPropertiesEntry("maximizeResourceAllocation", "true")) 
  • 不要忘记在驱动程序(sc.close())年底前关闭您的上下文。否则,纱线永远不会启动。欢迎无证。
  • 阴影JAR中的资源只能由与资源相同的“JAR”内的类加载。换句话说,不要使用ClassLoader.getSystemClassLoader()。如果class A通常在a.jar想要访问b.jar中的资源,并且class Bb.jar中的类,请使用B.class.getClassLoader().getResource...。此外,使用相对路径(在资源引用的开头省略正斜杠)。我会建议捕获NullPointerException并尝试这两种方法,这样无论它如何打包,您的JAR都能正常工作。
  • 如果您使用类实现Function接口和类似的类,请确保创建一个无参数构造函数来执行您可能依赖的所有初始化。 Spark对闭包和函数实例都使用Kryo序列化(而不是Java序列化),如果您忽略为特定于应用程序的初始化代码提供无参数构造函数(例如,从资源加载),则不会执行所有操作您期待的初始化。
+0

哇,非常感谢你这么详细的深入的答案 - 我会尝试什么似乎是一个徒劳的尝试很快:D – null

+0

我甚至不想尝试这个了。反正...非常好的回应 – Cristian