Creare cluster, notebook e processi con Terraform

Questo articolo illustra come usare il provider Databricks Terraform per creare un cluster, un notebook e un processo in un'area di lavoro di Azure Databricks esistente.

Questo articolo è complementare agli articoli introduttivi di Azure Databricks seguenti:

È anche possibile adattare le configurazioni di Terraform in questo articolo per creare cluster, notebook e processi personalizzati nelle aree di lavoro.

Passaggio 1: Creare e configurare il progetto Terraform

  1. Creare un progetto Terraform seguendo le istruzioni nella sezione Requisiti dell'articolo Panoramica del provider Terraform di Databricks.

  2. Per creare un cluster, creare un file denominato cluster.tfe aggiungere il contenuto seguente al file. Questo contenuto crea un cluster con la quantità minima di risorse consentite. Questo cluster usa la versione LTS (Long Term Support) più recente di Databricks Runtime.

    Per un cluster che funziona con Unity Catalog:

    variable "cluster_name" {}
    variable "cluster_autotermination_minutes" {}
    variable "cluster_num_workers" {}
    variable "cluster_data_security_mode" {}
    
    # Create the cluster with the "smallest" amount
    # of resources allowed.
    data "databricks_node_type" "smallest" {
      local_disk = true
    }
    
    # Use the latest Databricks Runtime
    # Long Term Support (LTS) version.
    data "databricks_spark_version" "latest_lts" {
      long_term_support = true
    }
    
    resource "databricks_cluster" "this" {
      cluster_name            = var.cluster_name
      node_type_id            = data.databricks_node_type.smallest.id
      spark_version           = data.databricks_spark_version.latest_lts.id
      autotermination_minutes = var.cluster_autotermination_minutes
      num_workers             = var.cluster_num_workers
      data_security_mode      = var.cluster_data_security_mode
    }
    
    output "cluster_url" {
     value = databricks_cluster.this.url
    }
    

    Per un cluster all-purpose:

    variable "cluster_name" {
      description = "A name for the cluster."
      type        = string
      default     = "My Cluster"
    }
    
    variable "cluster_autotermination_minutes" {
      description = "How many minutes before automatically terminating due to inactivity."
      type        = number
      default     = 60
    }
    
    variable "cluster_num_workers" {
      description = "The number of workers."
      type        = number
      default     = 1
    }
    
    # Create the cluster with the "smallest" amount
    # of resources allowed.
    data "databricks_node_type" "smallest" {
      local_disk = true
    }
    
    # Use the latest Databricks Runtime
    # Long Term Support (LTS) version.
    data "databricks_spark_version" "latest_lts" {
      long_term_support = true
    }
    
    resource "databricks_cluster" "this" {
      cluster_name            = var.cluster_name
      node_type_id            = data.databricks_node_type.smallest.id
      spark_version           = data.databricks_spark_version.latest_lts.id
      autotermination_minutes = var.cluster_autotermination_minutes
      num_workers             = var.cluster_num_workers
    }
    
    output "cluster_url" {
     value = databricks_cluster.this.url
    }
    
  3. Per creare un cluster, creare un altro file denominato cluster.auto.tfvarse aggiungere il contenuto seguente al file. Questo file contiene valori di variabile per la personalizzazione del cluster. Sostituire i valori segnaposto con i propri valori.

    Per un cluster che funziona con Unity Catalog:

    cluster_name                    = "My Cluster"
    cluster_autotermination_minutes = 60
    cluster_num_workers             = 1
    cluster_data_security_mode      = "SINGLE_USER"
    

    Per un cluster all-purpose:

    cluster_name                    = "My Cluster"
    cluster_autotermination_minutes = 60
    cluster_num_workers             = 1
    
  4. Per creare un notebook, creare un altro file denominato notebook.tfe aggiungere il contenuto seguente al file:

    variable "notebook_subdirectory" {
      description = "A name for the subdirectory to store the notebook."
      type        = string
      default     = "Terraform"
    }
    
    variable "notebook_filename" {
      description = "The notebook's filename."
      type        = string
    }
    
    variable "notebook_language" {
      description = "The language of the notebook."
      type        = string
    }
    
    resource "databricks_notebook" "this" {
      path     = "${data.databricks_current_user.me.home}/${var.notebook_subdirectory}/${var.notebook_filename}"
      language = var.notebook_language
      source   = "./${var.notebook_filename}"
    }
    
    output "notebook_url" {
     value = databricks_notebook.this.url
    }
    
  5. Se si crea un cluster, salvare il codice del notebook seguente in un file nella stessa directory del notebook.tf file:

    Per il notebook Python per Esercitazione: Eseguire una pipeline di analisi lakehouse end-to-end, un file denominato notebook-getting-started-lakehouse-e2e.py con il contenuto seguente:

    # Databricks notebook source
    external_location = "<your_external_location>"
    catalog = "<your_catalog>"
    
    dbutils.fs.put(f"{external_location}/foobar.txt", "Hello world!", True)
    display(dbutils.fs.head(f"{external_location}/foobar.txt"))
    dbutils.fs.rm(f"{external_location}/foobar.txt")
    
    display(spark.sql(f"SHOW SCHEMAS IN {catalog}"))
    
    # COMMAND ----------
    
    from pyspark.sql.functions import col
    
    # Set parameters for isolation in workspace and reset demo
    username = spark.sql("SELECT regexp_replace(current_user(), '[^a-zA-Z0-9]', '_')").first()[0]
    database = f"{catalog}.e2e_lakehouse_{username}_db"
    source = f"{external_location}/e2e-lakehouse-source"
    table = f"{database}.target_table"
    checkpoint_path = f"{external_location}/_checkpoint/e2e-lakehouse-demo"
    
    spark.sql(f"SET c.username='{username}'")
    spark.sql(f"SET c.database={database}")
    spark.sql(f"SET c.source='{source}'")
    
    spark.sql("DROP DATABASE IF EXISTS ${c.database} CASCADE")
    spark.sql("CREATE DATABASE ${c.database}")
    spark.sql("USE ${c.database}")
    
    # Clear out data from previous demo execution
    dbutils.fs.rm(source, True)
    dbutils.fs.rm(checkpoint_path, True)
    
    # Define a class to load batches of data to source
    class LoadData:
    
      def __init__(self, source):
        self.source = source
    
      def get_date(self):
        try:
          df = spark.read.format("json").load(source)
        except:
            return "2016-01-01"
        batch_date = df.selectExpr("max(distinct(date(tpep_pickup_datetime))) + 1 day").first()[0]
        if batch_date.month == 3:
          raise Exception("Source data exhausted")
          return batch_date
    
      def get_batch(self, batch_date):
        return (
          spark.table("samples.nyctaxi.trips")
            .filter(col("tpep_pickup_datetime").cast("date") == batch_date)
        )
    
      def write_batch(self, batch):
        batch.write.format("json").mode("append").save(self.source)
    
      def land_batch(self):
        batch_date = self.get_date()
        batch = self.get_batch(batch_date)
        self.write_batch(batch)
    
    RawData = LoadData(source)
    
    # COMMAND ----------
    
    RawData.land_batch()
    
    # COMMAND ----------
    
    # Import functions
    from pyspark.sql.functions import col, current_timestamp
    
    # Configure Auto Loader to ingest JSON data to a Delta table
    (spark.readStream
      .format("cloudFiles")
      .option("cloudFiles.format", "json")
      .option("cloudFiles.schemaLocation", checkpoint_path)
      .load(file_path)
      .select("*", col("_metadata.file_path").alias("source_file"), current_timestamp().alias("processing_time"))
      .writeStream
      .option("checkpointLocation", checkpoint_path)
      .trigger(availableNow=True)
      .option("mergeSchema", "true")
      .toTable(table))
    
    # COMMAND ----------
    
    df = spark.read.table(table_name)
    
    # COMMAND ----------
    
    display(df)
    

    Per il notebook Python per Avvio rapido: Eseguire un processo Spark nell'area di lavoro di Azure Databricks usando il portale di Azure, un file denominato notebook-quickstart-create-databricks-workspace-portal.py con il contenuto seguente:

    # Databricks notebook source
    blob_account_name = "azureopendatastorage"
    blob_container_name = "citydatacontainer"
    blob_relative_path = "Safety/Release/city=Seattle"
    blob_sas_token = r""
    
    # COMMAND ----------
    
    wasbs_path = 'wasbs://%s@%s.blob.core.windows.net/%s' % (blob_container_name, blob_account_name,blob_relative_path)
    spark.conf.set('fs.azure.sas.%s.%s.blob.core.windows.net' % (blob_container_name, blob_account_name), blob_sas_token)
    print('Remote blob path: ' + wasbs_path)
    
    # COMMAND ----------
    
    df = spark.read.parquet(wasbs_path)
    print('Register the DataFrame as a SQL temporary view: source')
    df.createOrReplaceTempView('source')
    
    # COMMAND ----------
    
    print('Displaying top 10 rows: ')
    display(spark.sql('SELECT * FROM source LIMIT 10'))
    
  6. Se si crea un notebook, creare un altro file denominato notebook.auto.tfvarse aggiungere il contenuto seguente al file. Questo file contiene valori di variabile per la personalizzazione della configurazione del notebook.

    Per il notebook Python per Esercitazione: Eseguire una pipeline di analisi end-to-end lakehouse:

    notebook_subdirectory = "Terraform"
    notebook_filename     = "notebook-getting-started-lakehouse-e2e.py"
    notebook_language     = "PYTHON"
    

    Per il notebook Python per Avvio rapido: Eseguire un processo Spark nell'area di lavoro di Azure Databricks usando il portale di Azure:

    notebook_subdirectory = "Terraform"
    notebook_filename     = "notebook-quickstart-create-databricks-workspace-portal.py"
    notebook_language     = "PYTHON"
    
  7. Se si crea un notebook, nell'area di lavoro di Azure Databricks assicurarsi di configurare eventuali requisiti per l'esecuzione corretta del notebook, facendo riferimento alle istruzioni seguenti per:

  8. Per creare il processo, creare un altro file denominato job.tfe aggiungere il contenuto seguente al file. Questo contenuto crea un processo per eseguire il notebook.

    variable "job_name" {
      description = "A name for the job."
      type        = string
      default     = "My Job"
    }
    
    resource "databricks_job" "this" {
      name = var.job_name
      existing_cluster_id = databricks_cluster.this.cluster_id
      notebook_task {
        notebook_path = databricks_notebook.this.path
      }
      email_notifications {
        on_success = [ data.databricks_current_user.me.user_name ]
        on_failure = [ data.databricks_current_user.me.user_name ]
      }
    }
    
    output "job_url" {
      value = databricks_job.this.url
    }
    
  9. Se si sta creando un processo, creare un altro file denominato job.auto.tfvarse aggiungere il contenuto seguente al file. Questo file contiene un valore variabile per personalizzare la configurazione del processo.

    job_name = "My Job"
    

