Chapter 4. Run Spark workloads from OpenShift AI workbenches


This section describes how to deploy a Spark Connect server on OpenShift AI and run PySpark workloads from Jupyter workbenches without managing Spark infrastructure.

Important

Spark Connect on OpenShift AI is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.

For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.

4.1. Spark Connect on OpenShift AI

You can run distributed Apache Spark workloads directly from OpenShift AI Jupyter workbenches by using Spark Connect and the Kubeflow Spark Operator. Spark Connect provides a gRPC-based client/server architecture that decouples the PySpark client in your notebook from the Spark driver, so you can submit and monitor Spark jobs without managing Spark infrastructure or requiring cluster-level permissions.

The Kubeflow Spark Operator automates the lifecycle of Spark Connect servers on OpenShift AI. When you create a SparkConnect custom resource, the Operator provisions the following resources automatically:

  • A ConfigMap with the executor pod template.
  • A server pod that runs the Spark Connect driver process.
  • A ClusterIP Service that exposes the server with a stable DNS name, including ports for Spark internal communication (7078, 7079, 4040) and the gRPC endpoint (15002).

The auto-created Service follows the naming convention _<connect_name>_-server, where _<connect_name>_ is the metadata.name value from your SparkConnect custom resource. The full DNS name is _<connect_name>_-server._<namespace>_.svc.cluster.local.

You do not need to create a Service manually. The Operator creates and manages it as part of the SparkConnect resource reconciliation.

You can deploy a Spark Connect server in a namespace other than {dbd-config-default-namespace} to isolate workloads by team or project. Because the Spark Operator creates RBAC and NetworkPolicy resources only in {dbd-config-default-namespace} during initial deployment, you must manually create these resources in the target namespace before deploying the SparkConnect custom resource.

Important

Spark Connect on OpenShift AI is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.

For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.

Prerequisites

  • A Spark Connect server is deployed successfully in {dbd-config-default-namespace}, confirming that the Spark Operator is working correctly.
  • You have cluster administrator privileges for the OpenShift cluster.
  • The OpenShift CLI (`oc`) is installed.
  • You know the target namespace where you want to deploy the Spark Connect server.

Procedure

  1. Create the ServiceAccount in the target namespace.

    In the following command, replace _<target_namespace>_ with the name of your namespace:

    $ cat <<EOF | oc apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: spark-operator-spark
      namespace: <target_namespace>
    EOF
  2. Create the Role with the required permissions in the target namespace.

    The Spark driver needs permissions to create and manage executor pods, ConfigMaps, PersistentVolumeClaims, and Services:

    $ cat <<EOF | oc apply -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: spark-role
      namespace: <target_namespace>
    rules:
    - apiGroups:
      - ""
      resources:
      - pods
      - configmaps
      - persistentvolumeclaims
      - services
      verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
      - delete
      - deletecollection
    EOF
  3. Create the RoleBinding to connect the ServiceAccount to the Role:

    $ cat <<EOF | oc apply -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: spark-role-binding
      namespace: <target_namespace>
    subjects:
    - kind: ServiceAccount
      name: spark-operator-spark
    roleRef:
      kind: Role
      name: spark-role
      apiGroup: rbac.authorization.k8s.io
    EOF
  4. Create the NetworkPolicy to allow Spark internal communication and workbench connectivity:

    $ cat <<EOF | oc apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: spark-operator-allow-internal
      namespace: <target_namespace>
    spec:
      podSelector:
        matchLabels:
          sparkoperator.k8s.io/launched-by-spark-operator: "true"
      policyTypes:
        - Ingress
      ingress:
        - ports:
            - port: 7078
              protocol: TCP
            - port: 7079
              protocol: TCP
            - port: 4040
              protocol: TCP
          from:
            - podSelector: {}
            - namespaceSelector:
                matchLabels:
                  network.openshift.io/policy-group: ingress
        - ports:
            - port: 15002
              protocol: TCP
          from:
            - podSelector: {}
            - namespaceSelector:
                matchLabels:
                  opendatahub.io/dashboard: "true"
              podSelector:
                matchLabels:
                  opendatahub.io/workbenches: "true"
    EOF
    • Ports 7078 and 7079 enable remote procedure call (RPC) and block manager communication between the Spark driver and executor pods.
    • Port 4040 enables the Spark UI.
    • Port 15002 enables gRPC connectivity from workbench pods in namespaces managed by the OpenShift AI dashboard.
  5. Deploy the SparkConnect custom resource in the target namespace.

    Create a YAML file with the metadata.namespace field set to your target namespace and apply it:

    $ oc apply -f spark-connect-server.yaml

    For the complete SparkConnect custom resource example, see SparkConnect custom resource and infrastructure reference.

Verification

  1. Verify that the server pod is running in the target namespace:

    $ oc get pods -n <target_namespace> -l sparkoperator.k8s.io/launched-by-spark-operator=true

    The output shows a pod with a Running status.

  2. Verify that the auto-created Service exists:

    $ oc get svc -n <target_namespace> -l sparkoperator.k8s.io/connect-name=spark-connect

    The output shows a ClusterIP Service exposing port 15002.

  3. Verify that the RBAC resources are created correctly:

    $ oc get serviceaccount spark-operator-spark -n <target_namespace>
    $ oc get role spark-role -n <target_namespace>
    $ oc get rolebinding spark-role-binding -n <target_namespace>
  4. Verify that the NetworkPolicy was created in the target namespace:

    $ oc get networkpolicy spark-operator-allow-internal -n <target_namespace>

4.3. Deploy a Spark Connect server on OpenShift AI

You can deploy a Spark Connect server on OpenShift AI by creating a SparkConnect custom resource in a namespace that an administrator has configured with the required RBAC resources. After you deploy the server, the Kubeflow Spark Operator automatically creates a ClusterIP Service that provides a stable DNS endpoint for workbench connectivity.

Prerequisites

  • Red Hat OpenShift AI 3.5 or later is installed.
  • The DataScienceCluster resource is configured with the sparkoperator component set to Managed.
  • The DataScienceCluster resource is configured with the workbenches component set to Managed.
  • An administrator has configured a namespace with the required ServiceAccount, Role, RoleBinding, and NetworkPolicy resources for Spark Connect.
  • You have access to the configured namespace.
  • The OpenShift CLI (`oc`) is installed.

Procedure

  1. Create a YAML file for the SparkConnect resource. The following example shows a basic SparkConnect configuration:

    apiVersion: sparkoperator.k8s.io/v1alpha1
    kind: SparkConnect
    metadata:
      name: spark-connect
      namespace: <your_namespace>
    spec:
      sparkVersion: 4.0.1
      server:
        template:
          spec:
            containers:
            - name: spark-kubernetes-driver
              image: quay.io/opendatahub/data-processing:Spark-v4.0.1
              imagePullPolicy: Always
              resources:
                requests:
                  cpu: 1
                  memory: 1Gi
                limits:
                  cpu: 1
                  memory: 1Gi
            serviceAccountName: spark-operator-spark
            securityContext: {}
      executor:
        instances: 2
        cores: 1
        memory: 512m
        template:
          spec:
            containers:
            - name: spark-kubernetes-executor
              image: quay.io/opendatahub/data-processing:Spark-v4.0.1
              imagePullPolicy: Always
            securityContext: {}
    Note

    The securityContext: {} field at the pod level is intentionally empty. On OpenShift, the restricted-v2 Security Context Constraint (SCC) automatically enforces security restrictions such as runAsNonRoot, capability dropping, and seccomp profiles. Do not add explicit security context fields to the SparkConnect custom resource.

  2. Apply the SparkConnect custom resource:

    $ oc apply -f spark-connect-server.yaml
  3. Verify that the Spark Connect server pod is running:

    $ oc get pods -n <your_namespace> -l sparkoperator.k8s.io/launched-by-spark-operator=true

    The output shows a pod named spark-connect-server with a Running status:

    NAME                     READY   STATUS    RESTARTS   AGE
    spark-connect-server     1/1     Running   0          30s
  4. Verify that the Operator has created the ClusterIP Service automatically:

    $ oc get svc -n <your_namespace> -l sparkoperator.k8s.io/connect-name=spark-connect

    The output shows the auto-created Service exposing ports for Spark internal communication and gRPC connectivity:

    NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                   AGE
    spark-connect-server     ClusterIP   172.30.x.x      <none>        7078/TCP,7079/TCP,4040/TCP,15002/TCP      30s
    Note

    The Operator creates this Service automatically. You do not need to create a Service manually. The Service DNS name follows the convention _<connect_name>_-server._<namespace>_.svc.cluster.local. For example, the DNS name is spark-connect-server.<your_namespace>.svc.cluster.local.

  5. Record the Service DNS name for use in the workbench connection string:

    spark-connect-server.<namespace>.svc.cluster.local

    Workbench users use this DNS name with port 15002 to construct the sc:// connection URL in their workbench notebooks.

