Pular para o conteúdo principal

CI/CD com Jenkins na Databricks

nota

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 CI/CD que o senhor pode usar para gerenciar e executar seu pipeline CI/CD. Este artigo ilustra como usar o servidor de automação Jenkins. CI/CD é um padrão de design, portanto, as etapas e os estágios descritos neste artigo devem ser transferidos com algumas alterações na linguagem de definição do pipeline em cada ferramenta. Além disso, grande parte do código deste exemplo pipeline executa o código Python padrão, que o senhor pode invocar em outras ferramentas. Para obter uma visão geral da CI/CD na Databricks, consulte O que é CI/CD na Databricks?

CI/CD desenvolvimento fluxo de trabalho

A Databricks sugere o seguinte fluxo de trabalho para o desenvolvimento de CI/CD com o 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 provedor de Git de terceiros.
  3. Puxe todos os artefatos atualizados existentes (como o Notebook, os arquivos de código e os 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 provedor de Git de terceiros.
  5. Repita as etapas 3 e 4 conforme necessário.
  6. Use o Jenkins periodicamente como uma abordagem integrada para extrair automaticamente artefatos do repositório de terceiros para o computador de desenvolvimento local ou Databricks workspace; criar, testar e executar código no computador de desenvolvimento local ou Databricks workspace; e relatar os resultados dos testes e da execução. Embora seja possível executar o Jenkins manualmente, em implementações reais, o senhor instruiria o provedor terceirizado Git a executar o Jenkins sempre que ocorresse um evento específico, como uma solicitação de pull do repositório.

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

Configuração da máquina de desenvolvimento local

O exemplo deste artigo usa o Jenkins para instruir os pacotes Databricks CLI e Databricks ativo Bundles para fazer o seguinte:

  1. Crie um arquivo Python wheel em seu computador de desenvolvimento local.
  2. Implemente o arquivo Python wheel construído junto com os arquivos Python adicionais e o Notebook Python de sua máquina de desenvolvimento local em um Databricks workspace.
  3. Teste e execute o upload do arquivo Python wheel e o Notebook nesse workspace.

Para configurar o computador de desenvolvimento local para instruir o Databricks workspace a executar os estágios de compilação e upload deste exemplo, faça o seguinte no computador de desenvolvimento local:

Etapa 1: instalar as ferramentas necessárias

Nesta etapa, o senhor instala as ferramentas de compilação Databricks CLI, Jenkins, jq e Python wheel em sua máquina de desenvolvimento local. Essas ferramentas são necessárias para a execução deste exemplo.

  1. Instale o site Databricks CLI versão 0.205 ou acima, caso ainda não o tenha feito. O Jenkins usa o Databricks CLI para passar as instruções de teste e execução deste exemplo para o seu workspace. Consulte Instalar ou atualizar a CLI da Databricks.

  2. Instale e inicie o Jenkins, caso ainda não o tenha feito. Consulte Instalação do Jenkins para Linux, macOS ou Windows.

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

  4. Use pip para instalar as ferramentas de compilação do Python wheel com o seguinte comando (alguns sistemas podem exigir que o senhor use pip3 em vez de pip):

    Bash
    pip install --upgrade wheel

Etapa 2: Criar um pipeline do Jenkins

Nesta etapa, o senhor usa o Jenkins para criar um pipeline do Jenkins para o exemplo deste artigo. O Jenkins oferece alguns tipos de projetos diferentes para criar o pipeline CI/CD. O pipeline do Jenkins fornece uma interface para definir estágios em um pipeline do Jenkins usando o código Groovy para chamar e configurar os plugins do Jenkins.

Tipos de projeto Jenkins

Para criar o pipeline do Jenkins no Jenkins:

  1. Depois de iniciar o Jenkins, no painel do Jenkins, clique em New Item (Novo item ).
  2. Em Enter an item name (Inserir um nome de item ), digite um nome para o pipeline do Jenkins, por exemplo, jenkins-demo.
  3. Clique no ícone do tipo de projeto de pipeline .
  4. Clique em OK . A página Configure (Configurar ) do pipeline do Jenkins é exibida.
  5. Na área de pipeline , na lista suspensa Definição , selecione o script de pipeline do SCM .
  6. Na lista suspensa SCM , selecione Git .
  7. Em Repository URL (URL do repositório ), digite a URL do repositório hospedado pelo seu provedor Git de terceiros.
  8. Em Branch Specifier , digite */<branch-name>, onde <branch-name> é o nome da ramificação em seu repositório que você deseja usar, por exemplo */main.
  9. Em Caminho do script , digite Jenkinsfile, se ainda não estiver definido. O senhor cria o Jenkinsfile mais adiante neste artigo.
  10. Desmarque a caixa intitulada Lightweight checkout , se já estiver marcada.
  11. Clique em Salvar .

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

Nesta etapa, o senhor adiciona três variáveis globais de ambiente ao Jenkins. Jenkins passa essas variáveis de ambiente para o site Databricks CLI. O Databricks CLI precisa dos valores para essas variáveis de ambiente para se autenticar com o seu Databricks workspace. 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 Databricks workspace, consulte Autorizar o acesso autônomo ao Databricks recurso com uma entidade de serviço usando o OAuth.

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

  • DATABRICKS_HOST, definido como seu URL Databricks workspace , começando com https://. Consulte nomes de instância de espaço de trabalho, URLs e IDs.
  • DATABRICKS_CLIENT_IDdefinido como o ID do cliente da entidade de serviço, que também é conhecido como ID do aplicativo.
  • DATABRICKS_CLIENT_SECRETdefinido como o segredo do Databricks OAuth da entidade de serviço.

Para definir a variável global de ambiente no Jenkins, no painel do Jenkins:

  1. Na barra lateral, clique em gerenciar Jenkins .
  2. Na seção Configuração do sistema , clique em Sistema .
  3. Na seção Global properties (Propriedades globais ), marque a caixa tiled variável de ambiente .
  4. Clique em Add e insira o nome e o valor da variável de ambiente. Repita esse procedimento para cada variável de ambiente adicional.
  5. Quando terminar de adicionar a variável de ambiente, clique em Save (Salvar ) para retornar ao painel do Jenkins.

Projetar o pipeline do Jenkins

O Jenkins oferece alguns tipos de projetos diferentes para criar o pipeline CI/CD. Este exemplo implementa um pipeline do Jenkins. O pipeline do Jenkins fornece uma interface para definir estágios em um pipeline do Jenkins usando o código Groovy para chamar e configurar os plugins do Jenkins.

O senhor escreve uma definição de pipeline do Jenkins 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 o pipeline do Jenkins. Aqui está o pipeline do Jenkins para o exemplo deste artigo. Neste exemplo Jenkinsfile, substitua os seguintes espaços reservados:

  • Substitua <user-name> e <repo-name> pelo nome de usuário e pelo nome do repositório hospedado pelo seu provedor Git de terceiros. Este artigo usa um URL GitHub como exemplo.
  • Substitua <release-branch-name> pelo nome da ramificação de lançamento em seu repositório. Por exemplo, isso pode ser main.
  • Substitua <databricks-cli-installation-path> pelo caminho em seu computador de desenvolvimento local onde a CLI da Databricks está instalada. Por exemplo, no macOS, isso pode ser /usr/local/bin.
  • Substitua <jq-installation-path> pelo caminho em 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 algumas cadeias de caracteres para ajudar a identificar exclusivamente o trabalho Databricks que é criado em seu workspace para este exemplo. Por exemplo, isso pode ser jenkins-demo.
  • Observe que BUNDLETARGET está definido como dev, que é o nome do Databricks ativo Bundle target definido posteriormente neste artigo. Em implementações do mundo real, você alteraria isso para o nome do seu próprio pacote de destino. Mais detalhes sobre os alvos do pacote são fornecidos mais adiante neste artigo.

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

Groovy
// 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 desse pipeline do Jenkins e como configurar os artefatos e o comando para o Jenkins executar nesse estágio.

Obtenha os artefatos mais recentes do repositório de terceiros

O primeiro estágio desse pipeline do Jenkins, o estágio Checkout, é definido da seguinte forma:

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

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

Validar o pacote Databricks ativo

O segundo estágio desse pipeline do Jenkins, o estágio Validate Bundle, é definido da seguinte forma:

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

Esse estágio garante que o Databricks ativo Bundle, que define o fluxo de trabalho para testar e executar seus artefatos, esteja sintaticamente correto. Databricks ativo Bundles , conhecidos simplesmente como bundles , possibilitam expressar análises completas de dados e projetos ML como uma coleção de arquivos de origem. Veja o que são Databricks ativo Bundles?

Para definir o pacote para esse artigo, crie um arquivo chamado databricks.yml na raiz do repositório clonado em seu computador local. Neste exemplo de arquivo databricks.yml, substitua os seguintes espaços reservados:

  • Substitua <bundle-name> por um nome programático exclusivo para o pacote. Por exemplo, isso pode ser jenkins-demo.
  • Substitua <job-prefix-name> por algumas cadeias de caracteres para ajudar a identificar exclusivamente o trabalho Databricks que é criado em seu workspace para este exemplo. Por exemplo, isso pode ser jenkins-demo. Ele deve corresponder ao valor JOBPREFIX em seu arquivo Jenkins.
  • Substitua <spark-version-id> pela ID da versão Databricks Runtime do seu Job clustering, por exemplo, 13.3.x-scala2.12.
  • Substitua <cluster-node-type-id> pelo ID do tipo de nó do Job clustering, por exemplo, i3.xlarge.
  • Observe que dev no mapeamento targets é o mesmo que BUNDLETARGET em seu arquivo Jenkins. Um destino de pacote especifica o host e os comportamentos de implantação relacionados.

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

YAML
# 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 Databricks ativo Bundle configuration.

implantado o pacote em seu workspace

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

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

Esse estágio faz duas coisas:

  1. Como o mapeamento artifact no arquivo databricks.yml está definido como whl, isso instrui o Databricks CLI a criar o arquivo Python wheel usando o arquivo setup.py no local especificado.
  2. Depois que o arquivo Python wheel é criado em sua máquina de desenvolvimento local, o Databricks CLI implanta o arquivo Python wheel criado junto com os arquivos Python especificados e o Notebook em seu Databricks workspace. Por default, Databricks ativo Bundles implantado o arquivo Python wheel e outros arquivos para /Workspace/Users/<your-username>/.bundle/<bundle-name>/<target-name>.

Para permitir que o arquivo Python wheel seja criado conforme especificado no arquivo databricks.yml, crie as seguintes pastas e arquivos na raiz do repositório clonado em seu computador local.

Para definir a lógica e os testes de unidade para o arquivo Python wheel contra o qual o Notebook será executado, crie dois arquivos denominados addcol.py e test_addcol.py e adicione-os a uma estrutura de pastas denominada python/dabdemo/dabdemo dentro da pasta Libraries do seu repositório, visualizada da seguinte forma (as elipses indicam pastas omitidas no repositório, para fins de brevidade):

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

O arquivo addcol.py contém uma função de biblioteca que é criada posteriormente em um arquivo Python wheel e, em seguida, instalada em um cluster Databricks. Trata-se de uma função simples que adiciona uma nova coluna, preenchida por um literal, a um Apache Spark DataFrame:

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

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

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

Python
# 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 que o Databricks CLI possa empacotar corretamente esse código de biblioteca em um arquivo Python wheel, crie dois arquivos denominados __init__.py e __main__.py na mesma pasta dos dois arquivos anteriores. Além disso, crie um arquivo chamado setup.py na pasta python/dabdemo, visualizado da seguinte forma (elipses indicam pastas omitidas, para resumir):

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

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

Python
# 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 arquivo __main__.py contém o ponto de entrada da biblioteca:

Python
# 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 arquivo setup.py contém configurações adicionais para criar a biblioteca em um arquivo Python wheel. Substitua <my-url>, <my-author-name>@<my-organization> e <my-package-description> por valores significativos:

Python
# 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 dos componentes do site Python wheel

O estágio Run Unit Tests, o quarto estágio deste pipeline do Jenkins, usa o pytest para testar a lógica de uma biblioteca e garantir que ela funcione como foi construída. Esse estágio é definido da seguinte forma:

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

Essa etapa usa o site Databricks CLI para executar um trabalho de notebook. Esse Job executa o Python Notebook com o nome de arquivo run-unit-test.py. A execução do Notebook pytest contraria a lógica da biblioteca.

Para executar os testes de unidade deste exemplo, adicione um arquivo do Notebook Python chamado run_unit_tests.py com o seguinte conteúdo à raiz do repositório clonado em seu computador local:

Python
# 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 o Python wheel

A quinta etapa desse pipeline do Jenkins, intitulada Run Notebook, executa um notebook Python que chama a lógica no arquivo Python wheel criado, como segue:

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

Esse estágio executa o Databricks CLI, que, por sua vez, instrui o workspace a executar um Notebook Job. Este Notebook cria um objeto DataFrame, passa-o para a função with_status da biblioteca, imprime o resultado e informa os resultados da execução do Job. Crie o Notebook adicionando um arquivo Python Notebook chamado dabdaddemo_notebook.py com o seguinte conteúdo na raiz do repositório clonado em seu computador de desenvolvimento local:

Python
# 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 Notebook Job

O estágio Evaluate Notebook Runs, o sexto estágio desse pipeline do Jenkins, avalia os resultados da execução do Notebook Job anterior. Esse estágio é definido da seguinte forma:

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

Essa etapa executa o Databricks CLI, que, por sua vez, instrui o workspace a executar um arquivo Job Python. Esse arquivo Python determina os critérios de falha e sucesso para a execução do Notebook Job e informa esse resultado de falha ou sucesso. Crie um arquivo chamado evaluate_notebook_runs.py com o seguinte conteúdo na raiz do seu repositório clonado em sua máquina de desenvolvimento local:

Python
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

A sétima etapa desse pipeline do Jenkins, intitulada Import Test Results, usa o Databricks CLI para enviar os resultados do teste do seu workspace para a máquina de desenvolvimento local. A oitava e última etapa, intitulada Publish Test Results, publica os resultados do teste no Jenkins usando o plugin junit Jenkins. Isso permite que você visualize relatórios e painéis relacionados ao status dos resultados do teste. Esses estágios são definidos da seguinte forma:

Groovy
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 seu repositório de terceiros

Agora você deve enviar o conteúdo do seu repositório clonado em sua máquina de desenvolvimento local para seu repositório de terceiros. Antes de fazer o push, adicione as seguintes entradas ao arquivo .gitignore no repositório clonado, pois provavelmente não deve fazer o push de arquivos de trabalho internos do Databricks ativo Bundle, relatórios de validação, arquivos de compilação do Python e caches do Python no repositório de terceiros. Normalmente, o senhor deseja gerar novamente novos relatórios de validação e a última versão do Python wheel integrada ao seu Databricks workspace, em vez de usar relatórios de validação e compilações do Python wheel potencialmente desatualizados:

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

executar seu pipeline Jenkins

Agora o senhor está pronto para executar o pipeline do Jenkins manualmente. Para fazer isso, no painel do Jenkins:

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

Nesse ponto, o pipeline de CI/CD concluiu um ciclo de integração e implantação. Ao automatizar esse processo, o senhor pode garantir que seu código seja testado e implantado por um processo eficiente, consistente e repetível. Para instruir o provedor terceirizado Git a executar o Jenkins sempre que ocorrer um evento específico, como uma solicitação de pull do repositório, consulte a documentação do provedor terceirizado Git.