diff --git a/docs/source/spark/index.rst b/docs/source/spark/index.rst index 84507f899..555a52fba 100644 --- a/docs/source/spark/index.rst +++ b/docs/source/spark/index.rst @@ -52,13 +52,28 @@ using ``num_executors`` and ``resources_per_executor``. .. note:: - Batch job submission requires the ``spark-operator-spark`` ServiceAccount to - exist in the target namespace, with the required SparkApplication RBAC - permissions bound to it. Otherwise, ``submit_job()`` requests will fail. - - This is a current Spark Operator requirement and is expected to be simplified - once `kubeflow/spark-operator#3049 `_ - is resolved. + ``SparkClient`` does not set a ServiceAccount on the driver spec. Both + interactive sessions and batch jobs need the driver to run as a + ServiceAccount with permission to create executor pods in the target + namespace. That ServiceAccount is provisioned and selected by the platform, + not by the SDK. + + On a Kubeflow Platform install, the Profile controller creates a + ``default-editor`` ServiceAccount in every profile namespace and binds it to + the ``kubeflow-edit`` ClusterRole, which covers pods and services + in-namespace. Set ``controller.defaultServiceAccount=default-editor`` in the + Spark Operator Helm chart so the operator falls back to it. On a standalone + Spark Operator install, set it to a ServiceAccount that exists in every + namespace where you submit jobs — for example the ``-spark`` + account the chart creates in the namespaces listed under + ``spark.jobNamespaces``. + + When ``controller.defaultServiceAccount`` is unset, the driver runs as the + namespace's ``default`` ServiceAccount and the job fails with a + ``403 Forbidden`` error when the driver tries to create executor pods. + + Interactive sessions can override the ServiceAccount per session with + ``Driver(service_account=...)``; batch jobs have no per-job override. Two Ways to Run Spark ----------------------- diff --git a/examples/spark/README.md b/examples/spark/README.md index fc605cd1a..e44fbcb75 100644 --- a/examples/spark/README.md +++ b/examples/spark/README.md @@ -35,7 +35,7 @@ Install spark dependencies: uv pip install kubeflow[spark] ``` -The Spark examples run against a Kubernetes cluster with the Spark Operator installed. Batch job submission requires a `spark-operator-spark` ServiceAccount in the target namespace with the required SparkApplication RBAC permissions. See the [Spark SDK docs](https://sdk.kubeflow.org/en/latest/spark/index.html) for prerequisites. +The Spark examples run against a Kubernetes cluster with the Spark Operator installed. The SDK does not set a ServiceAccount on the driver spec, so the Spark Operator must be configured with a fallback ServiceAccount that can create executor pods in the target namespace (`controller.defaultServiceAccount` in the Helm chart). See the [Spark SDK docs](https://sdk.kubeflow.org/en/latest/spark/index.html) for prerequisites. ## Running Examples diff --git a/kubeflow/spark/backends/kubernetes/constants.py b/kubeflow/spark/backends/kubernetes/constants.py index dc4067246..3b78c0ae0 100644 --- a/kubeflow/spark/backends/kubernetes/constants.py +++ b/kubeflow/spark/backends/kubernetes/constants.py @@ -51,7 +51,6 @@ DEFAULT_DRIVER_MEMORY = "512Mi" DEFAULT_EXECUTOR_CPU = 1 DEFAULT_EXECUTOR_MEMORY = "512Mi" -DEFAULT_SERVICE_ACCOUNT = "spark-operator-spark" # Function-based Spark job script FUNC_JOB_VOLUME_NAME = "spark-app-source" diff --git a/kubeflow/spark/backends/kubernetes/utils.py b/kubeflow/spark/backends/kubernetes/utils.py index 1885bce04..2deb91176 100644 --- a/kubeflow/spark/backends/kubernetes/utils.py +++ b/kubeflow/spark/backends/kubernetes/utils.py @@ -682,7 +682,6 @@ def get_spark_job_driver_spec( return models.SparkV1beta2DriverSpec( cores=cores, memory=memory, - service_account=constants.DEFAULT_SERVICE_ACCOUNT, ) diff --git a/kubeflow/spark/backends/kubernetes/utils_test.py b/kubeflow/spark/backends/kubernetes/utils_test.py index 35170d3b7..c13672d9a 100644 --- a/kubeflow/spark/backends/kubernetes/utils_test.py +++ b/kubeflow/spark/backends/kubernetes/utils_test.py @@ -1245,7 +1245,7 @@ def test_read_pod_logs(test_case: TestCase) -> None: "test_case", [ TestCase( - name="default spark job driver spec", + name="default spark job driver spec leaves service account unset", expected_status=SUCCESS, config={}, ), @@ -1264,7 +1264,7 @@ def test_get_spark_job_driver_spec(test_case: TestCase) -> None: assert spec.memory == _memory_kubernetes_to_spark( constants.DEFAULT_DRIVER_MEMORY, ) - assert spec.service_account == constants.DEFAULT_SERVICE_ACCOUNT + assert spec.service_account is None print("test execution complete")