Skip to content

Conversation

@pan3793
Copy link
Member

@pan3793 pan3793 commented Apr 23, 2024

What changes were proposed in this pull request?

Ingress a kind of K8s resource to expose HTTP and HTTPS routes from outside the K8s cluster to services within the K8s cluster. It's a common use case that uses ingress to access Spark UI for Spark jobs running on K8s in cluster mode.

This PR aims to add such a built-in feature to allow users to enable Ingress for Spark jobs which running on K8s cluster mode.

State: functionality completed; manuelly tested; ready for review to collect feedback
TODO: UT and docs

Why are the changes needed?

Simplify Spark live UI access from outside of the K8s.

Does this PR introduce any user-facing change?

This PR adds a new feature, but is disabled by default.

How was this patch tested?

I will supply the unit tests later.

Maunnely tested on an internal K8s cluster.

$ build/sbt clean package -Pkubernetes
$ bin/docker-image-tool.sh -r ****** -b java_image_tag=17 -t SPARK-47954 build
$ bin/docker-image-tool.sh -r ****** -t SPARK-47954 push
$ SPARK_PREPEND_CLASSES=true bin/spark-submit \
	--master=k8s://https://***********.org:6443 \
	--deploy-mode cluster \
	--driver-class-path='/opt/spark/examples/jars/*' \
	--driver-memory=512m \
	--executor-memory=512m \
	--executor-cores=1 \
	--num-executors=1 \
	--conf spark.kubernetes.context='*****' \
	--conf spark.kubernetes.authenticate.driver.serviceAccountName=***** \
	--conf spark.kubernetes.namespace=spark \
	--conf spark.kubernetes.file.upload.path=hdfs://*****/spark-staging \
	--conf spark.kubernetes.container.image=******/spark:SPARK-47954 \
	--conf spark.kubernetes.driver.ingress.enabled=true \
	--conf spark.kubernetes.driver.ingress.host={{APP_ID}}.bigdata*********.com \
	--conf spark.kubernetes.driver.ingress.ingressClassName=contour \
	--class org.apache.spark.examples.SparkPi \
	spark-internal 1000
$ kubectl get ingress org-apache-spark-examples-sparkpi-a947e18f0c055185-driver-svc-ingress
NAME                                                                    CLASS     HOSTS                                                         ADDRESS       PORTS   AGE
org-apache-spark-examples-sparkpi-a947e18f0c055185-driver-svc-ingress   contour   spark-cbf851baf8f74a1aa64b94d6e1d1b350.bigdata*********.com   10.49.128.6   80      2m32s
image

Was this patch authored or co-authored using generative AI tooling?

No.

@pan3793 pan3793 marked this pull request as draft April 23, 2024 14:54
@mridulm
Copy link
Contributor

mridulm commented Apr 23, 2024

+CC @zhouyejoe

@pan3793 pan3793 marked this pull request as ready for review April 23, 2024 18:19
@pan3793
Copy link
Member Author

pan3793 commented Apr 23, 2024

cc @dongjoon-hyun @yaooqinn @LuciferYang @EnricoMi appreciate it if you could have a look on this idea

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't do that. This is a huge regression for the existing system by reducing the space of service name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense, we can generate the ingress name independently to avoid such regression

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is not a bug, please move this logInfo to outside of this PR with a different message.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of warning like this, please check at line 37 together and proceed with no-op.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return statement implies that we might have too complex code path here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we provide a way for Spark Job runs successfully even DriverIngressFeatureStep and its K8s resources fails? IIUC, if something goes wrong, the Spark job is not started, isn't it?

Copy link
Member Author

@pan3793 pan3793 Apr 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good point.

yes, any failure during resource creation will fail the job submission at the current stage, and I did see the ingress creation failure cases internally. we mitigate it by providing a switch to allow users to disable creating ingress for certain jobs by spark.kubernetes.driver.ingress.enabled.

currently, we create resources in batches,

// Refresh all pre-resources' owner references
try {
addOwnerReference(createdDriverPod, preKubernetesResources)
kubernetesClient.resourceList(preKubernetesResources: _*).forceConflicts().serverSideApply()
} catch {
case NonFatal(e) =>
kubernetesClient.pods().resource(createdDriverPod).delete()
kubernetesClient.resourceList(preKubernetesResources: _*).delete()
throw e
}
// setup resources after pod creation, and refresh all resources' owner references
try {
val otherKubernetesResources = resolvedDriverSpec.driverKubernetesResources ++ Seq(configMap)
addOwnerReference(createdDriverPod, otherKubernetesResources)
kubernetesClient.resourceList(otherKubernetesResources: _*).forceConflicts().serverSideApply()
} catch {
case NonFatal(e) =>
kubernetesClient.pods().resource(createdDriverPod).delete()
throw e
}

if we want to make some resources optional, we need to add a new batch to handle them independently. meanwhile, a new method may need to add in KubernetesFeatureConfigStep, something like

trait KubernetesFeatureConfigStep {
  ...
  def getAdditionalOptionalKubernetesResources(): Seq[HasMetadata] = Seq.empty
}

@dongjoon-hyun WDYT?

Copy link
Member Author

