Перейти к основному содержимому

Настройка CI/CD с помощью пользовательских конвейеров

Обновлен
dbt Cloud
Orchestration
CI
Intermediate
Menu

    Введение

    Один из основных принципов dbt заключается в том, что аналитический код должен быть под версионным контролем. Это приносит вашей организации множество преимуществ в плане сотрудничества, согласованности кода, стабильности и возможности отката к предыдущей версии. Существует дополнительное преимущество, предоставляемое вашей платформой хостинга кода, которое часто упускается из виду или недооценивается. Некоторые из вас могут иметь опыт использования вебхуков dbt Cloud для запуска задания при создании PR. Это замечательная возможность, и она удовлетворяет большинство случаев использования для тестирования вашего кода перед слиянием в продакшн. Однако бывают случаи, когда организации требуется дополнительная функциональность, например, запуск рабочих процессов при каждом коммите (линтинг) или запуск рабочих процессов после завершения слияния. В этой статье мы покажем вам, как настроить пользовательские конвейеры для линтинга вашего проекта и запуска задания dbt Cloud через API.

    Примечание о терминологии в этой статье, так как каждая платформа хостинга кода использует разные термины для схожих концепций. Термины pull request (PR) и merge request (MR) используются взаимозаменяемо, чтобы обозначить процесс слияния одной ветки с другой.

    Что такое конвейеры?

    Конвейеры (которые известны под многими именами, такими как рабочие процессы, действия или шаги сборки) — это серия предопределенных заданий, которые запускаются определенными событиями в вашем репозитории (создание PR, отправка коммита, слияние ветки и т.д.). Эти задания могут выполнять практически все, что вы пожелаете, при условии, что у вас есть соответствующий доступ к безопасности и навыки программирования.

    Задания выполняются на раннерах, которые являются виртуальными серверами. Раннеры предварительно настроены с Ubuntu Linux, macOS или Windows. Это означает, что команды, которые вы выполняете, определяются операционной системой вашего раннера. Вы увидите, как это будет использоваться позже в настройке, но пока просто помните, что ваш код выполняется на виртуальных серверах, которые, как правило, размещаются платформой хостинга кода.

    Диаграмма работы конвейеров

    Обратите внимание, что раннеры, размещенные вашей платформой хостинга кода, предоставляют определенное количество бесплатного времени. После этого могут применяться платежные сборы в зависимости от того, как настроена ваша учетная запись. Вы также можете разместить свои собственные раннеры. Это выходит за рамки данной статьи, но ознакомьтесь с приведенными ниже ссылками, если вам интересно настроить это:

    • Информация о биллинге для раннеров, размещенных в репозитории:
    • Информация о самостоятелных раннерах:

    Кроме того, если вы используете бесплатный тарифный план GitLab, вы все равно можете следовать этому руководству, но вас могут попросить предоставить кредитную карту для подтверждения вашей учетной записи. Вы увидите что-то подобное, когда впервые попытаетесь запустить конвейер:

    Предупреждение от GitLab о необходимости предоставления платежной информации

    Как настроить конвейеры

    Это руководство предоставляет подробности для нескольких платформ хостинга кода. Где шаги уникальны, они представлены без опции выбора. Если код специфичен для платформы (например, GitHub, GitLab, Bitbucket), вы увидите опцию выбора для каждой.

    Конвейеры могут быть запущены различными событиями. Процесс вебхука dbt Cloud уже запускает выполнение, если вы хотите запускать свои задания на запрос слияния, поэтому это руководство сосредоточено на запуске конвейеров для каждого пуша и при слиянии PR. Поскольку пуши происходят часто в проекте, мы сделаем это задание супер простым и быстрым, используя линтинг с SQLFluff. Конвейер, который запускается на запросах слияния, будет запускаться реже и может быть использован для вызова API dbt Cloud для запуска конкретного задания. Это может быть полезно, если у вас есть специфические требования, которые должны выполняться при обновлении кода в продакшене, например, выполнение --full-refresh на всех затронутых инкрементальных моделях.

    Вот краткий обзор того, что этот конвейер будет выполнять:

    Диаграмма, показывающая создаваемые конвейеры и задействованные программы

    Запуск задания dbt Cloud при слиянии

    Это задание потребует немного больше настройки, но является хорошим примером того, как вызвать API dbt Cloud из конвейера CI/CD. Представленные здесь концепции могут быть обобщены и использованы в любом виде, который лучше всего подходит для вашего случая использования.

    Запуск при слиянии

    Если ваш поставщик Git имеет нативную интеграцию с dbt Cloud, вы можете воспользоваться настройкой Merge jobs в интерфейсе.

    Настройка ниже показывает, как вызвать API dbt Cloud для запуска задания каждый раз, когда происходит пуш в вашу основную ветку (ветка, в которую обычно сливаются pull requests. Обычно называется основной, первичной или мастер-веткой, но может быть названа иначе).

    1. Получите ваш API-ключ dbt Cloud

    При запуске конвейера CI/CD вы захотите использовать сервисный токен вместо API-ключа любого отдельного пользователя. Существуют подробные документы на эту тему, но ниже приведен краткий обзор (это должен выполнить администратор учетной записи):

    • Войдите в свою учетную запись dbt Cloud
    • В левом верхнем углу нажмите кнопку меню, затем Настройки учетной записи
    • Нажмите Сервисные токены слева
    • Нажмите Новый токен, чтобы создать новый токен специально для вызовов API CI/CD
    • Назовите ваш токен, например, “CICD Token”
    • Нажмите кнопку +Добавить в разделе Доступ и предоставьте этому токену разрешение Администратор заданий
    • Нажмите Сохранить, и вы увидите серую рамку с вашим токеном. Скопируйте его и сохраните в безопасном месте (это пароль, и с ним следует обращаться соответствующим образом).
    Вид страницы dbt Cloud, где создаются сервисные токеныВид страницы dbt Cloud, где создаются сервисные токены

    Вот видео, показывающее эти шаги:

    2. Поместите ваш API-ключ dbt Cloud в ваш репозиторий

    Следующая часть будет происходить на вашей платформе хостинга кода. Нам нужно сохранить ваш API-ключ из предыдущего шага в секрет репозитория, чтобы задание, которое мы создаем, могло получить к нему доступ. Не рекомендуется когда-либо сохранять пароли или API-ключи в вашем коде, поэтому этот шаг гарантирует, что ваш ключ останется безопасным, но все еще будет доступен для ваших конвейеров.

    • Откройте ваш репозиторий, в котором вы хотите запустить конвейер (тот же, в котором находится ваш проект dbt)
    • Нажмите Настройки, чтобы открыть параметры репозитория
    • Слева нажмите на раскрывающееся меню Секреты и переменные в разделе Безопасность
    • Из этого списка выберите Действия
    • В середине экрана нажмите кнопку Новый секрет репозитория
    • Вас попросят ввести имя, поэтому давайте назовем его DBT_API_KEY
      • Очень важно, чтобы вы скопировали/вставили это имя точно, так как оно используется в скриптах ниже.
    • В разделе Секрет вставьте ключ, который вы скопировали из dbt Cloud
    • Нажмите Добавить секрет, и все готово!

    ** Быстрая заметка о безопасности: хотя использование секрета репозитория является самым простым способом настройки этого секрета, в GitHub доступны и другие опции. Они выходят за рамки этого руководства, но могут быть полезны, если вам нужно создать более безопасную среду для выполнения действий. Ознакомьтесь с документацией GitHub о секретах здесь.*

    Вот видео, показывающее эти шаги:

    3. Создайте скрипт для запуска задания dbt Cloud через вызов API

    В вашем проекте dbt Cloud создайте новую папку на корневом уровне с именем python. В этой папке создайте файл с именем run_and_monitor_dbt_job.py. Вы скопируете/вставите содержимое из этого gist в этот файл.

    my_awesome_project
    ├── python
    │ └── run_and_monitor_dbt_job.py

    Этот файл Python содержит все, что вам нужно для вызова API dbt Cloud, но требует нескольких входных данных (см. фрагмент ниже). Эти входные данные передаются этому скрипту через переменные окружения, которые будут определены на следующем шаге.

    #------------------------------------------------------------------------------
    # получение переменных окружения
    #------------------------------------------------------------------------------
    api_base = os.getenv('DBT_URL', 'https://cloud.getdbt.com/') # по умолчанию используется URL многопользовательской версии
    job_cause = os.getenv('DBT_JOB_CAUSE', 'API-triggered job') # по умолчанию используется общее сообщение
    git_branch = os.getenv('DBT_JOB_BRANCH', None) # по умолчанию None
    schema_override = os.getenv('DBT_JOB_SCHEMA_OVERRIDE', None) # по умолчанию None
    api_key = os.environ['DBT_API_KEY'] # здесь нет значения по умолчанию, просто выбросьте ошибку, если ключ не предоставлен
    account_id = os.environ['DBT_ACCOUNT_ID'] # здесь нет значения по умолчанию, просто выбросьте ошибку, если id не предоставлен
    project_id = os.environ['DBT_PROJECT_ID'] # здесь нет значения по умолчанию, просто выбросьте ошибку, если id не предоставлен
    job_id = os.environ['DBT_PR_JOB_ID'] # здесь нет значения по умолчанию, просто выбросьте ошибку, если id не предоставлен

    Требуемый ввод:

    Для вызова API dbt Cloud скрипту необходимо несколько данных. Самый простой способ получить эти значения — открыть задание, которое вы хотите запустить в dbt Cloud. URL, когда вы находитесь внутри задания, содержит все необходимые значения:

    • DBT_ACCOUNT_ID - это число сразу после accounts/ в URL
    • DBT_PROJECT_ID - это число сразу после projects/ в URL
    • DBT_PR_JOB_ID - это число сразу после jobs/ в URL

    Изображение URL задания dbt Cloud с выделенными частями для учетной записи, проекта и задания

    4. Обновите ваш проект, чтобы включить новый вызов API

    Для этого нового задания мы добавим файл для вызова API dbt Cloud с именем dbt_run_on_merge.yml.

    my_awesome_project
    ├── python
    │ └── run_and_monitor_dbt_job.py
    ├── .github
    │ ├── workflows
    │ │ └── dbt_run_on_merge.yml
    │ │ └── lint_on_push.yml

    Файл YAML будет выглядеть довольно похоже на наше предыдущее задание, но есть новый раздел под названием env, который мы будем использовать для передачи необходимых переменных. Обновите переменные ниже, чтобы они соответствовали вашей настройке на основе комментариев в файле.

    Стоит отметить, что мы изменили раздел on:, чтобы теперь запускать только при пушах в ветку с именем main (т.е. когда PR сливается). Ознакомьтесь с документацией GitHub по этим фильтрам для дополнительных случаев использования.

    name: run dbt Cloud job on push

    # Этот фильтр говорит запускать это задание только при пуше в основную ветку
    # Это работает на предположении, что вы ограничили эту ветку только для PR, чтобы пушить в ветку по умолчанию
    # Обновите имя, чтобы оно соответствовало имени вашей ветки по умолчанию
    on:
    push:
    branches:
    - 'main'

    jobs:

    # задание вызывает API dbt Cloud для запуска задания
    run_dbt_cloud_job:
    name: Run dbt Cloud Job
    runs-on: ubuntu-latest

    # Установите переменные окружения, необходимые для выполнения
    env:
    DBT_ACCOUNT_ID: 00000 # введите ваш id учетной записи
    DBT_PROJECT_ID: 00000 # введите ваш id проекта
    DBT_PR_JOB_ID: 00000 # введите ваш id задания
    DBT_API_KEY: ${{ secrets.DBT_API_KEY }}
    DBT_JOB_CAUSE: 'GitHub Pipeline CI Job'
    DBT_JOB_BRANCH: ${{ github.ref_name }}

    steps:
    - uses: "actions/checkout@v4"
    - uses: "actions/setup-python@v5"
    with:
    python-version: "3.9"
    - name: Run dbt Cloud job
    run: "python python/run_and_monitor_dbt_job.py"

    5. Протестируйте ваше новое действие

    Теперь, когда у вас есть новое действие, пришло время протестировать его! Поскольку это изменение настроено на запуск только при слияниях в вашу ветку по умолчанию, вам нужно будет создать и слить это изменение в вашу основную ветку. Как только вы это сделаете, вы увидите, что было запущено новое задание конвейера для выполнения задания dbt Cloud, которое вы назначили в разделе переменных.

    Кроме того, вы увидите задание в истории выполнения dbt Cloud. Его должно быть довольно легко заметить, потому что оно будет отмечено как вызванное API, и в разделе INFO будет указана ветка, которую вы использовали для этого руководства.

    dbt run on merge job in GitHubdbt run on merge job in GitHub
    dbt Cloud job showing it was triggered by GitHubdbt Cloud job showing it was triggered by GitHub

    Запуск задания dbt Cloud при pull request

    Если ваш поставщик git не имеет нативной интеграции с dbt Cloud, но вы все равно хотите воспользоваться преимуществами CI-сборок, вы пришли в нужное место! С небольшими усилиями можно настроить задание, которое будет запускать задание dbt Cloud при создании pull request (PR).

    Запуск на PR

    Если ваш поставщик git имеет нативную интеграцию с dbt Cloud, вы можете воспользоваться инструкциями по настройке здесь. Этот раздел предназначен только для тех проектов, которые подключаются к своему git-репозиторию с использованием SSH-ключа.

    Настройка этого конвейера будет использовать те же шаги, что и на предыдущей странице. Перед тем как продолжить, выполните шаги 1-5 с предыдущей страницы.

    1. Создайте задание конвейера, которое запускается при создании PR

    Для этого задания мы настроим его с использованием файла bitbucket-pipelines.yml, как в предыдущем шаге. Файл YAML будет выглядеть довольно похоже на наше предыдущее задание, но мы передадим необходимые переменные в скрипт Python с помощью операторов export. Обновите этот раздел, чтобы он соответствовал вашей настройке на основе комментариев в файле.

    Что будет делать этот конвейер?
    Настройка ниже запустит задание dbt Cloud каждый раз, когда в этом репозитории создается PR. Она также будет запускать свежую версию конвейера для каждого коммита, который делается в PR, пока он не будет слит. Например: Если вы откроете PR, он запустит конвейер. Если вы затем решите, что нужны дополнительные изменения, и сделаете коммит/пуш в ветку PR, новый конвейер будет запущен с обновленным кодом.

    Следующие переменные управляют этим заданием:

    • DBT_JOB_BRANCH: Указывает заданию dbt Cloud запускать код в ветке, которая создала этот PR
    • DBT_JOB_SCHEMA_OVERRIDE: Указывает заданию dbt Cloud запускать это в пользовательской целевой схеме
      • Формат этого будет выглядеть как: DBT_CLOUD_PR_{REPO_KEY}_{PR_NUMBER}
    image: python:3.11.1


    pipelines:
    # Это задание будет запускаться, когда в репозитории создаются pull requests
    pull-requests:
    '**':
    - step:
    name: 'Run dbt Cloud PR Job'
    script:
    # Проверьте, чтобы строить только если назначение PR - master (или другая ветка).
    # Закомментируйте или удалите строку ниже, если хотите запускать на всех PR, независимо от ветки назначения.
    - if [ "${BITBUCKET_PR_DESTINATION_BRANCH}" != "main" ]; then printf 'PR Destination is not master, exiting.'; exit; fi
    - export DBT_URL="https://cloud.getdbt.com"
    - export DBT_JOB_CAUSE="Bitbucket Pipeline CI Job"
    - export DBT_JOB_BRANCH=$BITBUCKET_BRANCH
    - export DBT_JOB_SCHEMA_OVERRIDE="DBT_CLOUD_PR_"$BITBUCKET_PROJECT_KEY"_"$BITBUCKET_PR_ID
    - export DBT_ACCOUNT_ID=00000 # введите ваш id учетной записи здесь
    - export DBT_PROJECT_ID=00000 # введите ваш id проекта здесь
    - export DBT_PR_JOB_ID=00000 # введите ваш id задания здесь
    - python python/run_and_monitor_dbt_job.py

    2. Подтвердите, что конвейер запускается

    Теперь, когда у вас есть новый конвейер, пришло время запустить его и убедиться, что он работает. Поскольку он запускается только при создании PR, вам нужно будет создать новый PR на ветке, содержащей приведенный выше код. Как только вы это сделаете, вы должны увидеть конвейер, который выглядит следующим образом:

    Конвейер Bitbucket: dbt run on PR job in Bitbucket

    Задание dbt Cloud: dbt Cloud job showing it was triggered by Bitbucket

    3. Обработка этих дополнительных схем в вашей базе данных

    Как отмечено выше, когда задание PR выполняется, оно создаст новую схему на основе PR. Чтобы избежать перегрузки вашей базы данных схемами PR, рассмотрите возможность добавления задания "очистки" в вашу учетную запись dbt Cloud. Это задание может выполняться по расписанию для очистки любых схем PR, которые не были обновлены/использованы недавно.

    Добавьте это как макрос в ваш проект. Он принимает 2 аргумента, которые позволяют контролировать, какие схемы будут удалены:

    • age_in_days: Количество дней с момента последнего изменения схемы, после которого она должна быть удалена (по умолчанию 10 дней)
    • database_to_clean: Имя базы данных, из которой нужно удалить схемы
    {# 
    Этот макрос находит схемы PR старше установленной даты и удаляет их
    Макрос по умолчанию удаляет схемы старше 10 дней, но может быть настроен с помощью входного аргумента age_in_days
    Пример использования с другой датой:
    dbt run-operation pr_schema_cleanup --args "{'database_to_clean': 'analytics','age_in_days':'15'}"
    #}
    {% macro pr_schema_cleanup(database_to_clean, age_in_days=10) %}

    {% set find_old_schemas %}
    select
    'drop schema {{ database_to_clean }}.'||schema_name||';'
    from {{ database_to_clean }}.information_schema.schemata
    where
    catalog_name = '{{ database_to_clean | upper }}'
    and schema_name ilike 'DBT_CLOUD_PR%'
    and last_altered <= (current_date() - interval '{{ age_in_days }} days')
    {% endset %}

    {% if execute %}

    {{ log('Schema drop statements:' ,True) }}

    {% set schema_drop_list = run_query(find_old_schemas).columns[0].values() %}

    {% for schema_to_drop in schema_drop_list %}
    {% do run_query(schema_to_drop) %}
    {{ log(schema_to_drop ,True) }}
    {% endfor %}

    {% endif %}

    {% endmacro %}

    Этот макрос входит в задание dbt Cloud, которое выполняется по расписанию. Команда будет выглядеть следующим образом (текст ниже для копирования/вставки): dbt Cloud job showing the run operation command for the cleanup macro dbt run-operation pr_schema_cleanup --args "{ 'database_to_clean': 'development','age_in_days':15}"

    Учитывайте риск конфликтов при использовании нескольких инструментов оркестрации

    Запуск заданий dbt Cloud через конвейер CI/CD является формой оркестрации заданий. Если вы также запускаете задания с использованием встроенного планировщика dbt Cloud, у вас теперь есть 2 инструмента оркестрации, запускающих задания. Риск в этом заключается в том, что вы можете столкнуться с конфликтами - вы можете представить случай, когда вы запускаете конвейер по определенным действиям и запускаете запланированные задания в dbt Cloud, вы, вероятно, столкнетесь с конфликтами заданий. Чем больше у вас инструментов, тем больше вам нужно убедиться, что все они взаимодействуют друг с другом.

    Тем не менее, если единственная причина, по которой вы хотите использовать конвейеры, это добавление проверки линтинга или запуск при слиянии, вы можете решить, что плюсы перевешивают минусы, и, таким образом, вы хотите использовать гибридный подход. Просто имейте в виду, что если два процесса попытаются запустить одно и то же задание одновременно, dbt Cloud поставит задания в очередь и выполнит одно за другим. Это балансирующий акт, но его можно достичь с помощью тщательности, чтобы гарантировать, что вы оркестрируете задания таким образом, чтобы они не конфликтовали.

    100