ALM

3. Pipelines de déploiement d’un environnement Databricks (Infra, paramétrage, notebooks,…)

Oct 24, 2022

Nicolas Bailly

Cet article est le 3ème d’une série de 3 articles sur l’automatisation des déploiements Databricks :

  1. Déploiement d’un Workspace Databricks dans Azure avec Terraform
  2. Paramétrage d’un workspace Databricks par Terraform
  3. Pipelines de déploiement d’un environnement Databricks (Infra, paramétrage, notebooks,…)

Nous avons vu dans les articles précédents comment créer un environnement Databricks en utilisant Terraform. Pour aller jusqu’au bout, nous allons maintenant voir comment intégrer tout ceci dans des pipelines de déploiement Azure Devops pour déployer l’infrastructure et des notebooks.
Retrouvez le code source complet ici

Découpage des pipelines de déploiements

Les pipelines de déploiements que nous allons faire, sont découpés en 3 :

  • un pipeline de déploiement de l’infra Azure. Il s’agit de déployer ce que nous avons fait sur le 1er article
  • un pipeline de déploiement de la configuration Databricks. Il s’agit de déployer ce que nous avons fait sur le 2ème article
  • un pipeline de déploiement de Notebooks et de fichiers d’initialisation des clusters. C’est ce dernier pipeline que les data engineers utiliseront le plus pour déployer leurs développements

Déploiement Terraform

Que ce soit pour le pipeline de déploiement de l’infra Azure ou le pipeline de déploiement de la configuration Databricks, on utilise Terraform, ce qui nous permet d’utiliser la même méthode de déploiement et d’avoir un template yaml qui effectue les tâches suivantes :

  • Affectation des variables Terraform
  • Installation de Terraform
  • Lancement de la commande « Terraform init »
  • Lancement de la commande « Terraform apply »

Pour que l’affectation des variables se fasse, il y a quelques pré-requis :

  • Ajouter un groupe de variables dans Azure Devops pour chaque environnement
  • Créer un fichier « tfvars » dans les scripts Terraform qui référence les variables Azure Devops
  • Référencer le groupe de variables dans le pipeline Yaml

Les groupes de variables sont automatiquement injectés lors de l’exécution d’un pipeline s’il est référencé dans le Yaml. Un groupe de variables par environnement est nécessaire et on peut prévoir un groupe de variables pour tout ce qui va être commun à tous les environnements.

Dans nos scripts Terraform, on crée un fichier « variables.auto.tfvars » qui référence les variables que l’on a mis dans nos groupes de variables Azure Devops :

Ici, par exemple, « __ResourceGroupName__ » sera remplacé par la valeur de la variable « ResourceGroupName ».
Et pour que ces variables soient injectées, il faut les référencer et utiliser une tâche de « Replace tokens » :

jobs:
  - job: DeployInfra
    displayName: 'Deploy Infra'
    variables:
    - group: 'Infra-Dev'
    - group: 'Infra-Common'
    steps:
    - task: replacetokens@5
      inputs:
        rootDirectory: '$(Pipeline.Workspace)/${{ parameters.PipelineResourceName }}/${{ parameters.ArtifactName }}'
        targetFiles: 'variables.auto.tfvars'
        encoding: 'auto'
        tokenPattern: 'rm'
        writeBOM: false
        actionOnMissing: 'warn'
        keepToken: false
        actionOnNoFiles: 'continue'
        enableTransforms: false
        enableRecursion: false
        useLegacyPattern: false
        enableTelemetry: true

Là, on voit bien que l’on référence les groupes de variables « Infra-Dev » et « Infra-Common » et la tâche « Replace tokens » remplace toutes les valeurs dans le fichier variables.auto.tfvars.
On peut ensuite dérouler le process classique d’exécution des scripts Terraform :

- task: TerraformInstaller@0
            displayName: 'Install Terraform 1.2.9'
            inputs:
              terraformVersion: 1.2.9
          
          - task: TerraformTaskV2@2
            displayName: 'Terraform : azurerm init'
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(Pipeline.Workspace)/${{ parameters.PipelineResourceName }}/${{ parameters.ArtifactName }}'
              commandOptions: '-no-color'
              backendServiceArm: '${{ parameters.ServiceConnection }}'
              backendAzureRmResourceGroupName: '$(TerraformResourceGroupName)'
              backendAzureRmStorageAccountName: $(TerraformStorageAccountName)
              backendAzureRmContainerName: '$(TerraformContainerName)'
              backendAzureRmKey: '${{ parameters.TerraformKey }}'

          - task: TerraformTaskV2@2
            displayName: 'Terraform : azurerm validate and apply'
            inputs:
              provider: 'azurerm'
              command: apply
              workingDirectory: '$(Pipeline.Workspace)/${{ parameters.PipelineResourceName }}/${{ parameters.ArtifactName }}'
              commandOptions: '-no-color --var-file=variables.auto.tfvars'
              environmentServiceNameAzureRM: '${{ parameters.ServiceConnection }}'

En « templatisant » tout ceci dans un fichier Yaml, on crée à la fois le 1er et le 2ème pipeline. Voici à quoi ressemble le déploiement de l’environnement de dev pour l’infra Azure :

stages:
- stage: Dev
  displayName: Dev
  condition: or(eq(variables['Build.SourceBranch'],'refs/heads/develop'), startsWith(variables['Build.SourceBranch'],'refs/heads/feature'), startsWith(variables['Build.SourceBranch'],'refs/heads/fix'))

  variables:
  - group: 'Infra-Dev'
  - group: 'Infra-Common'

  jobs:
  - template: release-terraform-stage-template.yml
    parameters: 
      ServiceConnection: 'CONNECTION-DEV'
      PoolName: 'POOL-DEV'
      PipelineResourceName: 'resourceBuild'
      EnvironmentName: Develop
      ArtifactName: infra-azure
      TerraformKey: infra-azure.tfstate