@pan3793 pan3793 Apr 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the other hand, silently ignoring a resource that the user requested may confuse users. for example, if something goes wrong during the spark UI start, we fail the job immediately.

Copy link
Member Author

@pan3793 pan3793 Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need a decision here.

option 1: provide a switch spark.kubernetes.driver.ingress.enabled to allow the user to explicitly enable/disable ingress, and fail the job if ingress creation failed.

pros:

  1. behavior consistent with spark.ui.enabled;
  2. if ingress creation failed, the user knows what happened rather than confusing why Spark UI is unavailable;
  3. code change is simple;

cons:

  1. ingress is actually an optional resource, technically, Spark could run well without ingress.

option 2: treat ingress as an optional resource, ignore ingress creation failure
pros:

  1. Keep the same stability for Spark jobs submission even with ingress enabled.

cons:

  1. ingress resource is not reliable even the user enable ingress explicitly
  2. we need to touch the developer interface to achieve such functionalities (there is one proposal above)

internally, we adopted option 1, and some users explicitly disabled the ingress because they didn't want the job submission to fail because of ingress creation failure.

@dongjoon-hyun @yaooqinn @mridulm @LuciferYang please let me know which one you are preferred.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To @pan3793 , I'm very careful about this PR because we consider Having K8s Ingress is more vulnerable than having Spark UI because it increases the boundary of exposure. Spark UI itself is not open to the other pods if you design to isolate Spark pod-to-pod communication carefully.

In addition, some K8s implementations (and their security backend) increases K8s Ingress creation. I expected a few minutes delays for only K8s ingress entity creations. It could mean a delay of Spark Job time too if we design to wait for K8s ingress.

Since we verified that the deletion is handled by K8s ownership correctly. Let's double-check the following.

  • What is the job start time for Option 1? There is no delay?
    • If there is a delay, is there a way to mitigate it?
    • If there is no delay because it is handled separately, what happens during the time gap after the job start already and K8s Ingress is still under construction. Are we in the same situation where Option 2's Cons 1?

Copy link
Member Author

@pan3793 pan3793 Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected a few minutes delays for only K8s ingress entity creations.

that surprises me, in our cluster, the overhead of ingress creation is negligible.

I tested with ingress enabled/disabled 5 times in internal cluster to measure the job submission time by spark.app.startTime - spark.app.submitTime. (based on the implementation of option 1)

ingress enabled: 20715, 24954, 17129, 16631, 16076
ingress disabled: 20246, 20262, 17176, 18633, 19962

the difference is majorly caused by Pod creation, ingress creation is negligible.

if the ingress creation is too heavy, option 2 is the only choice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we don't need this because KUBERNETES_INGRESS_HOST_PATTERN is mandatory always. Shall we remove this redudant configuration?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask when you want to override this? And, is it safe in terms of the security?

Copy link
Member Author

@pan3793 pan3793 Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internally, we only use path / with pathType ImplementationSpecific, let me hardcode it and make it configurable if someone has real request in the future

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that there is no owner setting here. Could you double-check if this increase the number of ingress monotonically? I'm wondering who and when this ingress is removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, OwnerReference is handled at

I checked that deleting the driver pod removes the ingress too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the confirmation.

Copy link
Member

@dongjoon-hyun dongjoon-hyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @pan3793 . I left a few comments.

@dongjoon-hyun
Copy link
Member

dongjoon-hyun commented Apr 29, 2024

Gentle ping, @pan3793 . It would be great if we can land this before Apache Spark 4.0.0-preview.

To @cloud-fan , just a question, when do you think you are going to cut 4.0.0-preview tag?

@pan3793
Copy link
Member Author

pan3793 commented Apr 29, 2024

@dongjoon-hyun sorry for late, I'm a little busy these days, will address comments soon

Copy link
Member

@dongjoon-hyun dongjoon-hyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please address this.

We don't need spark.kubernetes.driver.ingress.enabled because spark.kubernetes.driver.ingress.host is the same semantic.

@dongjoon-hyun
Copy link
Member

For the record, Apache Spark 4.0.0-preview is scheduled on next Monday.

@dongjoon-hyun
Copy link
Member

Just FYI, please take your time. We can target this for Apache Spark 4.0.0.

@github-actions
Copy link

We're closing this PR because it hasn't been updated in a while. This isn't a judgement on the merit of the PR in any way. It's just a way of keeping the PR queue manageable.
If you'd like to revive this PR, please reopen it and ask a committer to remove the Stale tag!

@github-actions github-actions bot added the Stale label Aug 21, 2024
@github-actions github-actions bot closed this Aug 22, 2024
@melin
Copy link

melin commented Apr 23, 2025

@pan3793 Is there any new progress in this pr?

To access the url: http://hostname:port/proxy/[applicationId], add conf: spark.driver.extraJavaOptions="-Dspark.ui.proxyBase=/proxy/applicationId"

example:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spark-app-ingress
  namespace: superior
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      - path: /proxy/spark-1745724044946
        pathType: Prefix
        backend:
          service:
            name: spark-1745724044946-driver-svc
            port:
              number: 4040

To access the url http://hostname:port/proxy/spark-174572404494 is redirected to the http://hostname:port/jobs。 Failed to access the UI.

sparkcontext initializes the ui, and the basepath is empty

image

@dongjoon-hyun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants