Partilhar via


CI/CD com o Jenkins no Azure Databricks

Observação

Este artigo abrange o Jenkins, que é desenvolvido por terceiros. Para entrar em contato com o provedor, consulte a Ajuda do Jenkins.

Existem inúmeras ferramentas de CI/CD que você pode usar para gerenciar e executar seus pipelines de CI/CD. Este artigo ilustra como usar o servidor de automação Jenkins . CI/CD é um padrão de design, portanto, as etapas e estágios descritos neste artigo devem ser transferidos com algumas alterações na linguagem de definição de pipeline em cada ferramenta. Além disso, grande parte do código neste pipeline de exemplo executa código Python padrão, que você pode invocar em outras ferramentas. Para obter uma visão geral do CI/CD no Azure Databricks, consulte CI/CD no Azure Databricks.

Para obter informações sobre como usar o Azure DevOps com o Azure Databricks, consulte Integração e entrega contínuas no Azure Databricks usando o Azure DevOps.

Fluxo de trabalho de desenvolvimento de CI/CD

A Databricks sugere o seguinte fluxo de trabalho para o desenvolvimento de CI/CD com Jenkins:

  1. Crie um repositório ou use um repositório existente com seu provedor Git de terceiros.
  2. Conecte sua máquina de desenvolvimento local ao mesmo repositório de terceiros. Para obter instruções, consulte a documentação do seu provedor Git de terceiros.
  3. Puxe todos os artefatos atualizados existentes (como blocos de anotações, arquivos de código e scripts de compilação) do repositório de terceiros para sua máquina de desenvolvimento local.
  4. Conforme desejado, crie, atualize e teste artefatos em sua máquina de desenvolvimento local. Em seguida, envie todos os artefatos novos e alterados de sua máquina de desenvolvimento local para o repositório de terceiros. Para obter instruções, consulte a documentação do seu provedor Git de terceiros.
  5. Repita os passos 3 e 4 conforme necessário.
  6. Use o Jenkins para transferir automaticamente artefactos do seu repositório de terceiros para a sua máquina local de desenvolvimento ou para o espaço de trabalho Azure Databricks; construir, testar e executar código na sua máquina de desenvolvimento local ou no espaço de trabalho Azure Databricks; e reportar os resultados dos testes e execuções. Embora possa executar o Jenkins manualmente, em implementações reais instrui o fornecedor terceirizado do Git para executar o Jenkins sempre que ocorre um evento específico, como um pedido de pull no repositório.

O restante deste artigo utiliza um projeto de exemplo para descrever como usar o Jenkins para implementar o fluxo de trabalho de desenvolvimento CI/CD anterior.

Para obter informações sobre como usar o Azure DevOps em vez do Jenkins, consulte Integração e entrega contínuas no Azure Databricks usando o Azure DevOps.

Configuração da máquina de desenvolvimento local

O exemplo deste artigo usa Jenkins para instruir a CLI do Databricks e o Databricks Asset Bundles a fazer o seguinte:

  1. Crie um arquivo de roda Python em sua máquina de desenvolvimento local.
  2. Implante o arquivo de roda Python criado junto com arquivos Python adicionais e blocos de anotações Python de sua máquina de desenvolvimento local em um espaço de trabalho do Azure Databricks.
  3. Teste e execute o arquivo de roda Python carregado e os blocos de anotações nesse espaço de trabalho.

Para configurar sua máquina de desenvolvimento local para instruir seu espaço de trabalho do Azure Databricks a executar os estágios de compilação e carregamento para este exemplo, faça o seguinte em sua máquina de desenvolvimento local:

Etapa 1: Instalar as ferramentas necessárias