You can connect to a Spark Connect server from an OpenShift AI Jupyter workbench and run distributed PySpark workloads directly from your notebook. Spark Connect supports DataFrame operations and Spark SQL, so you can process large datasets without managing Spark infrastructure.

Prerequisites

  • A Spark Connect server is deployed and running on your OpenShift AI cluster. For instructions, see Deploy a Spark Connect server on OpenShift AI.
  • The ClusterIP Service for the Spark Connect server is available, and you know the Service DNS name.
  • An OpenShift AI Jupyter workbench is running. A minimal Jupyter image with CPU support is sufficient for Spark Connect workloads, such as "Jupyter | Minimal | CPU | Python 3.12".

Procedure

  1. In your Jupyter notebook, install PySpark and the required dependencies by running the following command in a notebook cell:

    !pip install -q pyspark==4.0.1 pandas pyarrow grpcio grpcio-status zstandard
    Important

    The PySpark version must match the Spark version used in the Spark Connect server container image. A version mismatch causes connection failures. After the installation completes, restart the notebook kernel so that the new packages take effect.

  2. Construct the Spark Connect URL by using the Service DNS name and port 15002.

    The URL follows this format:

    sc://<connect_name>-server.<namespace>.svc.cluster.local:15002

    For a SparkConnect resource named spark-connect deployed in {dbd-config-default-namespace}, the URL is:

    sc://spark-connect-server.redhat-ods-applications.svc.cluster.local:15002
  3. Create a SparkSession by using the sc:// connection URL.

    Run the following code in a notebook cell:

    from pyspark.sql import SparkSession
    
    spark = SparkSession.builder.remote(
        "sc://spark-connect-server.redhat-ods-applications.svc.cluster.local:15002"
    ).getOrCreate()
    Note

    Replace the Service DNS name and namespace if your Spark Connect server is deployed in a different namespace.

  4. Run a PySpark DataFrame workload to verify the connection.

    The following example creates a DataFrame, applies a filter, and displays the results:

    data = [("Alice", 34), ("Bob", 45), ("Claire", 66)]
    df = spark.createDataFrame(data, ["Name", "Age"])
    df.filter(df.Age > 40).show()

    The expected output is as follows:

    +------+---+
    |  Name|Age|
    +------+---+
    |   Bob| 45|
    |Claire| 66|
    +------+---+

If you cannot connect to the Spark Connect server from your OpenShift AI workbench, you can diagnose the problem by checking the server pod status, verifying the Service endpoint, and confirming that NetworkPolicy rules allow gRPC traffic on port 15002.

Prerequisites

  • A Spark Connect server has been deployed. For instructions, see Deploy a Spark Connect server on OpenShift AI.
  • The OpenShift CLI (`oc`) is installed.
  • You have access to the namespace where the Spark Connect server is deployed.
Procedure

