Repository navigation
Description
What feature you would like to be added?
Define a supported Apache Spark version policy, automatically propose updates when new supported Spark patch releases become available, and publish a documented matrix of Spark runtime images for each supported Spark version and architecture.
This issue supersedes the stale-closed #1982 by adding automated version discovery, lifecycle management, verification, and matrix publication rather than relying on manual image builds.
Why is this needed?
Spark Operator releases should not force users onto one Spark version or require every user to maintain custom Spark runtime images. Manual version tracking causes supported images and documentation to drift behind Apache Spark releases. Users also need a clear compatibility matrix showing which Spark Operator releases are tested with which Spark versions and which image tags are available.
Describe the solution you would like
- Establish a support policy, for example latest patch releases from the currently supported Spark minor lines.
- Keep supported Spark versions in one machine-readable source of truth.
- Add scheduled automation that detects new Apache Spark releases and opens reviewed pull requests updating that source, build inputs, tests, and generated documentation.
- Build and publish Spark runtime images for every supported Spark version across
linux/amd64andlinux/arm64. - Use unambiguous immutable tags containing Spark version and, where needed, Hadoop/Java variants.
- Publish a generated compatibility matrix covering Spark Operator version, Spark version, image tag, architecture, Java/Hadoop variant, and support status.
- Exercise each supported image in CI with representative SparkApplication tests before publication.
- Define deprecation and removal behavior for Spark versions leaving the support window.
- Generate provenance, checksums, or signatures according to Kubeflow release standards.
Describe alternatives you have considered
- Publish only one Spark image per Spark Operator release. This couples unrelated release cycles and forces upgrades.
- Require users to build all Spark images themselves. This creates duplicated work and inconsistent security maintenance.
- Maintain multiple images manually. This does not prevent version or documentation drift.
Additional context
Related stale issue: #1982
The repository already contains spark-docker/ and e2e support for loading a Spark image, providing a foundation for matrix builds and validation.
This work should coordinate with release automation tracked by #3063.
Love this feature?
Give it a 👍 We prioritize the features with most 👍
Activity
nabuskey commentedon Aug 7, 2026
I am thinking the project should generally support:
This however, adds maintenance burden. We now have to test the operator for all support spark versions. Given spark-submit's stability and Spark's strong compatibility inside a major line, the burden may not be too bad. We probably don't need to test for version compatibility every PR. Weekly or per release tests might be enough.
vikas-saxena02 commentedon Aug 8, 2026
@nabuskey in the above example, does that mean we are supporting all version under 3.5 series or only the LTS version. the proplem with LTS version is that its never a fixed one, often bugs tend to shift it and I guess that's what you meant up by maintainers overhead. But happy to support this on ongoing basis as it will make spark version migrations for users very easy. In general. all kubeflow components support multiple versions of kubernetes so we can also support multiple versions of saprk and ci-checks can be added for the same.
vjanelle commentedon Aug 8, 2026
That was my thought around the matrix. It's mostly that we have to commit to running the e2e tests 4x- at least until 3.5 is dropped. The scope of the operator hasn't changed I think since the introduction of pod templates?
nabuskey commentedon Aug 9, 2026
Yep that's what I meant by maintenance overhead. We need to add testing and promise to providing fixes for version specific bugs.
As of right now, we must support Spark 3.5.x because Spark is still in the transition period and they intend to support it until the end of next year.
Distinguishing between LTS and non LTS is tricky imo. The Spark project does define LTS but I am not sure if this is something we want to define and support in our repository.
vikas-saxena02 commentedon Aug 10, 2026
Thanks @nabuskey — the three-rule framing works for me, and I agree with @vjanelle that the real cost here is e2e runtime, not the build. Two things push toward deciding this sooner rather than later:
Spark 4.3.0 is imminent. Per the versioning policy, 4.3.0 had its code freeze and branch cut on Aug 1, with RC voting late August. So within weeks the candidate set is 3.5.x, 4.1.x, 4.2.x and 4.3.x — four lines against a stated cap of three. We need a rule that resolves this automatically rather than a list we hand-edit each quarter.
Our current default isn't in the proposed set. The repo points at
apache/spark:4.0.4in theMakefile, all 14examples/*.yaml, andconfig/samples/(and3.5.0intest/e2e/sparkapplication_test.go). 4.0.x isn't among the three lines above, so adopting this policy drops 4.0 as the default on day one. That's user-visible and shouldn't arrive as an incidental generator diff.Current upstream state: 3.5.9, 4.1.3, 4.0.4, 4.2.0.
Proposed design
1. A source of truth that transcribes upstream rather than defining policy. I am reluctant to own an LTS definition as its a shifting goalpost and I think we can sidestep it: we don't classify anything, we just record upstream's maintenance dates as opaque fields with a source link and apply a selection rule on top.
A validator fails CI when a window lapses or the list exceeds
max_concurrent_lines— mechanical checks, no judgment encoded.2. Generate everything downstream of it. README matrix,
SPARK_IMAGE, example manifests, e2e defaults, and the published compatibility matrix all render from this file, with a drift check that fails on an uncommitted diff — same pattern asmake generateandmake detect-crds-drift. Today the README matrix stops atv2.3.xwhileVERSIONreadsv2.5.0-rc.0, which is exactly the drift this closes.3. Tiered test cadence, with no added per-PR cost. This is the answer to @vjanelle's concern. Today's e2e matrix is 4 k8s versions × 2 deploy methods = 8 jobs per PR; multiplying by Spark versions gives 24–32, which isn't acceptable. But the multiplication isn't necessary:
workflow_dispatchbefore releases: Spark version × one k8s version × one deploy method — 3–4 jobs.Worth noting: the operator has exactly three Spark-version gates in the whole codebase —
sparkapplication_validator.go,sparkconnect_validator.go, and two call sites insubmission.go— and every one isCompareSemanticVersion(..., "3.0.0")guarding pod templates. @vjanelle's point that the scope hasn't changed since pod templates is literally true in the source, which is why per-PR compat testing looks hard to justify.4. Image publishing comes later.
spark-docker/is currently orphaned — nothing in CI builds it and itsARG SPARK_IMAGEstill saysspark:3.5.5. The multi-arch build can reuse the per-arch-digest +imagetools createpattern fromrelease-latest-images.yaml.Decisions needed
undecidedabove rather than deciding for you. It's a 6-month-window feature release, so it's the first line to age out — but @nabuskey named it explicitly, so I'd rather have the call made here than have a generator quietly drop it.Proposed PR sequence
spark-docker/build and publishspark-versions.yamlHappy to take this on starting with (1) once there's a call on the questions above.
thisis-Shitanshu commentedon Aug 22, 2026
Hi @nabuskey, @vikas-saxena02! Following up on the image discussion from yesterday's call, I think this issue is probably the right place to continue the discussion rather than opening a separate one.
I've been thinking about the proposal to build and publish our own Spark runtime images, particularly from the security and long-term maintenance perspective.
My initial thought was that owning the image could give the project more control over rebuild cadence, verification, and supply-chain metadata. At the same time, it would also mean taking on an ongoing maintenance commitment: rebuilding, scanning, testing, supporting architectures/Java variants, handling CVEs, and keeping the image aligned with upstream Spark.
One finding that changed how I think about this slightly is the distinction Apache itself makes between the two Spark images:
apache/spark: reviewed and published by the Apache Spark community, but according toapache/spark-docker, a version is generally built and pushed once at release time.spark: uses the same Spark Dockerfile sources, but is maintained through the Docker Official Images process and is actively rebuilt for updates/security fixes.Ref: https://github.com/apache/spark-docker#about-images
So I think the comparison may be broader than simply:
There seems to be a useful third baseline:
From a security perspective, I also think it would help to separate the type of vulnerability we are trying to address.
If the vulnerability is in an OS package or JRE, a Kubeflow-controlled rebuild cadence could improve remediation without Kubeflow having to discover or implement the vulnerability fix itself.
If the vulnerability is inside Spark or one of its bundled JARs, however, rebuilding the same Spark distribution may not solve it. At that point, we would either need to wait for/upgrade to an upstream fix or start carrying downstream patches, which creates a different maintenance problem.
For that reason, my current preference would be to keep any Kubeflow image delta from upstream as small and explicit as possible: potentially own the build/release/security process without gradually creating a Kubeflow-specific Spark distribution.
Vikas, could you clarify what ownership boundary you have in mind for building our own images? (Asking this to better understand the direction myself)
For example, were you thinking primarily about:
Also, were there particular CVEs or classes of vulnerabilities behind the proposal? Knowing whether the main concern is OS/JRE-level or Spark/dependency-level would help establish what owning the image is expected to solve.
Small experiment before the next call
Before the next meeting, I am considering putting together a small controlled comparison around Spark 4.0.4:
For the first prototype, I would intentionally keep
kubeflow-protoclose to the upstream Spark 4.0.4 runtime rather than introducing Spark/JAR-level patches or unrelated optimizations. The initial question I want to measure is what we gain or lose by owning the build and image lifecycle itself.Initial matrix:
apache/sparksparkofficialI would keep the Kubernetes side controlled as much as possible: same Spark version, SparkApplication, Kubernetes environment, resource settings, and test suite, changing primarily the runtime image.
For startup measurements, I would also separate cold and warm runs so that registry/network pull time is not confused with Spark/container startup behavior, and repeat the timings rather than relying on a single run.
Beyond the summary matrix, I am thinking of recording:
The goal would not be to argue abstractly that owning an image is "better". I would rather put some evidence behind the trade-off before the project commits to another artifact and lifecycle that maintainers need to support.
Would this experiment be useful before the next call?
And are there Spark Operator-specific requirements that you think should be mandatory in the prototype/benchmark, for example, entrypoint behavior, UID/security context, Java variants, Python/R requirements, Hadoop/cloud connectors, or specific cases from the existing e2e suite?
One long-term maintenance reference I found relevant is Meta's recent write-up on escaping their WebRTC fork:
https://engineering.fb.com/2026/04/09/developer-tools/escaping-the-fork-how-meta-modernized-webrtc-across-50-use-cases/
It is obviously a very different system and scale, but I think the maintenance principle is relevant: keep upstream as the source of truth, keep downstream modifications small and explicit, continuously validate against upstream changes, and avoid turning useful customization into a permanently divergent fork.
If this experiment looks useful, I am happy to take a first pass before the next meeting and post the methodology/results here.
vikas-saxena02 commentedon Sep 1, 2026
@thisis-Shitanshu from my perspective, when i scanned the lastest version of the image about a month ago (which is no longer the latest and the current one has quite a few of them fixed), there are following categories of security issues which i am summarising in the table below:
apt(gnupg2,wget,libc6,krb5-user,libnss3,gosu, …) and their transitive depstinionly (added in our Dockerfile)go.mod/go.sumand the Go toolchain that compiles/usr/bin/spark-operatorgo.mod, bump builder image/opt/spark/jars(jackson-databind, netty, avro, commons-*, derby, zookeeper, Hive, kubernetes-client/okhttp)/opt/java/openjdk, on OpenJDK's quarterly CPU cadenceeclipse-temurin:<n>-jammypython3,pip,setuptoolsand pip-installed packagesspark-coreunderorg/apache/spark/ui/static/(jQuery, Bootstrap, DataTables, d3)spark-coreThree notes on the table:
hadoop-client-apiandhadoop-client-runtimeare shaded fat JARs, soscanners attribute CVEs to relocated classes that aren't reachable.
PS:dont focus much on this section, the
Summarysection is importantSummary
Only category 2 is ours today. Go dependencies are fully under our control and already covered by source-level scanning. Every othercategory arrives pre-installed from the upstream Spark image.
Category 5 doesn't need a custom base. The unsuffixed Spark tag is the python3 variant —
apache/spark:4.0.2andapache/spark:4.0.2-python3share digest17dcbf3ceb10. Spark also publishes a scala-only variant, so this category could be dropped by pinning that tag instead. Nothing in the operator pod executes Python the binary is Go, the entrypoint is bash, and PySpark runs in driver/executor pods using the user'sspec.image. But again, we are just carrying extra vulnerabilities for something the operator doesn't need.. Much of what we inherit is inert. The operator shells out tospark-submit, which needs a Java runtime, bash, and a subset of the Spark distribution. Alongside that we carry:eclipse-temurin:<n>-jammy, not-jre-jammy, sojavacand thejdk.*tool modules ship with a process that never compileswget,gnupg2,net-tools,gosu,libnss-wrapper, present for running Spark and for build-time GPGverification
The question for this issue: stay on the upstream Spark image, or build our own base.
Staying upstream means categories 1, 3, 4, 6 and most of 7 remain outside our control — we inherit whatever the ASF ships and wait for the next Spark release to fix it, including for components we never load. Given Kubeflow is a CNCF graduated project, so I think we need to go minimal on security issues specially hig and critical ones.
Building our own base would put categories 5, 6 and 7 in our hands, along with much of 1 and a meaningful share of 3 and 4. It would also mean owning a rebuild cadence, CVE patching, multi-arch builds, and divergence from the ASF's tested Spark distribution — real ongoing cost that currently sits upstream.
Worth deciding deliberately rather than by default.
cc: @vjanelle @juliusvonkohout @nabuskey @vara-bonthu