Nesta etapa, instalem o Databricks CLI, Jenkins, jq e as ferramentas de construção de roda do Python na sua máquina de desenvolvimento local. Essas ferramentas são necessárias para executar este exemplo.

  1. Instale a CLI do Databricks versão 0.205 ou superior, se ainda não o tiver feito. Jenkins utiliza a interface de linha de comandos do Databricks para passar as instruções do teste deste exemplo e executá-las no teu espaço de trabalho. Consulte Instalar ou atualizar a CLI do Databricks.

  2. Instale e inicie o Jenkins, se ainda não o fez. Consulte Instalando o Jenkins para Linux, macOS ou Windows.

  3. Instale o jq. Este exemplo usa jq para analisar algumas saídas de comando formatadas em JSON.

  4. Use pip para instalar as ferramentas de construção de roda Python com o seguinte comando (alguns sistemas podem exigir que você use pip3 em vez de pip):

    pip install --upgrade wheel
    

Etapa 2: Criar um pipeline Jenkins

Nesta etapa, você usa Jenkins para criar um Jenkins Pipeline para o exemplo deste artigo. Jenkins fornece alguns tipos de projeto diferentes para criar pipelines de CI/CD. Jenkins Pipelines fornecem uma interface para definir estágios em um Jenkins Pipeline usando o código Groovy para chamar e configurar plug-ins Jenkins.

Tipos de projeto Jenkins

Para criar o Jenkins Pipeline em Jenkins:

  1. Depois de iniciar o Jenkins, no Painel do Jenkins, clique em Novo Item.
  2. Em Digite um nome de item, digite um nome para o Jenkins Pipeline, por exemplo jenkins-demo.
  3. Clique no ícone Tipo de projeto Pipeline .
  4. Clique em OK. A página Configurar do Jenkins Pipeline é exibida.
  5. Na área Pipeline, na lista suspensa Definição, selecione Script de Pipeline do SCM.
  6. Na lista suspensa SCM, selecione Git.
  7. Para URL do repositório, digite a URL para o repositório hospedado pelo seu provedor Git de terceiros.
  8. Em Especificador de ramificação, digite */<branch-name>, onde <branch-name> é o nome da ramificação no repositório que você deseja usar, por exemplo */main.
  9. Em Caminho do script, digite Jenkinsfile, se ainda não estiver definido. Você irá criar o Jenkinsfile mais adiante neste artigo.
  10. Desmarque a caixa intitulada Processo de compra leve, se já estiver marcada.
  11. Clique em Salvar.

Etapa 3: Adicionar variáveis de ambiente global ao Jenkins

Nesta etapa, você adiciona três variáveis de ambiente global ao Jenkins. Jenkins passa essas variáveis de ambiente para a CLI do Databricks. A CLI Databricks precisa dos valores destas variáveis de ambiente para autenticar no seu workspace Azure Databricks. Este exemplo usa a autenticação OAuth máquina-a-máquina (M2M) para uma entidade de serviço (embora outros tipos de autenticação também estejam disponíveis). Para configurar a autenticação OAuth M2M para seu espaço de trabalho do Azure Databricks, consulte Autorizar o acesso da entidade de serviço ao Azure Databricks com OAuth.

As três variáveis de ambiente global para este exemplo são:

  • DATABRICKS_HOST, defina a URL do espaço de trabalho do Azure Databricks, começando com https://. Consulte Nomes, URLs e IDs das instâncias do espaço de trabalho.
  • DATABRICKS_CLIENT_ID, configurado como ID do cliente da entidade de serviço, que também é conhecido como ID da aplicação.
  • DATABRICKS_CLIENT_SECRET, definido como o segredo OAuth do Azure Databricks da entidade de serviço.

Para definir variáveis de ambiente global no Jenkins, no seu Painel do Jenkins:

  1. Na barra lateral, clique em Gerenciar Jenkins.
  2. Na seção Configuração do Sistema, clique em Sistema.
  3. Na secção de propriedades globais , assinala a caixa intitulada Variáveis de Ambiente.
  4. Clique em Adicionar e insira Nome eValor da variável de ambiente. Repita isso para cada variável de ambiente adicional.
  5. Quando terminar de adicionar variáveis de ambiente, clique em Guardar para voltar ao seu Painel Jenkins.

Projete o oleoduto Jenkins

Jenkins fornece alguns tipos de projeto diferentes para criar pipelines de CI/CD. Este exemplo implementa um Jenkins Pipeline. Jenkins Pipelines fornecem uma interface para definir estágios em um Jenkins Pipeline usando o código Groovy para chamar e configurar plug-ins Jenkins.

Você escreve uma definição de Jenkins Pipeline em um arquivo de texto chamado Jenkinsfile, que por sua vez é verificado no repositório de controle de origem de um projeto. Para obter mais informações, consulte Jenkins Pipeline. Aqui está o Jenkins Pipeline para o exemplo deste artigo. Neste exemplo Jenkinsfile, substitua os seguintes espaços reservados:

  • Substitua <user-name> e <repo-name> pelo nome do utilizador e nome do repositório fornecidos pelo seu provedor Git de terceiros. Este artigo usa uma URL do GitHub como exemplo.
  • Substitua <release-branch-name> pelo nome do ramo de lançamento no seu repositório. Por exemplo, poderia ser main.
  • Substitua <databricks-cli-installation-path> pelo caminho na sua máquina de desenvolvimento local onde a CLI do Databricks está instalada. Por exemplo, no macOS isso pode ser /usr/local/bin.
  • Substitua <jq-installation-path> pelo caminho da sua máquina de desenvolvimento local onde jq está instalado. Por exemplo, no macOS isso pode ser /usr/local/bin.
  • Substitua <job-prefix-name> por alguma cadeia de caracteres para ajudar a identificar exclusivamente os trabalhos criados em seu espaço de trabalho para este exemplo. Por exemplo, poderia ser jenkins-demo.
  • Observe que BUNDLETARGET está definido como dev, que é o nome do destino do Databricks Asset Bundle definido posteriormente neste artigo. Em implementações do mundo real, altera isto para o nome do teu próprio alvo do bundle. Mais detalhes sobre destinos de pacote são fornecidos mais adiante neste artigo.

Aqui está o Jenkinsfile, que deve ser adicionado à raiz do seu repositório:

// Filename: Jenkinsfile
node {
  def GITREPOREMOTE = "https://github.com/<user-name>/<repo-name>.git"
  def GITBRANCH     = "<release-branch-name>"
  def DBCLIPATH     = "<databricks-cli-installation-path>"
  def JQPATH        = "<jq-installation-path>"
  def JOBPREFIX     = "<job-prefix-name>"
  def BUNDLETARGET  = "dev"

  stage('Checkout') {
    git branch: GITBRANCH, url: GITREPOREMOTE
  }
  stage('Validate Bundle') {
    sh """#!/bin/bash
          ${DBCLIPATH}/databricks bundle validate -t ${BUNDLETARGET}
       """
  }
  stage('Deploy Bundle') {
    sh """#!/bin/bash
          ${DBCLIPATH}/databricks bundle deploy -t ${BUNDLETARGET}
       """
  }
  stage('Run Unit Tests') {
    sh """#!/bin/bash
          ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} run-unit-tests
       """
  }
  stage('Run Notebook') {
    sh """#!/bin/bash
          ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} run-dabdemo-notebook
       """
  }
  stage('Evaluate Notebook Runs') {
    sh """#!/bin/bash
          ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} evaluate-notebook-runs
       """
  }
  stage('Import Test Results') {
    def DATABRICKS_BUNDLE_WORKSPACE_ROOT_PATH
    def getPath = "${DBCLIPATH}/databricks bundle validate -t ${BUNDLETARGET} | ${JQPATH}/jq -r .workspace.file_path"
    def output = sh(script: getPath, returnStdout: true).trim()

    if (output) {
      DATABRICKS_BUNDLE_WORKSPACE_ROOT_PATH = "${output}"
    } else {
      error "Failed to capture output or command execution failed: ${getPath}"
    }

    sh """#!/bin/bash
          ${DBCLIPATH}/databricks workspace export-dir \
          ${DATABRICKS_BUNDLE_WORKSPACE_ROOT_PATH}/Validation/Output/test-results \
          ${WORKSPACE}/Validation/Output/test-results \
          -t ${BUNDLETARGET} \
          --overwrite
       """
  }
  stage('Publish Test Results') {
    junit allowEmptyResults: true, testResults: '**/test-results/*.xml', skipPublishingChecks: true
  }
}

