Getting started¶
This guide describes the current source. Follow the installation guide to choose between a published release and a source checkout. Example file paths below are relative to the repository root.
Before submitting¶
Install the core package using the installation guide, and use that same Python environment for these commands. Choose either the Python or CLI example below; they submit the same kind of application.
Have these inputs ready:
- Kubernetes credentials for the intended cluster and namespace. By default the
client loads your kubeconfig's current context. Use
SPARK_ON_K8S_CONFIG_FILEandSPARK_ON_K8S_CONTEXTto select them explicitly, orSPARK_ON_K8S_IN_CLUSTER=truewhen running with an in-cluster service account. - A namespace and driver service account with the permissions described below. The submitting client's credentials and the driver's service account are separate.
- A Spark application image accessible to every node that may run a driver or
executor, with the application file and its dependencies included. A
local:///application path names a file inside that image, not on your laptop. - Data and storage access for both driver and executors.
The submitter does not need Spark or Java installed locally. The application image must have compatible Spark, Java and application runtimes; use the Spark compatibility guide when choosing it. If you do not yet have an image, follow the Python or Java examples first.
Set up the Kubernetes namespace¶
The client creates the Spark driver pod. The driver uses its Kubernetes service
account to create and manage executor pods. The following examples use namespace
spark and service account spark; the submitting client also needs permission
to create the application resources in that namespace.
If your administrator has already provided that namespace and service account, skip the setup helper and use their names in the submission examples.
The optional setup helper creates the namespace if needed, a service account named spark,
and a ClusterRoleBinding to the built-in edit ClusterRole. This grants that
service account edit access across all namespaces. It is broader than a
namespace-scoped Spark driver role. On a shared cluster, ask the cluster
administrator to provision a suitably scoped service account instead.
Running the helper requires credentials that can list namespaces, service accounts
and ClusterRoleBindings, create missing resources, and bind the edit role.
Use it only when that cluster-wide grant is intended. See the
Kubernetes RBAC documentation
for the difference between RoleBindings and ClusterRoleBindings.
With Python:
from spark_on_k8s.utils.setup_namespace import SparkOnK8SNamespaceSetup
namespace_setup = SparkOnK8SNamespaceSetup()
namespace_setup.setup_namespace(namespace="spark")
With the CLI:
spark-on-k8s namespace setup -n spark
Python client¶
Replace the image and application path with an image accessible to your cluster and an application file included in that image. The values below are placeholders:
from spark_on_k8s.client import SparkOnK8S
client = SparkOnK8S()
driver_pod_name = client.submit_app(
image="registry.example.com/team/my-app:1.0",
app_path="local:///opt/spark/work-dir/my-app.py",
app_arguments=["arg1", "arg2"],
app_name="my-app",
namespace="spark",
service_account="spark",
app_waiter="log",
image_pull_policy="IfNotPresent",
)
submit_app returns the driver pod name. The application ID is generated from the
application name with a timestamp suffix and is available in the application list.
The driver pod is usually named <application ID>-driver; an ID longer than 56
characters instead keeps a readable prefix followed by a hash of the full ID, so
use the returned name or run receipt rather than deriving it.
For a private registry, configure image-pull credentials for the pods in your cluster.
CLI¶
The CLI can submit applications and manage applications created by either the CLI or the Python client. Submit an application and stream its logs:
spark-on-k8s app submit \
--image registry.example.com/team/my-app:1.0 \
--path local:///opt/spark/work-dir/my-app.py \
--namespace spark \
--service-account spark \
--name my-app \
--image-pull-policy IfNotPresent \
--logs --timeout 300 \
param1 param2
The CLI example waits up to 300 seconds after submission and reports the final
status. A timeout stops observation; it does not cancel the application. Confirm
Succeeded and check the application's expected output before treating the run
as successful. For JSON output, saved receipts and safe cancellation, follow
Automation and run identities.
List applications to find their generated IDs:
spark-on-k8s apps list -n spark
To kill a running application, copy its actual ID from that output. The following
ID is illustrative; my-app alone is the application name, not the generated ID:
APP_ID="my-app-20261107120000" # Replace with the ID from the application list
spark-on-k8s app kill -n spark --app-id "$APP_ID"
To have Kubernetes stop a run that outlives its expected duration, even after your
terminal or CI job has gone, add --max-runtime SECONDS to app submit. Finished
drivers and their Services, ConfigMaps and Secrets remain until deleted;
spark-on-k8s apps prune -n spark --older-than 86400 lists them and deletes them only
with --delete. See driver lifetime limits and pruning.
Check the help for further commands and options:
spark-on-k8s --help
spark-on-k8s app --help
spark-on-k8s apps --help
REST API¶
Install the api extra before starting the REST API and web UI. The API serves
only its current namespace unless SPARK_ON_K8S_API_NAMESPACES names others; outside
a cluster, that is the namespace of the kubeconfig context. For local access to the
spark namespace:
export SPARK_ON_K8S_API_NAMESPACES=spark
spark-on-k8s api start \
--host "127.0.0.1" \
--port 8080 \
--workers 4 \
--log-level error \
--limit-concurrency 100
List applications in the spark namespace:
curl 'http://127.0.0.1:8080/apps/list_apps/spark' \
-H 'accept: application/json'
The console provides a searchable application list, status filters, namespace selection, application details, and confirmed stop/delete actions. It refreshes in the background without resetting your filters. The last successful fetch age and polling state help you recognize stale data. Counts and filters describe the applications loaded so far; use Load more when additional pages are available. Completed applications from the configured history server appear as archive entries without live-driver actions.
The driver-log view supports search, a configurable tail and frozen reading while new lines arrive. Its live buffer retains the most recent 10,000 lines. Download exports that buffered text; it is not a complete historical log archive. The excerpt preview lets you review and edit bounded text before copying or sharing it. Connection failures appear separately from log text, and a history-server outage leaves live applications available. JavaScript and CSS are packaged with the Python distribution; no CDN is needed.
Open http://127.0.0.1:8080/webserver/apps?namespace=spark and use Open Spark UI
for an application submitted with ui_reverse_proxy=True in Python or
--ui-reverse-proxy in the CLI. Its direct URL is
http://127.0.0.1:8080/webserver/ui/spark/APP_ID; replace APP_ID with the actual
application ID. The API process must be able to reach the driver's service.
When placing the console behind a gateway, configure the
browser and proxy boundaries, including the public
WebSocket origin when needed.
Use --host 0.0.0.0 to listen on all network interfaces when deploying behind an
appropriate service or proxy. Client URLs should use the server's reachable host
or address, not the wildcard bind address 0.0.0.0.
When a gateway serves the API below a path such as /platform/spark and removes that
prefix before forwarding, pass --root-path /platform/spark or set
SPARK_ON_K8S_API_ROOT_PATH=/platform/spark. See the
mounted path guidance.
Deploy the API with Helm¶
The chart deploys the API, its Service and RBAC resources. Ingress and the optional
Spark History Server are controlled by chart values. The chart's env list sets
environment variables for the API and its Kubernetes client.
The published chart is released independently from this source checkout. Inspect the selected published chart's values before deploying it; unreleased chart fixes and defaults described below may not be present:
helm repo add spark-on-k8s https://hussein.awala.fr/spark-on-k8s-chart
helm repo update
helm show values spark-on-k8s/spark-on-k8s
helm install spark-on-k8s-release spark-on-k8s/spark-on-k8s \
--values examples/helm/values.yaml
To use the chart from this checkout, render it for review first, then install
that local chart. Set API_IMAGE_TAG to a compatible, tested image from the
current source; the old 0.12.1 API does not implement the readiness endpoint:
helm template spark-on-k8s-release ./chart --values examples/helm/values.yaml \
--set-string image.tag="${API_IMAGE_TAG:?Set a qualified API image tag}"
helm install spark-on-k8s-release ./chart --values examples/helm/values.yaml \
--set-string image.tag="$API_IMAGE_TAG"
Choose one chart source for a release. The commands above use example values from
this checkout; adapt them to your cluster and selected chart version.
The source chart defaults to readOnly: true, namespaces: "" (the release
namespace), a ClusterIP Service on port 8000, and disabled ingress. Set namespaces
to a comma-separated string such as "spark" or "spark,analytics" to serve those
namespaces, or to "*" to serve every namespace through a chart-owned cluster role
with the same permissions. The chart-created Kubernetes permissions follow the same
value, and the API refuses requests, Spark UI proxy connections and log streams for
other namespaces. Enable readOnly: false only if the API should be able to kill or
delete applications; with "*", that also requires rbac.allowClusterWideWrite: true.
The separate web UI button settings control visibility,
not Kubernetes authorization. The API has no built-in user authentication; its
namespace scope limits what it can reach, not who can use it. Expose the API through
access-controlled networking or an authenticating proxy.
The source chart supplies in-cluster Kubernetes configuration and the API bind
address and port. It requires an explicit API image tag or digest; a published
chart defaults to its appVersion tag. Using a local chart does not build or
install the Python source into an image. Set image.repository
and image.tag to the API image version you intend to deploy, or use
image.digest to select verified image contents; the digest takes precedence over
the tag. This is independent of the Spark application image passed to submit_app.
Follow Chart deployment boundaries for scoped access,
image selection and existing-release upgrades.
Configuration¶
Set environment variables before starting the CLI/API process or importing the
Python client: configuration values are read at module import time. The table
below lists defaults in the current source, before chart or caller overrides.
unset means no environment default (None); {}, [] and an empty string are
distinct empty defaults. Use lowercase true or false for boolean settings.
Most client defaults also apply to the CLI. The six node-selector, label and
annotation variables marked CLI are CLI defaults; Python callers should pass
the corresponding driver_* or executor_* dictionaries to submit_app.
If all three executor-count variables are unset, applications use two executors
with dynamic allocation disabled. The two executor-template settings are mutually
exclusive, including when supplied through environment variables.
A value that cannot be parsed, such as SPARK_ON_K8S_DRIVER_CPU=two or an empty
integer, stops the CLI with one error line naming the variable; the SDK, API and Airflow
raise ConfigurationError (a ValueError) when the configuration is imported. JSON
values are never echoed in these errors.
| Environment variable | Description | Default |
|---|---|---|
SPARK_ON_K8S_DOCKER_IMAGE |
Spark driver and executor image; required unless passed explicitly | unset |
SPARK_ON_K8S_APP_PATH |
Application file URI; required unless passed explicitly | unset |
SPARK_ON_K8S_NAMESPACE |
Application namespace | default |
SPARK_ON_K8S_SERVICE_ACCOUNT |
Driver service account | spark |
SPARK_ON_K8S_APP_NAME |
Application name; generated when unset | unset |
SPARK_ON_K8S_SPARK_CONF |
Spark properties as a JSON object | {} |
SPARK_ON_K8S_CLASS_NAME |
Main class for a JVM application | unset |
SPARK_ON_K8S_PACKAGES |
Maven coordinates separated by commas | empty string |
SPARK_ON_K8S_APP_ARGUMENTS |
Application arguments as a JSON array of strings | [] |
SPARK_ON_K8S_APP_WAITER |
Wait mode: no_wait, wait or log |
no_wait |
SPARK_ON_K8S_IMAGE_PULL_POLICY |
Pod image pull policy: Always, IfNotPresent or Never |
IfNotPresent |
SPARK_ON_K8S_UI_REVERSE_PROXY |
Configure the application for Spark UI reverse proxy access | false |
SPARK_ON_K8S_DRIVER_CPU |
Driver CPU cores (integer) | 1 |
SPARK_ON_K8S_DRIVER_MEMORY |
Driver memory in MiB (integer) | 1024 |
SPARK_ON_K8S_DRIVER_MEMORY_OVERHEAD |
Driver memory overhead in MiB (integer) | 512 |
SPARK_ON_K8S_EXECUTOR_CPU |
Executor CPU cores (integer) | 1 |
SPARK_ON_K8S_EXECUTOR_MEMORY |
Executor memory in MiB (integer) | 1024 |
SPARK_ON_K8S_EXECUTOR_MEMORY_OVERHEAD |
Executor memory overhead in MiB (integer) | 512 |
SPARK_ON_K8S_EXECUTOR_MIN_INSTANCES |
Minimum executor count; enables dynamic allocation when set | unset |
SPARK_ON_K8S_EXECUTOR_MAX_INSTANCES |
Maximum executor count; enables dynamic allocation when set | unset |
SPARK_ON_K8S_EXECUTOR_INITIAL_INSTANCES |
Initial executor count; fixed count when minimum and maximum are unset | unset |
SPARK_ON_K8S_EXECUTOR_ALLOCATION_RATIO |
Dynamic allocation ratio (number) | 1.0 |
SPARK_ON_K8S_SCHEDULER_BACKLOG_TIMEOUT |
Backlog duration before requesting executors | 1s |
SPARK_ON_K8S_SUSTAINED_SCHEDULER_BACKLOG_TIMEOUT |
Backlog duration for subsequent executor requests | value of SPARK_ON_K8S_SCHEDULER_BACKLOG_TIMEOUT |
SPARK_ON_K8S_SECRET_ENV_VAR |
Driver and executor environment values as a JSON object; stored in an application-owned Secret | {} |
SPARK_ON_K8S_DRIVER_ENV_VARS_FROM_SECRET |
Existing Secret names separated by commas; import their keys as driver environment variables | empty list |
SPARK_ON_K8S_STARTUP_TIMEOUT |
Driver startup timeout in seconds when waiting or streaming logs (integer); 0 disables it |
0 |
SPARK_ON_K8S_CONFIG_FILE |
Kubeconfig file path | unset |
SPARK_ON_K8S_CONTEXT |
Kubeconfig context | unset |
SPARK_ON_K8S_CLIENT_CONFIG |
JSON object of synchronous kubernetes.client.Configuration constructor arguments |
unset |
SPARK_ON_K8S_ASYNC_CLIENT_CONFIG |
JSON object of kubernetes_asyncio.client.Configuration constructor arguments; requires the api or airflow extra |
unset |
SPARK_ON_K8S_IN_CLUSTER |
Load Kubernetes credentials from the running pod | false |
SPARK_ON_K8S_SPARK_DRIVER_NODE_SELECTOR |
CLI driver node selector as a JSON object | {} |
SPARK_ON_K8S_SPARK_EXECUTOR_NODE_SELECTOR |
CLI executor node selector as a JSON object | {} |
SPARK_ON_K8S_SPARK_DRIVER_LABELS |
CLI driver labels as a JSON object | {} |
SPARK_ON_K8S_SPARK_EXECUTOR_LABELS |
CLI executor labels as a JSON object | {} |
SPARK_ON_K8S_SPARK_DRIVER_ANNOTATIONS |
CLI driver annotations as a JSON object | {} |
SPARK_ON_K8S_SPARK_EXECUTOR_ANNOTATIONS |
CLI executor annotations as a JSON object | {} |
SPARK_ON_K8S_EXECUTOR_POD_TEMPLATE_PATH |
Executor template path already available to the driver | unset |
SPARK_ON_K8S_EXECUTOR_TEMPLATE |
Executor template YAML or local file path; creates and mounts a ConfigMap | unset |
SPARK_ON_K8S_API_NAMESPACES |
Namespaces the API serves: empty for its current namespace (the pod's, or the kubeconfig context's), * for every namespace, or a comma-separated list |
empty |
SPARK_ON_K8S_API_DEFAULT_NAMESPACE |
Namespace the API/web UI uses when a request names none; must be in the scope | the first namespace in scope; with *, the current namespace |
SPARK_ON_K8S_API_HOST |
API bind address | 127.0.0.1 |
SPARK_ON_K8S_API_PORT |
API listen port (integer) | 8000 |
SPARK_ON_K8S_API_WORKERS |
API worker count (integer) | 4 |
SPARK_ON_K8S_API_LOG_LEVEL |
API server log level | info |
SPARK_ON_K8S_API_LIMIT_CONCURRENCY |
Maximum concurrent API connections/tasks (integer) | 1000 |
SPARK_ON_K8S_API_ROOT_PATH |
Public path prefix, such as /platform/spark, that a gateway removes before forwarding to the API; --root-path default |
empty string |
SPARK_ON_K8S_API_SPARK_HISTORY_HOST |
Spark History Server host and port, optionally with an HTTP(S) scheme | unset |
SPARK_ON_K8S_API_WEBSERVER_APPS_KILL_BUTTON |
Show the kill button in the applications page | true |
SPARK_ON_K8S_API_WEBSERVER_APPS_DELETE_BUTTON |
Show the delete button in the applications page | true |
SPARK_ON_K8S_API_WEBSERVER_APPS_REFRESH_INTERVAL |
Applications-page refresh interval in whole seconds; 0 or a negative value disables it |
10 |
Boolean settings in this table accept true or false in any letter case; any other
value is treated as false.
JSON settings use JSON syntax, including double-quoted keys and strings. Packages and existing Secret names use comma-separated strings instead of JSON arrays:
export SPARK_ON_K8S_SPARK_CONF='{"spark.sql.shuffle.partitions": "8"}'
export SPARK_ON_K8S_APP_ARGUMENTS='["--input", "s3a://example/input"]'
export SPARK_ON_K8S_DRIVER_ENV_VARS_FROM_SECRET='spark-storage,spark-catalog'
export SPARK_ON_K8S_API_WEBSERVER_APPS_REFRESH_INTERVAL='10'
The named Secrets must exist in the application's namespace. Refresh values such
as 10s are invalid because the setting is parsed as an integer.