The following commands use spark-connect-server as the pod and Service name. The Operator derives these names from the metadata.name of your SparkConnect custom resource by appending -server. If your SparkConnect resource uses a different name, replace spark-connect-server with _<your_connect_name>_-server in all commands.

  1. Check the Spark Connect server pod status and logs.

    In the following commands, replace _<namespace>_ with the namespace where the SparkConnect resource is deployed:

    $ oc get pods -n <namespace> -l sparkoperator.k8s.io/launched-by-spark-operator=true

    If the pod is not in Running status, inspect the pod events for startup failures:

    $ oc describe pod spark-connect-server -n <namespace>

    Check the server logs for error messages:

    $ oc logs spark-connect-server -n <namespace>
  2. Verify that the ClusterIP Service exists and has the correct selector labels.

    $ oc get svc -n <namespace> -l sparkoperator.k8s.io/connect-name=spark-connect

    If no Service is returned, verify that the SparkConnect custom resource has been applied correctly. The Operator creates the Service automatically during reconciliation.

    Inspect the Service details to confirm that the selector labels and port match:

    $ oc describe svc spark-connect-server -n <namespace>

    The Service should select pods with labels sparkoperator.k8s.io/launched-by-spark-operator: true, sparkoperator.k8s.io/connect-name: _<connect_name>_, spark-role: connect-server, and spark-version: _<spark_version>_. The Service exposes ports 7078, 7079, 4040, and 15002.

  3. Verify that the Service DNS name resolves from inside the workbench pod.

    Find the workbench pod name:

    $ oc get pods --all-namespaces -l opendatahub.io/workbenches=true

    Test DNS resolution from inside the workbench pod. In the following command, replace _<workbench_pod>_ and _<workbench_namespace>_ with your workbench pod name and namespace:

    $ oc exec -n <workbench_namespace> <workbench_pod> -- nslookup spark-connect-server.<namespace>.svc.cluster.local

    If DNS resolution fails, verify that the Service exists in the expected namespace.

  4. Verify that the NetworkPolicy allows traffic on port 15002 from your workbench.

    Check that the workbench pod has the required label:

    $ oc get pod <workbench_pod> -n <workbench_namespace> --show-labels | grep opendatahub.io/workbenches

    Check that the workbench namespace has the required label:

    $ oc get namespace <workbench_namespace> --show-labels | grep opendatahub.io/dashboard

    If either label is missing, the spark-operator-allow-internal NetworkPolicy blocks the gRPC traffic. The workbench pod must have the opendatahub.io/workbenches: "true" label, and the workbench namespace must have the opendatahub.io/dashboard: "true" label.

  5. Test gRPC connectivity from the workbench pod.

    Open a remote shell on the workbench pod and attempt a PySpark connection:

    $ oc rsh -n <workbench_namespace> <workbench_pod>

    Inside the shell, run a basic connection test:

    python -c "from pyspark.sql import SparkSession; spark = SparkSession.builder.remote('sc://spark-connect-server.<namespace>.svc.cluster.local:15002').getOrCreate(); print(spark.version)"

    If this command succeeds but the notebook cell fails, restart the notebook kernel to reload the installed packages.

You can use the following reference tables to understand the fields in the SparkConnect custom resource, the RBAC resources required by the Spark Operator, the NetworkPolicy rules for workbench connectivity, and the PySpark client dependencies.

Important

The SparkConnect custom resource is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.

For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.

4.6.1. SparkConnect custom resource fields

The SparkConnect custom resource uses the sparkoperator.k8s.io/v1alpha1 API version. The following table describes the key fields.

Table 4.1. SparkConnect custom resource fields
FieldTypeDescription

metadata.name

String

The name of the SparkConnect resource. The Operator uses this value to derive the server pod name and Service name by appending -server.

metadata.namespace

String

The namespace where the SparkConnect server is deployed. If you use a namespace other than {dbd-config-default-namespace}, you must manually create RBAC and NetworkPolicy resources.

spec.sparkVersion

String

The version of Apache Spark that the Spark Connect server uses. This value must match the version in the container image.

spec.image

String

Optional. The default container image for the server and executor containers. Per-container images in spec.server.template or spec.executor.template take precedence.

spec.sparkConf

Map

Optional. Spark configuration properties, equivalent to --conf options in spark-submit.

spec.hadoopConf

Map

Optional. Hadoop configuration properties. The Operator automatically adds the spark.hadoop. prefix.

spec.server.template

PodTemplateSpec

The pod template for the Spark Connect server. Use this to configure the container image, resource requests, limits, security context, and service account. Set the service account at spec.server.template.spec.serviceAccountName.

spec.server.service

Service

Optional. Custom Service specification for the Spark Connect server. If omitted, the Operator creates a default ClusterIP Service with ports 7078 (driver-rpc), 7079 (blockmanager), 4040 (web-ui), and 15002 (gRPC).

spec.executor.instances

Integer

The number of executor instances to run. Minimum value is 0.

spec.executor.cores

Integer

The number of CPU cores per executor. Minimum value is 1.

spec.executor.memory

String

The amount of memory per executor, such as 512m or 1Gi.

spec.executor.template

PodTemplateSpec

The pod template for executor pods. Use this to configure the executor container image and resources.

spec.dynamicAllocation

Object

Optional. Configures dynamic allocation of executors, available since Spark 3.0 with the Kubernetes scheduler backend.

4.6.2. SparkConnect custom resource example

The following example creates a SparkConnect server with two executor instances in the {dbd-config-default-namespace} namespace:

apiVersion: sparkoperator.k8s.io/v1alpha1
kind: SparkConnect
metadata:
  name: spark-connect
  namespace: redhat-ods-applications
spec:
  sparkVersion: 4.0.1
  server:
    template:
      spec:
        containers:
        - name: spark-kubernetes-driver
          image: quay.io/opendatahub/data-processing:Spark-v4.0.1
          imagePullPolicy: Always
          resources:
            requests:
              cpu: 1
              memory: 1Gi
            limits:
              cpu: 1
              memory: 1Gi
        serviceAccountName: spark-operator-spark
        securityContext: {}
  executor:
    instances: 2
    cores: 1
    memory: 512m
    template:
      spec:
        containers:
        - name: spark-kubernetes-executor
          image: quay.io/opendatahub/data-processing:Spark-v4.0.1
          imagePullPolicy: Always
        securityContext: {}
  • securityContext: {} at the pod level delegates security enforcement to the OpenShift restricted-v2 Security Context Constraint. Do not add explicit security context fields such as runAsNonRoot or capabilities.drop, because OpenShift applies these constraints automatically.
  • spec.server.template.spec.serviceAccountName must reference a service account with sufficient RBAC permissions to manage executor pods.

4.6.3. RBAC resources

The Spark Operator creates the following RBAC resources in the {dbd-config-default-namespace} namespace during initial deployment. If you deploy a SparkConnect server in a different namespace, you must recreate these resources manually.

Table 4.2. ServiceAccount
FieldValue

Name

spark-operator-spark

Purpose

Identity for the Spark Connect server pod and Spark driver, which creates and manages executor pods.

Table 4.3. Role permissions
API groupResourcesVerbs

"" (core)

pods, configmaps, persistentvolumeclaims, services

get, list, watch, create, update, patch, delete, deletecollection

Table 4.4. RoleBinding
FieldValue

Name

spark-role-binding

Role reference

spark-role

Subject

ServiceAccount spark-operator-spark

4.6.4. NetworkPolicy

The Spark Operator creates a NetworkPolicy named spark-operator-allow-internal in the {dbd-config-default-namespace} namespace. This policy controls ingress traffic to pods launched by the Operator.

Table 4.5. NetworkPolicy ingress rules
PortProtocolPurpose

7078

TCP

Spark driver remote procedure call (RPC) communication between driver and executor pods.

7079

TCP

Spark block manager data transfer between pods.

4040

TCP

Spark UI for monitoring running applications.

15002

TCP

Spark Connect gRPC endpoint for workbench connectivity.

The port 15002 ingress rule allows traffic from:

  • Any pod in the same namespace through podSelector: {}.
  • Workbench pods labeled opendatahub.io/workbenches: "true" in namespaces labeled opendatahub.io/dashboard: "true". This combination enables workbenches running in rhods-notebooks or other dashboard-managed namespaces to reach the Spark Connect server.

4.6.5. Auto-created Service

When you create a SparkConnect custom resource, the Operator automatically creates a ClusterIP Service.

Table 4.6. Auto-created Service details
FieldValue

Name

_<connect_name>_-server, where _<connect_name>_ is the metadata.name of the SparkConnect resource.

Type

ClusterIP

Ports

7078 (driver-rpc), 7079 (blockmanager), 4040 (web-ui), 15002 (gRPC)

Selector labels

sparkoperator.k8s.io/launched-by-spark-operator: true, sparkoperator.k8s.io/connect-name: _<connect_name>_, spark-role: connect-server, and spark-version: _<spark_version>_

DNS name

_<connect_name>_-server._<namespace>_.svc.cluster.local

4.6.6. PySpark client dependencies

Install the following Python packages in your workbench to connect to the Spark Connect server. The PySpark version must match the Spark version used in the server container image.

Table 4.7. PySpark client dependencies
PackageVersionPurpose

pyspark

4.0.1

PySpark client library with Spark Connect support. Must match the server image Spark version.

pandas

Latest compatible

Required for DataFrame interoperability and toPandas() result collection.

pyarrow

Latest compatible

Columnar data interchange between PySpark and pandas.

grpcio

Latest compatible

gRPC client library for the sc:// protocol connection.

grpcio-status

Latest compatible

gRPC status code handling for error reporting.

zstandard

Latest compatible

Zstandard compression support for Spark Connect protocol communication.

Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat Documentation

Legal Notice

Theme

© 2026 Red Hat
Back to top