O restante deste artigo descreve cada estágio neste Jenkins Pipeline e como configurar os artefatos e comandos para Jenkins executar nesse estágio.

Extraia os artefatos mais recentes do repositório de terceiros

O primeiro estágio neste Jenkins Pipeline, o Checkout estágio, é definido da seguinte forma:

stage('Checkout') {
  git branch: GITBRANCH, url: GITREPOREMOTE
}

Este estágio garante que o diretório de trabalho que Jenkins usa em sua máquina de desenvolvimento local tenha os artefatos mais recentes do seu repositório Git de terceiros. Normalmente, Jenkins define esse diretório de trabalho como <your-user-home-directory>/.jenkins/workspace/<pipeline-name>. Isso permite que você, na mesma máquina de desenvolvimento local, mantenha sua própria cópia dos artefatos em desenvolvimento separada dos artefatos que Jenkins usa do repositório Git de terceiros.

Validar o pacote de ativos Databricks

O segundo estágio neste Jenkins Pipeline, o Validate Bundle estágio, é definido da seguinte forma:

stage('Validate Bundle') {
  sh """#!/bin/bash
        ${DBCLIPATH}/databricks bundle validate -t ${BUNDLETARGET}
     """
}

Esta fase garante que o pacote, que define os fluxos de trabalho para testar e executar os seus artefactos, está sintaticamente correto. Os Databricks Asset Bundles tornam possível expressar dados completos, análises e projetos de ML como uma coleção de ficheiros fonte. Consulte O que são os Databricks Asset Bundles?.

Para definir o pacote para este artigo, crie um arquivo nomeado databricks.yml na raiz do repositório clonado em sua máquina local. Neste arquivo de exemplo databricks.yml, substitua os seguintes marcadores de posição:

  • Substitua <bundle-name> por um nome programático exclusivo para o pacote. Por exemplo, poderia ser jenkins-demo.
  • Substitua <job-prefix-name> por alguma cadeia de caracteres para ajudar a identificar exclusivamente os trabalhos criados em seu espaço de trabalho para este exemplo. Por exemplo, poderia ser jenkins-demo. Ele deve corresponder ao JOBPREFIX valor em seu Jenkinsfile.
  • Substitua <spark-version-id> pelo ID de versão do Databricks Runtime para seus clusters de trabalho, por exemplo 13.3.x-scala2.12.
  • Substitua <cluster-node-type-id> pelo ID do tipo de nó para os agrupamentos de trabalho, como Standard_DS3_v2.
  • Observe que dev no mapeamento é o mesmo que targets no BUNDLETARGET Jenkinsfile. Um destino de pacote especifica o host e os comportamentos de implantação relacionados.

Aqui está o databricks.yml arquivo, que deve ser adicionado à raiz do seu repositório para que este exemplo funcione corretamente:

# Filename: databricks.yml
bundle:
  name: <bundle-name>

variables:
  job_prefix:
    description: A unifying prefix for this bundle's job and task names.
    default: <job-prefix-name>
  spark_version:
    description: The cluster's Spark version ID.
    default: <spark-version-id>
  node_type_id:
    description: The cluster's node type ID.
    default: <cluster-node-type-id>

artifacts:
  dabdemo-wheel:
    type: whl
    path: ./Libraries/python/dabdemo

resources:
  jobs:
    run-unit-tests:
      name: ${var.job_prefix}-run-unit-tests
      tasks:
        - task_key: ${var.job_prefix}-run-unit-tests-task
          new_cluster:
            spark_version: ${var.spark_version}
            node_type_id: ${var.node_type_id}
            num_workers: 1
            spark_env_vars:
              WORKSPACEBUNDLEPATH: ${workspace.root_path}
          notebook_task:
            notebook_path: ./run_unit_tests.py
            source: WORKSPACE
          libraries:
            - pypi:
                package: pytest
    run-dabdemo-notebook:
      name: ${var.job_prefix}-run-dabdemo-notebook
      tasks:
        - task_key: ${var.job_prefix}-run-dabdemo-notebook-task
          new_cluster:
            spark_version: ${var.spark_version}
            node_type_id: ${var.node_type_id}
            num_workers: 1
            data_security_mode: SINGLE_USER
            spark_env_vars:
              WORKSPACEBUNDLEPATH: ${workspace.root_path}
          notebook_task:
            notebook_path: ./dabdemo_notebook.py
            source: WORKSPACE
          libraries:
            - whl: '/Workspace${workspace.root_path}/files/Libraries/python/dabdemo/dist/dabdemo-0.0.1-py3-none-any.whl'
    evaluate-notebook-runs:
      name: ${var.job_prefix}-evaluate-notebook-runs
      tasks:
        - task_key: ${var.job_prefix}-evaluate-notebook-runs-task
          new_cluster:
            spark_version: ${var.spark_version}
            node_type_id: ${var.node_type_id}
            num_workers: 1
            spark_env_vars:
              WORKSPACEBUNDLEPATH: ${workspace.root_path}
          spark_python_task:
            python_file: ./evaluate_notebook_runs.py
            source: WORKSPACE
          libraries:
            - pypi:
                package: unittest-xml-reporting

targets:
  dev:
    mode: development

Para obter mais informações sobre o arquivo databricks.yml, consulte a Configuração do Databricks Asset Bundle.

Implantar o pacote em seu espaço de trabalho

O terceiro estágio do Jenkins Pipeline, intitulado Deploy Bundle, é definido da seguinte forma:

stage('Deploy Bundle') {
  sh """#!/bin/bash
        ${DBCLIPATH}/databricks bundle deploy -t ${BUNDLETARGET}
     """
}

Esta etapa faz duas coisas:

  1. Como o mapeamento no ficheiro artifact é definido como databricks.yml, isso instrui a CLI do Databricks a construir o ficheiro wheel do Python usando o ficheiro whl no local especificado.
  2. Depois que o arquivo de roda Python é criado em sua máquina de desenvolvimento local, a CLI do Databricks implanta o arquivo de roda Python criado junto com os arquivos Python e blocos de anotações especificados em seu espaço de trabalho do Azure Databricks. Por padrão, o Databricks Asset Bundles implanta o arquivo de roda Python e outros arquivos no /Workspace/Users/<your-username>/.bundle/<bundle-name>/<target-name>.

Para permitir que o arquivo de roda Python seja construído conforme especificado no databricks.yml arquivo, crie as seguintes pastas e arquivos na raiz do repositório clonado em sua máquina local.

Para definir a lógica e os testes unitários para o ficheiro wheel do Python que o notebook irá executar, crie dois ficheiros chamados addcol.py e test_addcol.py, e adicione-os a uma estrutura de pastas nomeada python/dabdemo/dabdemo dentro da pasta Libraries do seu repositório.

├── ...
├── Libraries
│    └── python
│          └── dabdemo
│                └── dabdemo
│                      ├── addcol.py
│                      └── test_addcol.py
├── ...

O addcol.py arquivo contém uma função de biblioteca que é criada posteriormente em um arquivo de roda Python e, em seguida, instalada em um cluster do Azure Databricks. Esta função adiciona uma nova coluna, preenchida por um literal, a um DataFrame do Apache Spark.

