-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-25222][K8S] Improve container status logging #22215
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 3 commits
ebcbf05
842a0b3
dcb5b00
355e66d
6f6442f
4c39a81
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 |
|---|---|---|
|
|
@@ -16,7 +16,11 @@ | |
| */ | ||
| package org.apache.spark.deploy.k8s | ||
|
|
||
| import org.apache.spark.SparkConf | ||
| import scala.collection.JavaConverters._ | ||
|
|
||
| import io.fabric8.kubernetes.api.model.{ContainerStateRunning, ContainerStateTerminated, ContainerStateWaiting, ContainerStatus, Pod, Time} | ||
|
|
||
| import org.apache.spark.{SparkConf, SparkException} | ||
| import org.apache.spark.util.Utils | ||
|
|
||
| private[spark] object KubernetesUtils { | ||
|
|
@@ -60,4 +64,90 @@ private[spark] object KubernetesUtils { | |
| } | ||
|
|
||
| def parseMasterUrl(url: String): String = url.substring("k8s://".length) | ||
|
|
||
| def formatPairsBundle(pairs: Seq[(String, String)], indent: Int = 1) = { | ||
| // Use more loggable format if value is null or empty | ||
| val indentStr = "\t" * indent | ||
| pairs.map { | ||
| case (k, v) => s"\n$indentStr $k: ${Option(v).filter(_.nonEmpty).getOrElse("N/A")}" | ||
| }.mkString("") | ||
| } | ||
|
|
||
| /** | ||
| * Given a pod output a human readable representation of its state | ||
|
||
| * @param pod Pod | ||
| * @return Human readable pod state | ||
| */ | ||
| def formatPodState(pod: Pod): String = { | ||
| val details = Seq[(String, String)]( | ||
| // pod metadata | ||
| ("pod name", pod.getMetadata.getName), | ||
| ("namespace", pod.getMetadata.getNamespace), | ||
| ("labels", pod.getMetadata.getLabels.asScala.mkString(", ")), | ||
| ("pod uid", pod.getMetadata.getUid), | ||
| ("creation time", formatTime(pod.getMetadata.getCreationTimestamp)), | ||
|
|
||
| // spec details | ||
| ("service account name", pod.getSpec.getServiceAccountName), | ||
| ("volumes", pod.getSpec.getVolumes.asScala.map(_.getName).mkString(", ")), | ||
| ("node name", pod.getSpec.getNodeName), | ||
|
|
||
| // status | ||
| ("start time", formatTime(pod.getStatus.getStartTime)), | ||
| ("container images", | ||
| pod.getStatus.getContainerStatuses | ||
| .asScala | ||
| .map(_.getImage) | ||
| .mkString(", ")), | ||
| ("phase", pod.getStatus.getPhase), | ||
| ("status", pod.getStatus.getContainerStatuses.asScala.map { status => | ||
| Seq( | ||
|
||
| ("Container name", status.getName), | ||
| ("Container image", status.getImage)) ++ | ||
| containerStatusDescription(status) | ||
| }.map(p => formatPairsBundle(p, 2)).mkString("\n\n")) | ||
| ) | ||
|
|
||
| formatPairsBundle(details) | ||
| } | ||
|
|
||
| def containersDescription(p: Pod): String = { | ||
| p.getStatus.getContainerStatuses.asScala.map { status => | ||
| Seq( | ||
| ("Container name", status.getName), | ||
|
||
| ("Container image", status.getImage)) ++ | ||
| containerStatusDescription(status) | ||
| }.map(p => formatPairsBundle(p, 1)).mkString("\n\n") | ||
| } | ||
|
|
||
| def containerStatusDescription(containerStatus: ContainerStatus) | ||
| : Seq[(String, String)] = { | ||
| val state = containerStatus.getState | ||
| Option(state.getRunning) | ||
rvesse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .orElse(Option(state.getTerminated)) | ||
| .orElse(Option(state.getWaiting)) | ||
| .map { | ||
| case running: ContainerStateRunning => | ||
| Seq( | ||
| ("Container state", "Running"), | ||
|
||
| ("Container started at", formatTime(running.getStartedAt))) | ||
| case waiting: ContainerStateWaiting => | ||
| Seq( | ||
| ("Container state", "Waiting"), | ||
| ("Pending reason", waiting.getReason)) | ||
| case terminated: ContainerStateTerminated => | ||
| Seq( | ||
| ("Container state", "Terminated"), | ||
| ("Container started at", formatTime(terminated.getStartedAt)), | ||
| ("Container finished at", formatTime(terminated.getFinishedAt)), | ||
| ("Exit code", terminated.getExitCode.toString), | ||
| ("Termination reason", terminated.getReason)) | ||
| case unknown => | ||
| throw new SparkException(s"Unexpected container status type ${unknown.getClass}.") | ||
| }.getOrElse(Seq(("Container state", "N/A"))) | ||
| } | ||
|
|
||
| def formatTime(time: Time): String = { | ||
| if (time != null) time.getTime else "N/A" | ||
| } | ||
| } | ||
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.
Can we prefer space-based indentation? Curious as to whether others have an opinion about this.
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.
I just preserved the original codes choice here, I would happily change to spaces if preferred