-
-
Notifications
You must be signed in to change notification settings - Fork 4
feat: add basic openlineage support #718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
739b49a
43235f5
c7313a8
87da728
6c5a3cb
69d746a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| = OpenLineage | ||
|
|
||
| https://openlineage.io/[OpenLineage] is an open standard for data lineage collection. | ||
| The Spark operator can automatically emit lineage events for a `SparkApplication` to an OpenLineage-compatible backend such as https://marquezproject.github.io/marquez/[Marquez], without any manual `sparkConf` wiring. | ||
|
|
||
| When enabled, the operator injects everything required to make the https://openlineage.io/docs/integrations/spark/[OpenLineage Spark listener] work: | ||
|
|
||
| * the OpenLineage Spark listener (appended to `spark.extraListeners`, so any listener you configure yourself is preserved), | ||
| * the OpenLineage jar baked into the Spark image, referenced via `spark.jars` so it shares the same classloader as connectors pulled in through xref:usage-guide/job-dependencies.adoc[`deps.packages`] (for example Apache Iceberg), | ||
| * an HTTP transport pointing at the backend endpoint, | ||
| * a stable lineage namespace and job name (see <<job-name>>), and | ||
| * the `--add-opens java.base/java.security=ALL-UNNAMED` JVM flag the listener requires on the driver and executors. | ||
|
|
||
| == Enabling OpenLineage | ||
|
|
||
| The backend endpoint is not configured on the `SparkApplication` directly. | ||
| Instead, mirroring the xref:usage-guide/logging.adoc[vector aggregator discovery], the operator reads it from a discovery ConfigMap referenced by `configMapName`: | ||
|
|
||
| [source,yaml] | ||
| ---- | ||
| apiVersion: spark.stackable.tech/v1alpha1 | ||
| kind: SparkApplication | ||
| metadata: | ||
| name: orders-by-nation | ||
| spec: | ||
| openLineage: | ||
| enabled: true # <1> | ||
| configMapName: marquez-discovery # <2> | ||
| # namespace: orders-lineage # <3> | ||
| # appName: orders-by-nation # <4> | ||
| ... | ||
| ---- | ||
| <1> Enable OpenLineage event emission. Defaults to `false`, in which case the operator emits nothing OpenLineage-related and behaviour is unchanged. | ||
| <2> Name of a discovery ConfigMap holding the backend endpoint (see below). | ||
| Must be in the same namespace as the `SparkApplication`. | ||
| <3> Optional. The OpenLineage namespace events are reported under. | ||
| Defaults to the application's Kubernetes namespace. | ||
| <4> Optional. The stable OpenLineage job name (see <<job-name>>). | ||
|
|
||
| The discovery ConfigMap holds the backend base URL under the `ADDRESS` key: | ||
|
|
||
| .Example discovery ConfigMap | ||
| [source,yaml] | ||
| ---- | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: marquez-discovery | ||
| data: | ||
| ADDRESS: http://marquez:5000 # <1> | ||
| ---- | ||
| <1> Base URL of the OpenLineage backend, for example the Marquez API service. | ||
|
|
||
| Alternatively, you can set the endpoint directly through `sparkConf` (`spark.openlineage.transport.url`). | ||
| If `enabled: true` and no endpoint can be resolved from either the discovery ConfigMap or `sparkConf`, reconciliation fails with a clear error rather than emitting a config that silently drops events. | ||
|
|
||
| [#job-name] | ||
| == Job name | ||
|
|
||
| The OpenLineage job name is resolved in the following order: | ||
|
|
||
| . `spec.openLineage.appName`, if set. | ||
| . `spark.app.name` from `sparkConf`, if set. | ||
| . `metadata.name` as a last resort. | ||
|
|
||
| The last case additionally emits a Kubernetes warning event. | ||
| Falling back to `metadata.name` is discouraged: if that name carries a timestamp or other run-specific suffix (as generated names often do), every run becomes a separate job in the backend and the run history fragments. | ||
| Set `spec.openLineage.appName` (or `spark.app.name`) to a stable value to keep all runs of the same logical job together. | ||
|
|
||
| == Overriding injected configuration | ||
|
|
||
| Every injected value is a default that goes into the submit configuration *before* your xref:usage-guide/overrides.adoc[`sparkConf`] is merged, so any key you set yourself wins. | ||
| `spark.extraListeners` and `spark.jars` are the exception: the operator *appends* to any value you provide (comma-merged) rather than overwriting it, so your own listeners and jars are kept alongside OpenLineage's. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,30 @@ pub const DEFAULT_LISTENER_CLASS: &str = "cluster-internal"; | |
|
|
||
| pub const DEFAULT_SUBMIT_JOB_RETRY_ON_FAILURE_COUNT: u16 = 0; | ||
|
|
||
| // --- OpenLineage (see the OpenLineage usage guide) --- | ||
|
|
||
| /// The OpenLineage Spark listener, appended to `spark.extraListeners` when OpenLineage is enabled. | ||
| pub const OPENLINEAGE_LISTENER_CLASS: &str = "io.openlineage.spark.agent.OpenLineageSparkListener"; | ||
|
|
||
| /// `local://` URI of the OpenLineage jar baked into the Spark image, referenced via `spark.jars` so | ||
| /// it shares the (child) classloader with `--packages` connectors. Delivering it this way — rather | ||
| /// than on `extraClassPath` (system classloader) — is what lets OpenLineage's connector probes | ||
| /// succeed; see the classpath discussion in the OpenLineage usage guide. | ||
| /// | ||
| /// IMPORTANT: the version MUST stay in sync with the `openlineage-spark-version` build-argument in | ||
| /// `docker-images/spark-k8s/boil-config.toml` (the image is what places the jar at this path). | ||
| pub const OPENLINEAGE_JAR_LOCAL_URI: &str = | ||
| "local:///stackable/spark/openlineage/openlineage-spark_2.13-1.51.0.jar"; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this needs a way that is version agnostic / some proper way to keep this in sync other than a comment.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way we solved similar problems in the past was to use symlinks |
||
|
|
||
| /// The key the OpenLineage discovery ConfigMap must hold, carrying the backend base URL. Mirrors the | ||
| /// `ADDRESS` contract of the existing `vectorAggregatorConfigMapName` discovery ConfigMap. | ||
| pub const OPENLINEAGE_CONFIG_MAP_ADDRESS_KEY: &str = "ADDRESS"; | ||
|
|
||
| /// Java module-system flag OpenLineage requires on Spark 4.x: without it the driver throws a | ||
|
sweb marked this conversation as resolved.
|
||
| /// non-fatal `InaccessibleObjectException` and silently degrades extension-interface lineage. | ||
| /// Appended to both driver and executor `extraJavaOptions`. | ||
| pub const OPENLINEAGE_ADD_OPENS: &str = "--add-opens java.base/java.security=ALL-UNNAMED"; | ||
|
|
||
| /// The JVM `security.properties` entries the operator sets by default (DNS cache TTLs). | ||
| pub fn default_jvm_security_properties() -> BTreeMap<String, String> { | ||
| [ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see other comment, this needs proper Spark 3 treatment.