# Filename: addcol.py
import pyspark.sql.functions as F

def with_status(df):
  return df.withColumn("status", F.lit("checked"))

O test_addcol.py arquivo contém testes para passar um objeto DataFrame simulado para a with_status função, definida em addcol.py. O resultado é então comparado a um objeto DataFrame contendo os valores esperados. Se os valores corresponderem, o que neste caso acontece, o teste passa:

# Filename: test_addcol.py
import pytest
from pyspark.sql import SparkSession
from dabdemo.addcol import *

class TestAppendCol(object):

  def test_with_status(self):
    spark = SparkSession.builder.getOrCreate()

    source_data = [
      ("paula", "white", "paula.white@example.com"),
      ("john", "baer", "john.baer@example.com")
    ]

    source_df = spark.createDataFrame(
      source_data,
      ["first_name", "last_name", "email"]
    )

    actual_df = with_status(source_df)

    expected_data = [
      ("paula", "white", "paula.white@example.com", "checked"),
      ("john", "baer", "john.baer@example.com", "checked")
    ]
    expected_df = spark.createDataFrame(
      expected_data,
      ["first_name", "last_name", "email", "status"]
    )

    assert(expected_df.collect() == actual_df.collect())

Para habilitar a CLI do Databricks para empacotar corretamente esse código de biblioteca em um arquivo de roda Python, crie dois arquivos nomeados __init__.py e __main__.py na mesma pasta que os dois arquivos anteriores. Além disso, cria um ficheiro com o nome setup.py na python/dabdemo pasta:

├── ...
├── Libraries
│    └── python
│          └── dabdemo
│                ├── dabdemo
│                │    ├── __init__.py
│                │    ├── __main__.py
│                │    ├── addcol.py
│                │    └── test_addcol.py
│                └── setup.py
├── ...

O __init__.py arquivo contém o número da versão e o autor da biblioteca. Substitua <my-author-name> pelo seu nome:

# Filename: __init__.py
__version__ = '0.0.1'
__author__ = '<my-author-name>'

import sys, os

sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))

O __main__.py arquivo contém o ponto de entrada da biblioteca:

# Filename: __main__.py
import sys, os

sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))

from addcol import *

def main():
  pass

if __name__ == "__main__":
  main()

O setup.py arquivo contém configurações adicionais para construir a biblioteca em um arquivo de roda Python. Substitua <my-url>, <my-author-name>@<my-organization>e <my-package-description> por valores significativos:

# Filename: setup.py
from setuptools import setup, find_packages

import dabdemo

setup(
  name = "dabdemo",
  version = dabdemo.__version__,
  author = dabdemo.__author__,
  url = "https://<my-url>",
  author_email = "<my-author-name>@<my-organization>",
  description = "<my-package-description>",
  packages = find_packages(include = ["dabdemo"]),
  entry_points={"group_1": "run=dabdemo.__main__:main"},
  install_requires = ["setuptools"]
)

Teste a lógica do componente da roda Python

O Run Unit Tests estágio, o quarto estágio deste Jenkins Pipeline, usa pytest para testar a lógica de uma biblioteca para garantir que ela funcione como construída. Esta fase define-se do seguinte modo:

stage('Run Unit Tests') {
  sh """#!/bin/bash
        ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} run-unit-tests
     """
}

Este estágio usa a CLI do Databricks para executar uma tarefa de notebook. Este trabalho executa o bloco de anotações Python com o nome do run-unit-test.pyarquivo . Este bloco de notas executa pytest contra a lógica da biblioteca.

Para executar os testes de unidade para este exemplo, adicione um arquivo de bloco de anotações Python nomeado run_unit_tests.py com o seguinte conteúdo à raiz do repositório clonado em sua máquina local:

# Databricks notebook source

# COMMAND ----------

# MAGIC %sh
# MAGIC
# MAGIC mkdir -p "/Workspace${WORKSPACEBUNDLEPATH}/Validation/reports/junit/test-reports"