Déploiement des Notebooks et des scripts d’initialisation des clusters

Dans l’article précédent, nous avons vu comment déployer des pipelines et des jobs clusters. Ils exécutent des notebooks qui se trouvent sur l’espace « /Shared » du workspace. Ces notebooks doivent être déployés par la CI/CD. Pour cela, nous allons faire un pipeline qui lance les tâches suivantes :

  • Récupération d’un token Azure AD pour se connecter à Databricks
  • Récupération de l’URL du workspace Databricks
  • Installation de Python. Nécessaire à l’installation de Databricks Connect
  • Installation de Databricks Connect
  • Déploiement des Notebooks
  • Déploiement des scripts d’initialisation des clusters

Récupération d’un token Azure AD pour se connecter à Databricks

Pour l’exécution de commandes Databricks Connect, nous avons besoin d’un token d’authentification. Nous utiliserons l’identité du Service Connection Azure Devops dont nous supposons qui a les permissions (le créateur du Workspace Databricks a les droits admin. Donc si c’est ce même Service Connection qui a été utilisé pour créer le workspace, il n’y aura pas de problème de permissions). Nous utilisons une commande AZ CLI pour récupérer ce token et nous l’enregistrons dans une variable d’environnement que Databricks Connect utilisera :

- task: AzureCLI@2
  displayName: 'Get SPN Acces token for Databricks'
  inputs:
    azureSubscription: '${{ parameters.ServiceConnection }}'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    inlineScript: |
      $accessToken=(az account get-access-token --resource 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d | jq .accessToken --raw-output)
      Write-Host ("##vso[task.setvariable variable=AccessToken;]$accessToken")
      [System.Environment]::SetEnvironmentVariable('DATABRICKS_AAD_TOKEN', $accessToken, [System.EnvironmentVariableTarget]::Machine)
    failOnStandardError: true

Récupération de l’URL du workspace Databricks

Nous utilisons une commande AZ CLI pour récupérer l’URL du workspace à partir de son nom qui se trouve dans le groupe de variables :

- task: AzureCLI@2
  displayName: 'Get Databricks Workspace URL'
  inputs:
    azureSubscription: '${{ parameters.ServiceConnection }}'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az extension add --name databricks
      $workspaceUrl=(az databricks workspace show --resource-group "$(ResourceGroupName)" --name "$(DatabricksWorkspaceName)" --query workspaceUrl --output tsv)
      Write-Host ("##vso[task.setvariable variable=WorkspaceUrl;]$workspaceUrl")
    failOnStandardError: true

Installation de Databricks Connect

Nous installons Databricks Connect et nous le configurons en créant un fichier databrickscfg qui contient les informations d’identification du workspace : l’url et l’access token précédemment récupérés.

- task: UsePythonVersion@0
  inputs:
    versionSpec: '3.8'
  displayName: 'Use Python 3.8'
- task: Bash@3
  displayName: 'Install et configure Databricks connect'
  inputs:
    targetType: 'inline'
    script: |
      python -m pip install databricks-cli
      cat > ~/.databrickscfg <<EOF
      [DEFAULT]
      host = https://$(WorkspaceUrl)/
      token = $(AccessToken)
      EOF

Déploiement des Notebooks

Maintenant que Databricks Connect est installé et configuré, nous pouvons l’utiliser pour déployer les notebooks.
D’abord nous supprimons le répertoire cible pour nettoyer tout éventuel ancien fichier indésirable puis nous copions les notebooks dans le répertoire « /shared/Notebooks » du workspace :

- task: Bash@3
  displayName: 'Suppression du répertoire /Shared/Notebooks'
  inputs:
    targetType: 'inline'
    script: |
      FOLDER=/Shared/Notebooks
      databricks workspace ls $FOLDER > /dev/null
      RES=$?
      if [ $RES -eq 0 ]; then
        databricks workspace delete -r $FOLDER
      fi
    failOnStderr: true

- task: Bash@3
  displayName: 'Deploy Notebooks'
  inputs:
    targetType: 'inline'
    script: |
      databricks workspace import_dir -o '$(Pipeline.Workspace)/resourceBuild/notebooks' '/Shared/Notebooks'
    failOnStderr: true

Déploiement des scripts d’initialisation des clusters

De la même manière que les notebooks, nous déployons les scripts d’initialisation sauf que ceux-ci doivent se trouver dans le DBFS du workspace :

- task: Bash@3
  displayName: 'Deploy init scripts'
  inputs:
    targetType: 'inline'
    script: |
      databricks fs rm -r dbfs:/FileStore/init-scripts/
      databricks fs cp -r $(Pipeline.Workspace)/resourceBuild/init-scripts/ dbfs:/FileStore/init-scripts/ --overwrite
    failOnStderr: true

Bilan

Avec tout ce que nous venons de faire nous avons une CI/CD complète pour déployer sur tous les environnements.
Le workflow de travail des Data Engineers ressemble à celui-ci :

  1. Les Data Engineers créent leur branche de travail depuis Databricks et developpent leurs fonctionnalités
  2. Ils commitent leur code depuis Databricks et lancent des Pull Request dans Azure Devops pour merger
  3. Les pipelines de build se lancent et déploient le travail des Data Engineers sur l’environnement cible

0 commentaires

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Découvrez nos autres articles

Aller au contenu principal