This is an automated email from the ASF dual-hosted git repository.
sunchao pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new a73c92179d0d [SPARK-58202][K8S] Skip driver containerPort declaration
when the port is 0
a73c92179d0d is described below
commit a73c92179d0db3cf436a976d43f5f5933402c412
Author: zhengchenyu <[email protected]>
AuthorDate: Tue Aug 18 22:07:44 2026 -0700
[SPARK-58202][K8S] Skip driver containerPort declaration when the port is 0
### What changes were proposed in this pull request?
`BasicDriverFeatureStep` skips `containerPort` entries whose port is 0.
### Why are the changes needed?
When we use host network, we want to resolve port conflicts by binding port
0. But setting `spark.driver.port` / `spark.driver.blockManager.port` /
`spark.ui.port` / `spark.connect.grpc.binding.port` to 0 makes driver Pod
creation fail. The error log is as follows:
```
Exception in thread "main"
io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: POST
at: https://XX.XX.XX.XX:443/api/v1/namespaces/default/pods. Message: Pod
"xxx-driver" is invalid: [spec.containers[0].ports[0].containerPort: Required
value, spec.containers[0].ports[1].containerPort: Required value ...
```
BasicExecutorFeatureStep already skips the entry when the port is 0. The
driver side should follow the same pattern.
> Note: DriverServiceFeatureStep still builds all four ServicePort entries
without a port != 0 filter, we will fix this in
https://github.com/apache/spark/pull/57350. For now, we can use below
configuration to avoid this problem.
>```
>spark.kubernetes.driver.pod.excludedFeatureSteps=org.apache.spark.deploy.k8s.features.DriverServiceFeatureStep
>spark.kubernetes.executor.useDriverPodIP=true
>```
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
* Unit tests in `BasicDriverFeatureStepSuite`.
* Test in kubernetes cluster.
Closes #57349 from zhengchenyu/SPARK-58202.
Authored-by: zhengchenyu <[email protected]>
Signed-off-by: Chao Sun <[email protected]>
(cherry picked from commit 1591e2f5bfc61f94c1fe846e499b63eb7d60e7fc)
Signed-off-by: Chao Sun <[email protected]>
---
.../k8s/features/BasicDriverFeatureStep.scala | 34 ++++++++-----------
.../k8s/features/BasicDriverFeatureStepSuite.scala | 38 ++++++++++++++++++++++
2 files changed, 52 insertions(+), 20 deletions(-)
diff --git
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
index 579e5baeffda..3201ab48e86e 100644
---
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
+++
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
@@ -100,30 +100,24 @@ private[spark] class BasicDriverFeatureStep(conf:
KubernetesDriverConf)
val driverUIPort = SparkUI.getUIPort(conf.sparkConf)
val driverSparkConnectServerPort =
conf.sparkConf.getInt(CONNECT_GRPC_BINDING_PORT,
DEFAULT_SPARK_CONNECT_SERVER_PORT)
+ // 0 is invalid as kubernetes containerPort request, we shall leave it
unmounted.
+ val driverContainerPorts = Seq(
+ DRIVER_PORT_NAME -> driverPort,
+ BLOCK_MANAGER_PORT_NAME -> driverBlockManagerPort,
+ UI_PORT_NAME -> driverUIPort,
+ SPARK_CONNECT_SERVER_PORT_NAME -> driverSparkConnectServerPort
+ ).collect { case (name, port) if port != 0 =>
+ new ContainerPortBuilder()
+ .withName(name)
+ .withContainerPort(port)
+ .withProtocol("TCP")
+ .build()
+ }
val driverContainer = new ContainerBuilder(pod.container)
.withName(Option(pod.container.getName).getOrElse(DEFAULT_DRIVER_CONTAINER_NAME))
.withImage(driverContainerImage)
.withImagePullPolicy(conf.imagePullPolicy)
- .addNewPort()
- .withName(DRIVER_PORT_NAME)
- .withContainerPort(driverPort)
- .withProtocol("TCP")
- .endPort()
- .addNewPort()
- .withName(BLOCK_MANAGER_PORT_NAME)
- .withContainerPort(driverBlockManagerPort)
- .withProtocol("TCP")
- .endPort()
- .addNewPort()
- .withName(UI_PORT_NAME)
- .withContainerPort(driverUIPort)
- .withProtocol("TCP")
- .endPort()
- .addNewPort()
- .withName(SPARK_CONNECT_SERVER_PORT_NAME)
- .withContainerPort(driverSparkConnectServerPort)
- .withProtocol("TCP")
- .endPort()
+ .addAllToPorts(driverContainerPorts.asJava)
.addNewEnv()
.withName(ENV_SPARK_USER)
.withValue(Utils.getCurrentUserName())
diff --git
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
index 70e2239205ae..fce22a28c6d0 100644
---
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
+++
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
@@ -475,6 +475,44 @@ class BasicDriverFeatureStepSuite extends SparkFunSuite {
assert(amountAndFormat(limits("memory")) === "5500Mi")
}
+ test("SPARK-58202: containerPort entries are skipped when the corresponding
Spark port is 0") {
+ val sparkConf = new SparkConf()
+ .set(CONTAINER_IMAGE, "spark-driver:latest")
+ .set(KUBERNETES_DRIVER_POD_NAME, "spark-driver-pod")
+ .set(DRIVER_PORT, 0)
+ .set(DRIVER_BLOCK_MANAGER_PORT, 0)
+ .set(UI_PORT, 0)
+ .set(CONNECT_GRPC_BINDING_PORT, "0")
+ val kubernetesConf: KubernetesDriverConf =
KubernetesTestConf.createDriverConf(
+ sparkConf = sparkConf,
+ environment = DRIVER_ENVS,
+ annotations = DRIVER_ANNOTATIONS)
+
+ val featureStep = new BasicDriverFeatureStep(kubernetesConf)
+ val configuredPod = featureStep.configurePod(SparkPod.initialPod())
+ assert(configuredPod.container.getPorts.isEmpty)
+ }
+
+ test("SPARK-58202: containerPort entries include only ports with non-zero
values") {
+ val sparkConf = new SparkConf()
+ .set(CONTAINER_IMAGE, "spark-driver:latest")
+ .set(KUBERNETES_DRIVER_POD_NAME, "spark-driver-pod")
+ .set(DRIVER_PORT, 9000)
+ .set(DRIVER_BLOCK_MANAGER_PORT, 0)
+ .set(UI_PORT, 4040)
+ .set(CONNECT_GRPC_BINDING_PORT, "0")
+ val kubernetesConf: KubernetesDriverConf =
KubernetesTestConf.createDriverConf(
+ sparkConf = sparkConf,
+ environment = DRIVER_ENVS,
+ annotations = DRIVER_ANNOTATIONS)
+
+ val featureStep = new BasicDriverFeatureStep(kubernetesConf)
+ val configuredPod = featureStep.configurePod(SparkPod.initialPod())
+ val portsByName = configuredPod.container.getPorts.asScala
+ .map(cp => cp.getName -> cp.getContainerPort).toMap
+ assert(portsByName === Map(DRIVER_PORT_NAME -> 9000, UI_PORT_NAME -> 4040))
+ }
+
def containerPort(name: String, portNumber: Int): ContainerPort =
new ContainerPortBuilder()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]