# COMMAND ----------

# Prepare to run pytest.
import sys, pytest, os

# Skip writing pyc files on a readonly filesystem.
sys.dont_write_bytecode = True

# Run pytest.
retcode = pytest.main(["--junit-xml", f"/Workspace{os.getenv('WORKSPACEBUNDLEPATH')}/Validation/reports/junit/test-reports/TEST-libout.xml",
                      f"/Workspace{os.getenv('WORKSPACEBUNDLEPATH')}/files/Libraries/python/dabdemo/dabdemo/"])

# Fail the cell execution if there are any test failures.
assert retcode == 0, "The pytest invocation failed. See the log for details."

Use a roda Python construída

O quinto estágio deste Jenkins Pipeline, intitulado Run Notebook, executa um notebook Python que chama a lógica no arquivo de roda Python construído, da seguinte maneira:

stage('Run Notebook') {
  sh """#!/bin/bash
        ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} run-dabdemo-notebook
     """
  }

Este estágio executa o CLI do Databricks, que, por sua vez, instrui o seu espaço de trabalho a executar um trabalho no notebook. Este notebook cria um objeto DataFrame, passa-o para a função da biblioteca with_status, imprime o resultado e relata os resultados da execução da tarefa. Crie o bloco de anotações adicionando um arquivo de bloco de anotações Python nomeado dabdaddemo_notebook.py com o seguinte conteúdo na raiz do repositório clonado em sua máquina de desenvolvimento local:

# Databricks notebook source

# COMMAND ----------

# Restart Python after installing the wheel.
dbutils.library.restartPython()

# COMMAND ----------

from dabdemo.addcol import with_status

df = (spark.createDataFrame(
  schema = ["first_name", "last_name", "email"],
  data = [
    ("paula", "white", "paula.white@example.com"),
    ("john", "baer", "john.baer@example.com")
  ]
))

new_df = with_status(df)

display(new_df)

# Expected output:
#
# +------------+-----------+-------------------------+---------+
# │first_name │last_name │email                   │status  |
# +============+===========+=========================+=========+
# │paula      │white     │paula.white@example.com │checked |
# +------------+-----------+-------------------------+---------+
# │john       │baer      │john.baer@example.com   │checked |
# +------------+-----------+-------------------------+---------+

Avaliar os resultados da execução do trabalho do bloco de notas

A Evaluate Notebook Runs etapa, a sexta etapa deste Jenkins Pipeline, avalia os resultados da execução anterior da tarefa do notebook. Esta fase define-se do seguinte modo:

stage('Evaluate Notebook Runs') {
  sh """#!/bin/bash
        ${DBCLIPATH}/databricks bundle run -t ${BUNDLETARGET} evaluate-notebook-runs
     """
  }

Este estágio executa a CLI do Databricks, que, por sua vez, instrui seu espaço de trabalho a executar um trabalho de arquivo Python. Este ficheiro Python determina os critérios de falha e sucesso para a execução do trabalho no notebook e reporta o resultado de falha ou sucesso. Crie um arquivo nomeado evaluate_notebook_runs.py com o seguinte conteúdo na raiz do repositório clonado em sua máquina de desenvolvimento local:

import unittest
import xmlrunner
import json
import glob
import os

class TestJobOutput(unittest.TestCase):

  test_output_path = f"/Workspace${os.getenv('WORKSPACEBUNDLEPATH')}/Validation/Output"

  def test_performance(self):
    path = self.test_output_path
    statuses = []

    for filename in glob.glob(os.path.join(path, '*.json')):
      print('Evaluating: ' + filename)

      with open(filename) as f:
        data = json.load(f)

        duration = data['tasks'][0]['execution_duration']

        if duration > 100000:
            status = 'FAILED'
        else:
            status = 'SUCCESS'

        statuses.append(status)
        f.close()

    self.assertFalse('FAILED' in statuses)

  def test_job_run(self):
    path = self.test_output_path
    statuses = []

    for filename in glob.glob(os.path.join(path, '*.json')):
      print('Evaluating: ' + filename)

      with open(filename) as f:
        data = json.load(f)
        status = data['state']['result_state']
        statuses.append(status)
        f.close()

    self.assertFalse('FAILED' in statuses)

