-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-7657][YARN] Add driver logs links in application UI, in cluster mode. #6166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1cf338f
402e8e4
50cdae3
c0de336
99fb1a3
629c1dc
346f4ea
6c5c285
4033725
537a2f7
0840a95
b3f9b9d
9e5c04b
943fc4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,10 +17,19 @@ | |
|
|
||
| package org.apache.spark.scheduler.cluster | ||
|
|
||
| import java.net.NetworkInterface | ||
|
|
||
| import scala.collection.JavaConverters._ | ||
|
|
||
| import org.apache.hadoop.yarn.api.records.NodeState | ||
| import org.apache.hadoop.yarn.client.api.YarnClient | ||
| import org.apache.hadoop.yarn.conf.YarnConfiguration | ||
|
|
||
| import org.apache.spark.SparkContext | ||
| import org.apache.spark.deploy.yarn.YarnSparkHadoopUtil | ||
| import org.apache.spark.deploy.yarn.YarnSparkHadoopUtil._ | ||
| import org.apache.spark.scheduler.TaskSchedulerImpl | ||
| import org.apache.spark.util.IntParam | ||
| import org.apache.spark.util.{IntParam, Utils} | ||
|
|
||
| private[spark] class YarnClusterSchedulerBackend( | ||
| scheduler: TaskSchedulerImpl, | ||
|
|
@@ -53,4 +62,70 @@ private[spark] class YarnClusterSchedulerBackend( | |
| logError("Application attempt ID is not set.") | ||
| super.applicationAttemptId | ||
| } | ||
|
|
||
| override def getDriverLogUrls: Option[Map[String, String]] = { | ||
| var yarnClientOpt: Option[YarnClient] = None | ||
| var driverLogs: Option[Map[String, String]] = None | ||
| try { | ||
| val yarnConf = new YarnConfiguration(sc.hadoopConfiguration) | ||
| val containerId = YarnSparkHadoopUtil.get.getContainerId | ||
| yarnClientOpt = Some(YarnClient.createYarnClient()) | ||
| yarnClientOpt.foreach { yarnClient => | ||
| yarnClient.init(yarnConf) | ||
| yarnClient.start() | ||
|
|
||
| // For newer versions of YARN, we can find the HTTP address for a given node by getting a | ||
| // container report for a given container. But container reports came only in Hadoop 2.4, | ||
| // so we basically have to get the node reports for all nodes and find the one which runs | ||
| // this container. For that we have to compare the node's host against the current host. | ||
| // Since the host can have multiple addresses, we need to compare against all of them to | ||
| // find out if one matches. | ||
|
|
||
| // Get all the addresses of this node. | ||
| val addresses = | ||
| NetworkInterface.getNetworkInterfaces.asScala | ||
| .flatMap(_.getInetAddresses.asScala) | ||
| .toSeq | ||
|
|
||
| // Find a node report that matches one of the addresses | ||
| val nodeReport = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a brief comment explaining what is happening here, what cases you are trying to cover? |
||
| yarnClient.getNodeReports(NodeState.RUNNING).asScala.find { x => | ||
| val host = x.getNodeId.getHost | ||
| addresses.exists { address => | ||
| address.getHostAddress == host || | ||
| address.getHostName == host || | ||
| address.getCanonicalHostName == host | ||
| } | ||
| } | ||
|
|
||
| // Now that we have found the report for the Node Manager that the AM is running on, we | ||
| // can get the base HTTP address for the Node manager from the report. | ||
| // The format used for the logs for each container is well-known and can be constructed | ||
| // using the NM's HTTP address and the container ID. | ||
| // The NM may be running several containers, but we can build the URL for the AM using | ||
| // the AM's container ID, which we already know. | ||
| nodeReport.foreach { report => | ||
| val httpAddress = report.getHttpAddress | ||
| // lookup appropriate http scheme for container log urls | ||
| val yarnHttpPolicy = yarnConf.get( | ||
| YarnConfiguration.YARN_HTTP_POLICY_KEY, | ||
| YarnConfiguration.YARN_HTTP_POLICY_DEFAULT | ||
| ) | ||
| val user = Utils.getCurrentUserName() | ||
| val httpScheme = if (yarnHttpPolicy == "HTTPS_ONLY") "https://" else "http://" | ||
| val baseUrl = s"$httpScheme$httpAddress/node/containerlogs/$containerId/$user" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hari and I discussed this offline a bit on how this works when you've got multiple containers on a node -- it is just a bit confusing so I suggested adding a comment here, something like: "The nodeReport gives us the httpAddress for the NodeManager, which may be shared by more than one container on that node. But we know we have the container for the driver because we use the containerId as well" |
||
| logDebug(s"Base URL for logs: $baseUrl") | ||
| driverLogs = Some( | ||
| Map("stderr" -> s"$baseUrl/stderr?start=0", "stdout" -> s"$baseUrl/stdout?start=0")) | ||
| } | ||
| } | ||
| } catch { | ||
| case e: Exception => | ||
| logInfo("Node Report API is not available in the version of YARN being used, so AM" + | ||
| " logs link will not appear in application UI", e) | ||
| } finally { | ||
| yarnClientOpt.foreach(_.close()) | ||
| } | ||
| driverLogs | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the
toMapnecessary, isn'tlogsalready a map?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
JsonProtocolreturnsmutable.MapwhileexecutorToLogUrlshas value typed asPredef.Map. We can either calltoMaphere or inJsonProtocolas you can see here: https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/util/JsonProtocol.scala#L845There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want me to change it in
JsonProtocol? I think it is fine here in this case, since this is where we are using it (unlike in the other case, where it needs to go into anExecutorInfo.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I think its fine as-is then, I was just curious