在 Google Cloud 上大规模部署 dbt 项目

大规模部署容器化dbt项目

原文:towardsdatascience.com/dbt-deployment-gcp-a350074e3377

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/de794056e9361a3bfb9b8dd32e87d22a.png

图片由 Photo BoardsUnsplash 提供

在大规模管理数据模型是使用 dbt (数据构建工具) 的数据团队面临的常见挑战。最初,团队通常从易于管理和部署的简单模型开始。然而,随着 数据量 的增长和 业务 需求 的演变,这些模型的 复杂性 也随之增加。

这种发展往往导致一个 单体 仓库,其中所有依赖项都交织在一起,使得不同团队难以高效 协作。为了解决这个问题,数据团队可能会发现将他们的数据模型分散到多个 dbt 项目中是有益的。这种方法不仅促进了 更好的组织和模块化,还增强了整个数据基础设施的可扩展性和可维护性。

处理多个 dbt 项目引入的一个显著复杂性是它们的执行和部署方式。管理库依赖性成为一个关键问题,尤其是在不同项目需要不同版本的 dbt 时。虽然 dbt Cloud 提供了强大的解决方案来安排和执行多仓库 dbt 项目,但它需要重大的投资,并非每个组织都能负担得起或认为合理。一个常见的替代方案是使用 Cloud Composer,这是 Google Cloud 管理的 Apache Airflow 服务。

Cloud Composer 提供了一个具有大量预定义依赖项的管理环境。然而,根据我的经验,这种设置带来了重大挑战。在没有遇到 未解决的依赖项 的情况下安装任何 Python 库通常是困难的。当与 dbt-core 一起工作时,我发现由于版本依赖项冲突,在 Cloud Composer 环境中安装特定版本的 dbt 几乎是不可能的。这次经历突显了在 Cloud Composer 上直接运行任何 dbt 版本的难度。

容器化 提供了一种有效的解决方案。您无需在 Cloud Composer 环境中安装库,可以使用 Docker 镜像来容器化您的 dbt 项目,并通过 Cloud Composer 在 Kubernetes 上运行它们。这种方法可以保持您的 Cloud Composer 环境干净,同时允许您在 Docker 镜像中包含任何所需的库。它还提供了灵活性,可以不同的 dbt 版本上运行不同的 dbt 项目,解决依赖冲突,并确保无缝执行和部署。


在解决了管理多个 dbt 项目的复杂性之后,我们现在转向在 Google Cloud 上大规模部署这些项目的技术实现。下面的图示概述了容器化 dbt 项目、将 Docker 镜像存储在 Artifact Registry 中以及使用 GitHub Actions 自动化部署的过程。此外,它还说明了这些项目如何通过开源 Python 包 dbt-airflow 在 Cloud Composer 上执行,该包将 dbt 项目渲染为 Airflow DAG。以下部分将指导您完成每个步骤,提供一种全面的方法来有效地扩展您的 dbt 工作流程。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b3e00bd7d120f9a5cbbfbdb2ecc78fc2.png

Google Cloud 上 dbt 项目部署流程概述 – 来源:作者


使用 GitHub Actions 在 Artifact Registry 上部署容器化的 dbt 项目

在本节中,我们将使用 GitHub Actions 定义一个 CI/CD 管道,以自动化将 dbt 项目作为 Docker 镜像部署到 Google Artifact Registry。此管道将简化流程,确保您的 dbt 项目被容器化,并始终在 Docker 仓库中一致部署,这样 Cloud Composer 就可以从中提取它们。

首先,让我们从对存储库中 dbt 项目的结构有一个高层次概述开始。这将帮助您跟随 CI/CD 管道的定义,因为我们将在某些子目录中工作以完成任务。请注意,Python 依赖项通过 Poetry 管理,因此存在 pyproject.tomlpoetry.lock 文件。如果您以前使用过 dbt,以下的结构应该很容易理解。

.
├── README.md
├── dbt_project.yml
├── macros
├── models
├── packages.yml
├── poetry.lock
├── profiles
├── pyproject.toml
├── seeds
├── snapshots
└── tests

在项目结构就绪后,我们现在可以继续定义 CI/CD 管道。为了确保每个人都能跟上,我们将逐步通过 GitHub Action 工作流程中的每个步骤,并解释每个步骤的目的。这种详细的分解将帮助您了解如何实现和自定义管道以适应您自己的项目。让我们开始吧!


第一步:为 GitHub Action 工作流程创建触发器

我们 GitHub Action 工作流程的上半部分定义了将激活管道的触发器。

name: dbt project deployment
on:
  push:
    branches:
      - main
    paths:
      - 'projects/my_dbt_project/**'
      - '.github/workflows/my_dbt_project_deployment.yml'

实质上,当 projects/my_dbt_project/** 目录或 GitHub Action 工作流程文件发生更改时,由推送到 main 分支的推送事件触发管道。这种设置确保只有在进行相关更改时才运行部署过程,从而保持工作流程高效且最新。


第 2 步:定义一些环境变量

GitHub Action 工作流程的下一部分设置环境变量,这些变量将在后续步骤中使用:

env:
  ARTIFACT_REPOSITORY: europe-west2-docker.pkg.dev/my-gcp-project-name/my-af-repo
  COMPOSER_DAG_BUCKET: composer-env-c1234567-bucket
  DOCKER_IMAGE_NAME: my-dbt-project
  GCP_WORKLOAD_IDENTITY_PROVIDER: projects/11111111111/locations/global/workloadIdentityPools/github-actions/providers/github-actions-provider
  GOOGLE_SERVICE_ACCOUNT: [[email protected]](/cdn-cgi/l/email-protection)
  PYTHON_VERSION: '3.8.12'

这些环境变量存储了部署过程中所需的临界信息,例如 Artifact Registry 存储库、Cloud Composer DAG 存储桶、Docker 镜像名称、服务账户详情和工作负载身份联合。

💡 从高层次来看,Google Cloud 的 Workload Identity 允许在 Google Cloud 上运行的应用程序以安全且可扩展的方式对其身份进行认证和授权。

更多详情,请参阅Google Cloud 文档


第 3 步:检出仓库

GitHub Action 工作流程的下一步是检出仓库:

- uses: actions/[[email protected]](/cdn-cgi/l/email-protection)

此步骤使用 actions/checkout 动作从存储库中拉取最新代码。这确保工作流程可以访问构建和部署 Docker 镜像所需的最新版本的 dbt 项目文件和配置。


第 4 步:认证到 Google Cloud 和 Artifact Registry

工作流程的下一步涉及 Google Cloud 认证

- name: Authenticate to Google Cloud
  id: google_auth
  uses: google-github-actions/[[email protected]](/cdn-cgi/l/email-protection)
  with:
    token_format: access_token
    workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
    service_account: ${{ env.GOOGLE_SERVICE_ACCOUNT }}

- name: Login to Google Artifact Registry
  uses: docker/[[email protected]](/cdn-cgi/l/email-protection)
  with:
    registry: europe-west2-docker.pkg.dev
    username: oauth2accesstoken
    password: ${{ steps.google_auth.outputs.access_token }}

首先,工作流程使用 google-github-actions/auth 动作与 Google Cloud 进行认证。此步骤通过利用提供的 workload identity 提供者和服务账户检索访问令牌。

上一个认证步骤中的访问令牌用于通过指定的注册表(europe-west2-docker.pkg.dev)对 Docker 进行认证。在此登录后,工作流程可以在后续步骤中将 dbt 项目的 Docker 镜像推送到 Artifact Registry。


第 5 步:创建 Python 环境

下一步涉及设置 Python 环境,安装 Poetry 以及管理依赖项。

- name: Install poetry
  uses: snok/[[email protected]](/cdn-cgi/l/email-protection)
  with:
    version: 1.7.1
    virtualenvs-in-project: true

- name: Set up Python ${{ env.PYTHON_VERSION }}
  uses: actions/[[email protected]](/cdn-cgi/l/email-protection)
  with:
    python-version: ${{ env.PYTHON_VERSION }}
    cache: 'poetry'

- name: Load cached venv
  id: cached-poetry-dependencies
  uses: actions/[[email protected]](/cdn-cgi/l/email-protection)
  with:
    path: projects/my_dbt_project/.venv
    key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('projects/my_dbt_project/poetry.lock') }}

- name: Install dependencies
  if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
  working-directory: './projects/my_dbt_project/'
  run: poetry install --no-ansi --no-interaction --sync

我们首先安装 poetry,这是一个依赖管理工具,我们将使用它来安装 Python 依赖项。然后我们安装了指定的 Python 版本,并最终从缓存中加载虚拟环境。如果我们遇到缓存未命中(即自上次工作流程执行以来对 poetry 锁文件进行了更改),则将从零开始安装 Python 依赖项。或者,如果我们遇到缓存命中,则将从缓存中加载依赖项。


第 6 步:编译 dbt 项目

下一步涉及清理 dbt 环境,安装 dbt 依赖项,并编译 dbt 项目。

- name: Clean dbt, install deps and compile
  working-directory: './projects/my_dbt_priject/'
  run: |
    echo "Cleaning dbt"
    poetry run dbt clean --profiles-dir profiles --target prod

    echo "Installing dbt deps"
    poetry run dbt deps

    echo "Compiling dbt"
    poetry run dbt compile --profiles-dir profiles --target prod

此步骤还将生成manifest.json文件,这是 dbt 项目的元数据文件。此文件对于dbt-airflow包至关重要,该包将由 Cloud Composer 用于自动将 dbt 项目渲染为 Airflow DAG。


第 7 步:在工件注册表中构建和推送 Docker 镜像

工作流程的下一步是构建并推送 dbt 项目的 Docker 镜像到 Google Artifact Registry。

- name: Build and Push Docker Image
  run: |
    FULL_ARTIFACT_PATH="${ARTIFACT_REPOSITORY}/${DOCKER_IMAGE_NAME}"
    echo "Building image"
    docker build --build-arg project_name=my_dbt_project --tag ${FULL_ARTIFACT_PATH}:latest --tag ${FULL_ARTIFACT_PATH}:${GITHUB_SHA::7} -f Dockerfile .
    echo "Pushing image to artifact"
    docker push ${FULL_ARTIFACT_PATH} --all-tags

注意我们如何使用两个标签构建镜像,即latest以及短的提交 SHA。这种方法有两个目的;一是能够识别哪个 Docker 镜像版本是最新的,二是能够识别与每个 Docker 镜像关联的提交。后者在需要调试时可能非常有用。


第 8 步:将清单文件与 Cloud Composer GCS 存储桶同步

最后一步涉及将编译后的 dbt 项目,特别是manifest.json文件,同步到 Cloud Composer DAG 存储桶。

- name: Synchronize compiled dbt
  uses: docker://rclone/rclone:1.62
  with:
    args: >-
      sync -v --gcs-bucket-policy-only
      --include="target/manifest.json"
      projects/my_dbt_project/ :googlecloudstorage:${{ env.COMPOSER_DAG_BUCKET }}/dags/dbt/my_dbt_project

此步骤使用rclone Docker 镜像将manifest.json文件与 Cloud Composer 存储桶同步。这对于确保 Cloud Composer 有最新的元数据可用至关重要,这样它就可以被dbt-airflow包获取并渲染 dbt 项目所做的最新更改。


第 9 步:在失败时发送 Slack 警报

最后一步是在部署失败时发送 Slack 警报。为了复制此步骤,您需要发布一个令牌(SLACK_WEBHOOK),如文档中指定。

- name: Slack Alert (on failure)
  if: failure()
  uses: rtCamp/[[email protected]](/cdn-cgi/l/email-protection)
  env:
    SLACK_CHANNEL: alerts-slack-channel
    SLACK_COLOR: ${{ job.status }}
    SLACK_TITLE: 'dbt project deployment failed'
    SLACK_MESSAGE: |
      Your message with more details with regards to 
      the deployment failure goes here.
    SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

GitHub 动作的完整定义如下。如果您在运行它时遇到任何问题,请务必在评论中告诉我,我将尽力帮助您解决问题!

name: dbt project deployment
on:
  push:
    branches:
      - main
    paths:
      - 'projects/my_dbt_project/**'
      - '.github/workflows/my_dbt_project_deployment.yml'

env:
  ARTIFACT_REPOSITORY: europe-west2-docker.pkg.dev/my-gcp-project-name/my-af-repo
  COMPOSER_DAG_BUCKET: composer-env-c1234567-bucket
  DOCKER_IMAGE_NAME: my-dbt-project
  GCP_WORKLOAD_IDENTITY_PROVIDER: projects/11111111111/locations/global/workloadIdentityPools/github-actions/providers/github-actions-provider
  GOOGLE_SERVICE_ACCOUNT: [[email protected]](/cdn-cgi/l/email-protection)
  PYTHON_VERSION: '3.8.12'

jobs:
  deploy-dbt:
    runs-on: ubuntu-22.04
    permissions:
      contents: 'read'
      id-token: 'write'

    steps:
      - uses: actions/[[email protected]](/cdn-cgi/l/email-protection)

      - name: Authenticate to Google Cloud
        id: google_auth
        uses: google-github-actions/[[email protected]](/cdn-cgi/l/email-protection)
        with:
          token_format: access_token
          workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ env.GOOGLE_SERVICE_ACCOUNT }}

      - name: Login to Google Artifact Registry
        uses: docker/[[email protected]](/cdn-cgi/l/email-protection)
        with:
          registry: europe-west2-docker.pkg.dev
          username: oauth2accesstoken
          password: ${{ steps.google_auth.outputs.access_token }}

      - name: Install poetry
        uses: snok/[[email protected]](/cdn-cgi/l/email-protection)
        with:
          version: 1.7.1
          virtualenvs-in-project: true

      - name: Set up Python ${{ env.PYTHON_VERSION }}
        uses: actions/[[email protected]](/cdn-cgi/l/email-protection)
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'poetry'

      - name: Load cached venv
        id: cached-poetry-dependencies
        uses: actions/[[email protected]](/cdn-cgi/l/email-protection)
        with:
          path: projects/my_dbt_project/.venv
          key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('projects/my_dbt_project/poetry.lock') }}

      - name: Install dependencies
        if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
        working-directory: './projects/my_dbt_project/'
        run: poetry install --no-ansi --no-interaction --sync

      - name: Clean dbt, install deps and compile
        working-directory: './projects/my_dbt_priject/'
        run: |
          echo "Cleaning dbt"
          poetry run dbt clean --profiles-dir profiles --target prod

          echo "Installing dbt deps"
          poetry run dbt deps

          echo "Compiling dbt"
          poetry run dbt compile --profiles-dir profiles --target prod

      - name: Build and Push Docker Image
        run: |
          FULL_ARTIFACT_PATH="${ARTIFACT_REPOSITORY}/${DOCKER_IMAGE_NAME}"
          echo "Building image"
          docker build --build-arg project_name=my_dbt_project --tag ${FULL_ARTIFACT_PATH}:latest --tag ${FULL_ARTIFACT_PATH}:${GITHUB_SHA::7} -f Dockerfile .
          echo "Pushing image to artifact"
          docker push ${FULL_ARTIFACT_PATH} --all-tags

      - name: Synchronize compiled dbt
        uses: docker://rclone/rclone:1.62
        with:
          args: >-
            sync -v --gcs-bucket-policy-only
            --include="target/manifest.json"
            projects/my_dbt_project/ :googlecloudstorage:${{ env.COMPOSER_DAG_BUCKET }}/dags/dbt/my_dbt_project

      - name: Slack Alert (on failure)
        if: failure()
        uses: rtCamp/[[email protected]](/cdn-cgi/l/email-protection)
        env:
          SLACK_CHANNEL: alerts-slack-channel
          SLACK_COLOR: ${{ job.status }}
          SLACK_TITLE: 'dbt project deployment failed'
          SLACK_MESSAGE: |
            Your message with more details with regards to 
            the deployment failure goes here.
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

使用 Cloud Composer 和 dbt-airflow 运行 dbt 项目

在上一节中,我们讨论并演示了如何使用 GitHub Actions 在 Google Artifact Registry 上部署 dbt 项目 Docker 镜像。随着我们的 dbt 项目被容器化并安全存储,下一步关键是要确保 Cloud Composer 可以无缝地获取这些 Docker 镜像并执行 dbt 项目作为 Airflow DAG。这正是dbt-airflow包发挥作用的地方。

在本节中,我们将探讨如何配置和使用 Cloud Composer 以及dbt-airflow包来自动运行 dbt 项目。通过集成这些工具,我们可以利用 Apache Airflow 的编排能力,同时保持容器化部署提供的灵活性和可扩展性。


为了确保dbt-airflow包能够在 Cloud Composer 上渲染和执行我们的容器化 dbt 项目,我们需要提供以下内容:

  1. 清单文件路径manifest.json文件在 Google Cloud Storage (GCS)存储桶中的位置,该文件在 CI/CD 过程中由我们的 GitHub Action 推送

  2. Docker 镜像详情:位于 Artifact Registry 上的 Docker 镜像的相关细节,使dbt-airflow能够使用KubernetesPodOperator运行它。

这是 Airflow DAG 的完整定义:

import functools
from datetime import datetime 
from datetime import timedelta
from pathlib import Path

from airflow import DAG
from airflow.operators.empty import EmptyOperator
from airflow.operators.python import PythonOperator
from dbt_airflow.core.config import DbtAirflowConfig
from dbt_airflow.core.config import DbtProfileConfig
from dbt_airflow.core.config import DbtProjectConfig
from dbt_airflow.core.task_group import DbtTaskGroup
from dbt_airflow.operators.execution import ExecutionOperator

with DAG(
    dag_id='test_dag',
    start_date=datetime(2021, 1, 1),
    catchup=False,
    tags=['example'],
    default_args={
        'owner': 'airflow',
        'retries': 1,
        'retry_delay': timedelta(minutes=2),
    },
    'on_failure_callback': functools.partial(
        our_callback_function_to_send_slack_alerts
    ),
) as dag:

    t1 = EmptyOperator(task_id='extract')
    t2 = EmptyOperator(task_id='load')

    tg = DbtTaskGroup(
        group_id='transform',
        dbt_airflow_config=DbtAirflowConfig(
            create_sub_task_groups=True,
            execution_operator=ExecutionOperator.KUBERNETES,
            operator_kwargs={
                'name': f'dbt-project-1-dev',
                'namespace': 'composer-user-workloads',
                'image': 'gcp-region-docker.pkg.dev/gcp-project-name/ar-repo/my-dbt-project:latest',
                'kubernetes_conn_id': 'kubernetes_default',
                'config_file': '/home/airflow/composer_kube_config',
                'image_pull_policy': 'Always',
            },
        ),
        dbt_project_config=DbtProjectConfig(
            project_path=Path('/home/my_dbt_project/'),  # path within docker container
            manifest_path=Path('/home/airflow/gcs/dags/dbt/my_dbt_project/target/manifest.json'),  # path on Cloud Composer GCS bucket
        ),
        dbt_profile_config=DbtProfileConfig(
            profiles_path=Path('/home/my_dbt_project/profiles/'),  # path within docker container
            target=dbt_profile_target,
        ),
    )

    t1 >> t2 >> tg

一旦 Airflow 调度程序获取到文件,您的 dbt 项目将无缝转换为 Airflow DAG。这个自动化过程将您的项目转换为一系列任务,在 Airflow UI 中以可视化的形式表示。

如下截图所示,DAG 已构建并准备好根据定义的日程执行。这种可视化不仅提供了您 dbt 任务的流程和依赖关系的清晰度,还允许轻松监控和管理,确保您的数据转换运行顺畅且高效。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/7ad4d79fe92dd5bafc849f16446138eb.png

使用 dbt-airflow 自动渲染的 dbt 项目 – 来源:dbt-airflow 官方 GitHub 仓库

虽然这个概述为您提供了基础理解,但涵盖dbt-airflow包的全部功能和配置选项超出了本文的范围。

对于那些渴望进一步探索并解锁dbt-airflow的全部潜力,我强烈推荐阅读以下详细文章。它涵盖了您需要了解的所有内容,以确保您能够掌握 dbt 与 Airflow 的集成,从而提升您的数据转换工作流程。

dbt + Airflow = ❤


最后的想法

通过容器化您的 dbt 项目,利用 Artifact Registry、Cloud Composer、GitHub Actions 以及dbt-airflow包,您可以以可扩展和有效的方式简化并自动化您的数据工作流程。

本指南旨在帮助您构建部署流程,为您提供工具和知识,以高效地管理和执行您的 dbt 项目。我真心希望它对您有所启发,并使您对自己的项目实施这些策略充满信心。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值