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 do CI/CD no Databricks, consulte CI/CD no 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 Jenkins fornece uma interface para definir estágios em um pipeline Jenkins usando o código Groovy para chamar e configurar os plugins 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 digite 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 alvo do pacote ativo Databricks 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 posteriormente 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> pelo 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, n2-highmem-4.
  • 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 é incorporada 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 permitir que o Databricks CLI empacote 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. Este trabalho executa o Notebook Python 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 dos testes do 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. Databricks Antes de fazer push, adicione primeiro as seguintes entradas ao arquivo .gitignore no repositório clonado, pois provavelmente não deve fazer push de arquivos de trabalho internos do ativo Bundle, relatórios de validação, arquivos de compilação Python e caches 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.