Passaggio 2: Eseguire le configurazioni

In questo passaggio si eseguono le configurazioni terraform per distribuire il cluster, il notebook e il processo nell'area di lavoro di Azure Databricks.

  1. Verificare se le configurazioni di Terraform sono valide eseguendo il terraform validate comando . Se vengono segnalati errori, correggerli ed eseguire di nuovo il comando.

    terraform validate
    
  2. Verificare le operazioni eseguite da Terraform nell'area di lavoro, prima che Terraform lo esegua effettivamente, eseguendo il terraform plan comando .

    terraform plan
    
  3. Distribuire il cluster, il notebook e il processo nell'area di lavoro eseguendo il terraform apply comando . Quando viene richiesto di distribuire, digitare yes e premere INVIO.

    terraform apply
    

    Terraform distribuisce le risorse specificate nel progetto. La distribuzione di queste risorse (in particolare un cluster) può richiedere alcuni minuti.

Passaggio 3: Esplorare i risultati

  1. Se è stato creato un cluster, nell'output del terraform apply comando copiare il collegamento accanto a cluster_urle incollarlo nella barra degli indirizzi del Web browser.

  2. Se è stato creato un notebook, nell'output del terraform apply comando copiare il collegamento accanto a notebook_urle incollarlo nella barra degli indirizzi del Web browser.

    Nota

    Prima di usare il notebook, potrebbe essere necessario personalizzarne il contenuto. Vedere la documentazione correlata su come personalizzare il notebook.

  3. Se è stato creato un processo, nell'output del terraform apply comando copiare il collegamento accanto a job_urle incollarlo nella barra degli indirizzi del Web browser.

    Nota

    Prima di eseguire il notebook, potrebbe essere necessario personalizzarne il contenuto. Vedere i collegamenti all'inizio di questo articolo per la documentazione correlata su come personalizzare il notebook.

  4. Se è stato creato un processo, eseguire il processo come segue:

    1. Fare clic su Esegui ora nella pagina del processo.
    2. Al termine dell'esecuzione del processo, per visualizzare i risultati dell'esecuzione del processo, nell'elenco Esecuzioni completate (ultimi 60 giorni) nella pagina del processo fare clic sulla voce ora più recente nella colonna Ora di inizio . Il riquadro Output mostra il risultato dell'esecuzione del codice del notebook.

Passaggio 4: Pulire

In questo passaggio si eliminano le risorse precedenti dall'area di lavoro.

  1. Verificare le operazioni eseguite da Terraform nell'area di lavoro, prima che Terraform lo esegua effettivamente, eseguendo il terraform plan comando .

    terraform plan
    
  2. Eliminare il cluster, il notebook e il processo dall'area di lavoro eseguendo il terraform destroy comando . Quando viene richiesto di eliminare, digitare yes e premere INVIO.

    terraform destroy
    

    Terraform elimina le risorse specificate nel progetto.