Zum Inhalt springen

Spark in JupyterLab verwenden

Zuletzt aktualisiert am

STACKIT Notebooks werden vorkonfiguriert mit Spark-Unterstützung ausgeliefert. Je nachdem, ob Ihr Code interaktiv in JupyterLab oder automatisiert über einen Airflow/Papermill-Job ausgeführt wird, unterscheidet sich jedoch die Initialisierung der Spark-Sitzung.

Diese Anleitung erklärt, wie Sie eine Spark-Sitzung erstellen, die eine Verbindung zu Ihrem Data Lakehouse (STACKIT Dremio/Iceberg) herstellt.

Es gibt zwei Hauptarten, wie Ihr Notebook ausgeführt werden kann:

  • Interaktiver Modus: Sie führen die Zellen manuell in der JupyterLab-Benutzeroberfläche aus. Sie müssen Anmeldedaten (wie ein Personal Access Token) bereitstellen, um sich am Katalog zu authentifizieren.
  • Papermill-/Produktionsmodus: Das Notebook wird über STACKIT Workflows (Apache Airflow) ausgeführt. Die Umgebung ist bereits mit den erforderlichen Tokens und der Katalog-URI vorkonfiguriert.

Das folgende Code-Beispiel zeigt, wie beide Umgebungen gehandhabt werden. Es prüft das Vorhandensein der Umgebungsvariablen STACKIT__PAPERMILL, um zu entscheiden, ob die automatische Konfiguration oder die manuelle Credential-Injektion verwendet werden soll.

import os
import stackit_spark
# 1. Prüfen, ob der Code über Airflow/Papermill ausgeführt wird
if "STACKIT__PAPERMILL" in os.environ:
# Im Papermill-Modus bezieht get_spark() die Konfiguration
# automatisch aus der Umgebung / dem Operator.
CATALOG_NAME = "catalog"
spark = get_spark()
else:
# 2. Interaktiver Modus: Manuelle Konfiguration erforderlich
# Diese Variablen sollten im JupyterLab-Terminal oder über Umgebungseinstellungen gesetzt sein
DREMIO_PAT = os.environ["DREMIO_PAT"]
DREMIO_ADDRESS = os.environ["DREMIO_ADDRESS"]
CATALOG_URI = os.environ["CATALOG_URI"]
CATALOG_NAME = "stackit"
ICEBERG_VERSION = "1.9.1"
JAR_PATH = os.path.abspath("./authmgr-oauth2-runtime-0.1.2.jar")
packages = (
f"org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:{ICEBERG_VERSION},"
f"org.apache.iceberg:iceberg-azure-bundle:{ICEBERG_VERSION},"
f"org.apache.iceberg:iceberg-aws-bundle:{ICEBERG_VERSION},"
f"org.apache.iceberg:iceberg-gcp-bundle:{ICEBERG_VERSION}"
)
spark = stackit_spark.get_spark(
additional_config={
"spark.jars.packages": packages,
"spark.jars": JAR_PATH,
"spark.sql.extensions": "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions",
"spark.driver.userClassPathFirst": "true",
# --- Iceberg Katalog-Konfiguration ---
f"spark.sql.catalog.{CATALOG_NAME}": "org.apache.iceberg.spark.SparkCatalog",
f"spark.sql.catalog.{CATALOG_NAME}.type": "rest",
f"spark.sql.catalog.{CATALOG_NAME}.warehouse": "default",
f"spark.sql.catalog.{CATALOG_NAME}.uri": CATALOG_URI,
# Konfigurationen zur Bypassung von Validierungsprüfungen
f"spark.sql.catalog.{CATALOG_NAME}.check-location": "false",
f"spark.sql.catalog.{CATALOG_NAME}.validation.check-location-exists": "false",
f"spark.sql.catalog.{CATALOG_NAME}.metadata.check-location-exists": "false",
# --- OAuth2 Authentifizierung ---
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.type": "com.dremio.iceberg.authmgr.oauth2.OAuth2Manager",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.client-auth": "none",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.token-endpoint": f"https://{DREMIO_ADDRESS}/oauth/token",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.grant-type": "urn:ietf:params:oauth:grant-type:token-exchange",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.client-id": "dremio",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.scope": "dremio.all",
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.token-exchange.subject-token": DREMIO_PAT,
f"spark.sql.catalog.{CATALOG_NAME}.rest.auth.oauth2.token-exchange.subject-token-type": "urn:ietf:params:oauth:token-type:dremio:personal-access-token",
# --- STS/Vended Credentials Konfiguration ---
f"spark.sql.catalog.{CATALOG_NAME}.header.X-Iceberg-Access-Delegation": "vended-credentials",
f"spark.sql.catalog.{CATALOG_NAME}.io-impl": "org.apache.iceberg.aws.s3.S3FileIO",
f"spark.sql.catalog.{CATALOG_NAME}.s3.vended-credentials-enabled": "true"
}
)
spark.sql(f"USE {CATALOG_NAME}").show(truncate=False)
spark.sql("SHOW NAMESPACES").show()
spark.sql(f"CREATE TABLE IF NOT EXISTS {CATALOG_NAME}.test.customer (id INT, name STRING, age INT) USING iceberg")
spark.sql(f"SELECT * FROM {CATALOG_NAME}.test.customer ORDER BY id").show()
spark.sql(f"INSERT INTO {CATALOG_NAME}.test.customer VALUES (1, 'Daniel', 30), (2, 'Jane', 25)")
spark.sql(f"SELECT * FROM {CATALOG_NAME}.test.customer ORDER BY id").show()
  1. Umgebungsvariablen setzen

    Öffnen Sie ein Terminal in JupyterLab und exportieren Sie Ihr Dremio Personal Access Token (PAT):

    Terminal-Fenster
    export DREMIO_PAT="ihr-token-hier"
    export DREMIO_ADDRESS=ihre-dremio-url
    export CATALOG_URI=ihre-katalog-uri

    Alternativ können Sie diese Umgebungsvariablen direkt in einer Code-Zelle durch %env DREMIO_PAT="ihr-token-hier"setzen.

  2. Bibliotheken importieren Die Bibliothek stackit_spark ist ein STACKIT-spezifischer Wrapper um pyspark. Sie übernimmt die zugrunde liegende Konfiguration, die für die Kommunikation mit dem Spark-Cluster erforderlich ist.

  3. Katalog definieren Im obigen Code definieren wir den CATALOG als stackit. Dieser dient als Namespace innerhalb Ihrer Spark SQL-Abfragen (zum Beispiel SELECT * FROM stackit.DB.TABLE).

  4. SQL-Abfragen ausführen Sobald das spark-Objekt initialisiert ist, können Sie spark.sql() verwenden, um mit Ihren Iceberg-Tabellen wie mit einer gewöhnlichen SQL-Datenbank zu interagieren.

Die Konfiguration nutzt den REST-Katalog-Typ von Iceberg. Dies stellt sicher, dass jede über Spark erstellte oder geänderte Tabelle sofort in Dremio sichtbar und lesbar ist. Voraussetzung dafür ist, dass Sie dieselbe Warehouse-URI verwenden.