if __name__ == '__main__':
  unittest.main(
    testRunner = xmlrunner.XMLTestRunner(
      output = f"/Workspace${os.getenv('WORKSPACEBUNDLEPATH')}/Validation/Output/test-results",
    ),
    failfast   = False,
    buffer     = False,
    catchbreak = False,
    exit       = False
  )

Importar e relatar resultados de testes

O sétimo estágio neste Jenkins Pipeline, intitulado Import Test Results, usa a CLI do Databricks para enviar os resultados do teste do seu espaço de trabalho para sua máquina de desenvolvimento local. A oitava e última etapa, intitulada Publish Test Results, publica os resultados do teste para Jenkins usando o junit plugin Jenkins. Isso permite que você visualize relatórios e painéis relacionados ao status dos resultados do teste. Estas fases são definidas do seguinte modo:

stage('Import Test Results') {
  def DATABRICKS_BUNDLE_WORKSPACE_FILE_PATH
  def getPath = "${DBCLIPATH}/databricks bundle validate -t ${BUNDLETARGET} | ${JQPATH}/jq -r .workspace.file_path"
  def output = sh(script: getPath, returnStdout: true).trim()

  if (output) {
    DATABRICKS_BUNDLE_WORKSPACE_FILE_PATH = "${output}"
  } else {
    error "Failed to capture output or command execution failed: ${getPath}"
  }

  sh """#!/bin/bash
        ${DBCLIPATH}/databricks workspace export-dir \
        ${DATABRICKS_BUNDLE_WORKSPACE_FILE_PATH}/Validation/Output/test-results \
        ${WORKSPACE}/Validation/Output/test-results \
        --overwrite
     """
}
stage('Publish Test Results') {
  junit allowEmptyResults: true, testResults: '**/test-results/*.xml', skipPublishingChecks: true
}

Resultados do teste Jenkins

Envie todas as alterações de código para o repositório de terceiros

Agora você deve enviar o conteúdo do repositório clonado na máquina de desenvolvimento local para o repositório de terceiros. Antes de enviar por push, você deve primeiro adicionar as seguintes entradas ao .gitignore arquivo em seu repositório clonado, pois provavelmente não deve enviar arquivos de trabalho internos do Databricks Asset Bundle, relatórios de validação, arquivos de compilação Python e caches Python para seu repositório de terceiros. Normalmente, você desejará regenerar novos relatórios de validação e as últimas compilações de roda Python em seu espaço de trabalho do Azure Databricks, em vez de usar relatórios de validação potencialmente desatualizados e compilações de roda Python:

.databricks/
.vscode/
Libraries/python/dabdemo/build/
Libraries/python/dabdemo/__pycache__/
Libraries/python/dabdemo/dabdemo.egg-info/
Validation/

Execute seu pipeline Jenkins

Agora está pronto para executar manualmente o seu Jenkins Pipeline. Para fazer isso, no seu Painel Jenkins:

  1. Clique no nome do seu Jenkins Pipeline.
  2. Na barra lateral, clique em Criar agora.
  3. Para ver os resultados, clique na execução mais recente do Pipeline (por exemplo, #1) e, em seguida, clique em Saída do Console.

Neste ponto, o pipeline de CI/CD completou um ciclo de integração e implantação. Ao automatizar esse processo, você pode garantir que seu código tenha sido testado e implantado por um processo eficiente, consistente e repetível. Para instruir seu provedor Git de terceiros a executar o Jenkins sempre que um evento específico acontecer, como uma solicitação pull de repositório, consulte a documentação do provedor Git de terceiros.