Skip to content

Add Spark metrics collection via Spark REST API #689

Description

@Sharkyii

What you would like to be added?

Right now SparkClient can tell you the status of a job (get_job, wait_for_job_status) but nothing about what's actually going on inside it while it runs, no executor count, no task progress, nothing. If you want that today you're stuck going through kubectl or opening the Spark UI yourself.

I'd like to add a get_job_metrics(name) method that pulls basic runtime metrics straight from Spark's own REST API on the driver:

  • GET /api/v1/applications/<app-id>/executors for executor count, active/completed tasks, memory used
  • GET /api/v1/applications/<app-id>/stages for stage progress

Went through the actual Spark Operator source (web_ui.go, pkg/util/sparkapplication.go) before settling on an approach, a couple of things worth calling out:

The operator creates a dedicated Kubernetes Service for the driver UI on every SparkApplication, default port name spark-driver-ui-port, default port 4040 (driven by spark.ui.port, but can be overridden via SparkUIOptions on the SparkApplication spec). So the port should be read off the live Service object, not assumed.

The service name itself isn't safe to construct by hand either. The operator builds it as <app-name>-ui-svc only when that's 63 characters or under (Kubernetes' DNS label limit), otherwise it truncates the app name and appends an 8 character hash. What is reliable is the label the operator puts on every UI service it creates, sparkoperator.k8s.io/app-name: <app-name>. So the plan is to look the service up by that label selector instead of guessing the name.

Also, this UI service isn't guaranteed to exist at all. It's gated behind an operator-level flag (--enable-ui-service, defaults to true) that whoever deployed the Spark Operator in a given cluster could turn off. So get_job_metrics needs to treat "no UI service for this job" as a normal, expected case, not just something that happens after the job finishes and gets cleaned up.

To reach the service:

  • In-cluster: hit it directly through the Service DNS name.
  • Outside cluster (local dev, tests): port-forward to it. Same port-forward mechanism the Kubernetes backend already uses for Spark Connect (get_connect_url in backend.py, around lines 435-511), just pointed at this service instead.

Rough shape of what I'm thinking:

@dataclass
class SparkJobMetrics:
    num_executors: int | None = None
    active_tasks: int | None = None
    completed_tasks: int | None = None
    failed_tasks: int | None = None
    active_stages: int | None = None
    completed_stages: int | None = None

get_job_metrics should return a mostly-empty SparkJobMetrics rather than raising whenever the UI service can't be found, whether that's because it was disabled at the operator level or because it's already been cleaned up after the job finished.

Why is this needed?

This is part of the observability gap called out in #655, batch jobs work fine but there's basically no visibility into them once they're running. Metrics collection is one of the pieces on that epic that nobody's picked up yet.

Love this feature?

Give it a 👍 We prioritize the features with most 👍

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions