LoginSignup
8
2
新規開発や新技術の検証、導入にまつわる記事を投稿しよう!

Docker で dbt プロジェクトを始める方法をいくつか試してみた

Last updated at Posted at 2023-07-02

この記事は何?

Docker を使った dbt 環境の構築方法には様々なものがあります(公式の dbt イメージを使う、 Python イメージに dbt をインストールする、 etc...)。

この記事ではその中からいくつかをピックアップして、それぞれでの dbt プロジェクトの開始方法を試してみました。

筆者の実行環境

  • MacBook Pro (M1)
  • Docker (v24.0.2)
  • Docker Compose (v2.18.1)

今回構築した dbt 環境

  • dbt-bigquery (v1.5.1)

今回検討した方法

今回検討した方法は次のとおりです:

# Docker イメージ 認証方法
1-1 公式イメージ( ghcr.io/dbt-labs/dbt-bigquery OAuth 認証
1-2 公式イメージ( ghcr.io/dbt-labs/dbt-bigquery サービスアカウント認証
2-1 Python イメージ( python:3.9 OAuth 認証
2-2 Python イメージ( python:3.9 サービスアカウント認証

なお、どの方法でもやることはおおよそ下記の通りで同じです:

  1. docker-compose.yml など各種ファイルを作成
  2. Docker コンテナを起動して dbt init
  3. 作成された dbt プロジェクトディレクトリ内に 1. で作成した各種ファイルを移動( Git 管理しやすくするため)

補足

手順 2. と 3. はセットで行えるので、今回は dbt_init.sh というシェルスクリプトでまとめて行うようにしています。

構築した環境の動作チェック方法

今回、「構築した dbt 環境がちゃんと動くか?」の確認として次のチェックを行いました:

  • dbt debug の実行結果に All checks passed! と表示される
  • dbt docs serve でドキュメントが確認できる

補足

記事内では省略していますが、 dbt docs serve 実行前に dbt docs generate の実行が必要です。

方法 1-1 |公式イメージ& OAuth 認証

STEP 1 |ファイル作成

ディレクトリ構造を次のようにします:

.
|-- .env
|-- dbt_init.sh
|-- docker-compose.yml
|-- Dockerfile
|-- requirements.txt

各ファイルには次の内容を記載します:

.env
WORKING_DIR=/usr/src/app
dbt_init.sh
#!/bin/bash

PROJECT_NAME=$1
PJ_LOCATION=$2

dbt init $PROJECT_NAME
cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml
sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml
mv .env dbt_init.sh docker-compose.yml Dockerfile requirements.txt $PROJECT_NAME/

cat $PROJECT_NAME/profiles.yml

補足 1 | cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml の役割

dbt init で設定した接続情報は ~/.dbt/profiles.yml へ書き出されます。しかし、今回の Docker のマウント設定だと ~/.dbt/profiles.yml は Docker コンテナを落としたタイミングで消えてしまいます。

dbt では  profiles.yml の探索順としてまず作業ディレクトリ内を探し、そこに無かった場合に ~/.dbt/profiles.yml を読みに行く仕様となっています 1 。そこで、  cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml を行うことで、次回以降 Docker コンテナを起動した時に以前の接続情報を利用できるようにしています。

補足 2 | sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml の役割

dbt-bigquery では dbt init 時に選択できる location は USEU しかありません 2 。そのため、この2つ以外の location を設定したい場合は dbt init 完了後に profiles.yml を直接編集する必要があります。この操作を sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml で行っています。

docker-compose.yml
version: "3.8"

services:
  app:
    build: .
    image: dbt_env_official_image_oauth
    container_name: dbt_env_official_image_oauth
    env_file:
      - ./.env
    volumes:
      - ./:$WORKING_DIR
    working_dir: $WORKING_DIR
    ports:
      - 8080:8080
    entrypoint: /bin/bash

補足| entrypoint: /bin/bash の役割

公式イメージの Entrypoint は dbt に設定されています 3 。このままだと dbt_init.sh の実行が困難なので、 Entrypoint を /bin/bash へ変更しています。

Dockerfile
# ベースイメージ
FROM ghcr.io/dbt-labs/dbt-bigquery:1.5.1

# 必要なファイルをコンテナ内へコピー
COPY requirements.txt ${WORKING_DIR}

# 依存のインストール
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
    gnupg \
    curl

# gcloud CLI のインストール
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg  add - && apt-get update -y && apt-get install google-cloud-cli -y

# Python ライブラリのインストール
RUN pip install --upgrade pip && \
    pip install -r requirements.txt

補足| Docker イメージへの gcloud コマンドのインストール

下記公式ドキュメントに実行ステップが記載されています:

requirements.txt
# 好きなライブラリを記述してください

STEP 2 | dbt プロジェクトを初期化

次のコマンドを実行します:

$ docker compose build
$ docker compose run --rm app \
  ./dbt_init.sh <YOUR_DBT_PJ_NAME> <YOUR_GCP_PJ_LOCATION>

コマンドが完了したら次のようなディレクトリ構造になっているはずです:

.
|-- <YOUR_DBT_PJ_NAME>/
    |-- 〜〜( dbt 関連のディレクトリやファイルは省略)〜〜
    |-- .env
    |-- dbt_init.sh
    |-- docker-compose.yml
    |-- Dockerfile
    |-- requirements.txt

チェック 1 | dbt debug

STEP 2 が完了した状態で cd <YOUR_DBT_PJ_NAME>/ した後、次のコマンドを実行します:

$ docker compose run --rm --service-ports app
[In container]# gcloud auth application-default login
[In container]# dbt debug

実行結果が All checks passed! になれば OK です。

gcloud auth application-default login はコンテナを起動する度に実行する必要があります(理由は補足 2 を参照)。

補足 1 | --service-ports フラグをつける理由

2つめの違いとして、 docker-compose run コマンドはサービス設定ファイルで指定したポートを作成しません。これは、既に開いているポートとの衝突を避けるためです。サービス用のポートを作成し、ホスト側に割り当てるには、 --service-ports フラグを使います。
引用: https://docs.docker.jp/compose/reference/run.html

補足 2 | gcloud auth application-default login をコンテナ起動の度に実行する必要がある理由

gcloud auth application-default login は GCP の Application Default Credentials (ADC) を設定するコマンドです 4 。これにより、 Docker コンテナ内でも gcloud コマンドを使って dbt の OAuth 認証が通るようにしています。

ADC ではローカルに認証情報を保存します。保存先はコンテナ内の $HOME/.config/gcloud/application_default_credentials.json です 5 。しかし、今回その場所は Docker の Volume で指定したり等をしていないので、コンテナを落とすたびに削除されてしまいます。そのため、コンテナ起動のたびにコマンドの実行が必要となります。

チェック 2 | dbt docs serve

Docker コンテナ内に入った状態で次のコマンドを実行します:

[In container]# dbt docs serve

ブラウザからドキュメントが確認できれば OK です。

方法 1-2 |公式イメージ&サービスアカウント認証

STEP 1 |ファイル作成

ディレクトリ構造を次のようにします:

.
|-- .env
|-- dbt_init.sh
|-- docker-compose.yml
|-- <GCP_SA_KEYFILE_NAME>.json
|-- requirements.txt

各ファイルには次の内容を記載します:

.env
WORKING_DIR=/usr/src/app
dbt_init.sh
#!/bin/bash

PROJECT_NAME=$1
PJ_LOCATION=$2
KEYFILE_NAME=$3

dbt init $PROJECT_NAME
cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml
sed -i '/keyfile:/c\      keyfile: '$WORKING_DIR'/'$KEYFILE_NAME'' $PROJECT_NAME/profiles.yml
sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml
mv .env dbt_init.sh docker-compose.yml $KEYFILE_NAME requirements.txt $PROJECT_NAME/

cat $PROJECT_NAME/profiles.yml
docker-compose.yml
version: "3.8"

services:
  app:
    image: ghcr.io/dbt-labs/dbt-bigquery:1.5.1
    container_name: dbt_env_official_image_sa
    env_file:
      - ./.env
    volumes:
      - ./:$WORKING_DIR
    working_dir: $WORKING_DIR
    ports:
      - 8080:8080
requirements.txt
# 好きなライブラリを記述してください

STEP 2 | dbt プロジェクトを初期化

次のコマンドを実行します:

$ docker compose run --rm --entrypoint /bin/bash app \
  ./dbt_init.sh <YOUR_DBT_PJ_NAME> <YOUR_GCP_PJ_LOCATION> <GCP_SA_KEYFILE_NAME>.json

dbt の初期設定に関する質問ですが、 keyfile: の回答については任意の文字列で問題ありません ( dbt_init.sh 内の処理で自動的に設定されるため)。

コマンドが完了したら次のようなディレクトリ構造になっているはずです:

.
|-- <YOUR_DBT_PJ_NAME>/
    |-- 〜〜( dbt 関連のディレクトリやファイルは省略)〜〜
    |-- .env
    |-- dbt_init.sh
    |-- docker-compose.yml
    |-- <GCP_SA_KEYFILE_NAME>.json
    |-- requirements.txt

チェック 1 | dbt debug

STEP 2 が完了した状態で cd <YOUR_DBT_PJ_NAME>/ した後、次のコマンドを実行します:

$ docker compose run --rm app debug

実行結果が All checks passed! になれば OK です。

チェック 2 | dbt docs serve

次のコマンドを実行します:

$ docker compose run --rm --service-ports app docs serve

ブラウザからドキュメントが確認できれば OK です。

方法 2-1 | Python イメージ& OAuth 認証

STEP 1 |ファイル作成

ディレクトリ構造を次のようにします:

.
|-- .env
|-- dbt_init.sh
|-- docker-compose.yml
|-- Dockerfile
|-- requirements.txt
.env
WORKING_DIR=/usr/src/app
dbt_init.sh
#!/bin/bash

PROJECT_NAME=$1
PJ_LOCATION=$2

dbt init $PROJECT_NAME
cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml
sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml
mv .env dbt_init.sh docker-compose.yml Dockerfile requirements.txt $PROJECT_NAME/

cat $PROJECT_NAME/profiles.yml
docker-compose.yml
version: "3.8"

services:
  app:
    build: .
    image: dbt_env_python_image_oauth
    container_name: dbt_env_python_image_oauth
    env_file:
      - ./.env
    volumes:
      - ./:$WORKING_DIR
    working_dir: $WORKING_DIR
    tty: true
    ports:
      - 8080:8080
Dockerfile
# ベースイメージ
FROM python:3.9

# 必要なファイルをコンテナ内へコピー
COPY requirements.txt ${WORKING_DIR}

# 依存のインストール
RUN apt-get update && apt-get upgrade -y

# gcloud CLI のインストール
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg  add - && apt-get update -y && apt-get install google-cloud-cli -y

# Python ライブラリのインストール
RUN pip install --upgrade pip && \
    pip install -r requirements.txt
requirements.txt
dbt-bigquery==1.5.1

STEP 2 | dbt プロジェクトを初期化

次のコマンドを実行します:

$ docker compose build
$ docker compose up -d
$ docker exec -it <CONTAINER_ID> bash
[In container]# chmod 744 ./dbt_init.sh
[In container]# ./dbt_init.sh <YOUR_DBT_PJ_NAME> <YOUR_GCP_PJ_LOCATION>

補足| docker compose rundbt_init.sh を実行しない理由

docker compose rundbt_init.sh を実行しようとしたところ Permission Denined と言われてしまったので、コンテナ内に入って直接実行する方法を採っています。

該当エラーメッセージ
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "./dbt_init.sh": permission denied: unknown

コマンドが完了したら次のようなディレクトリ構造になっているはずです:

.
|-- <YOUR_DBT_PJ_NAME>/
    |-- 〜〜( dbt 関連のディレクトリやファイルは省略)〜〜
    |-- .env
    |-- dbt_init.sh
    |-- docker-compose.yml
    |-- Dockerfile
    |-- requirements.txt

チェック 1 | dbt debug

STEP 2 が完了した状態で cd <YOUR_DBT_PJ_NAME>/ した後、次のコマンドを実行します:

[In container]# gcloud auth application-default login
[In container]# dbt debug

実行結果が All checks passed! になれば OK です。

gcloud auth application-default login はコンテナを起動するたび実行する必要があります(理由は先ほどと同じ)。

チェック 2 | dbt docs serve

Docker コンテナ内で次のコマンドを実行します:

[In container]# dbt docs serve

ブラウザからドキュメントが確認できれば OK です。

方法 2-2 | Python イメージ&サービスアカウント認証

STEP 1 |ファイル作成

ディレクトリ構造を次のようにします:

.
|-- .env
|-- dbt_init.sh
|-- docker-compose.yml
|-- Dockerfile
|-- <GCP_SA_KEYFILE_NAME>.json
|-- requirements.txt

各ファイルには次の内容を記載します:

.env
WORKING_DIR=/usr/src/app
dbt_init.sh
#!/bin/bash

PROJECT_NAME=$1
PJ_LOCATION=$2
KEYFILE_NAME=$3

dbt init $PROJECT_NAME
cat ~/.dbt/profiles.yml > $PROJECT_NAME/profiles.yml
sed -i '/keyfile:/c\      keyfile: '$WORKING_DIR'/'$KEYFILE_NAME'' $PROJECT_NAME/profiles.yml
sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.yml
mv .env dbt_init.sh docker-compose.yml Dockerfile $KEYFILE_NAME requirements.txt $PROJECT_NAME/

cat $PROJECT_NAME/profiles.yml
Dockerfile
# ベースイメージ
FROM python:3.9

# 必要なファイルをコンテナ内へコピー
COPY requirements.txt ${WORKING_DIR}

# 依存のインストール
RUN apt-get update && apt-get upgrade -y

# Python ライブラリのインストール
RUN pip install --upgrade pip && \
    pip install -r requirements.txt
docker-compose.yml
version: "3.8"

services:
  app:
    build: .
    image: dbt_env_python_image_sa
    container_name: dbt_env_python_image_sa
    env_file:
      - ./.env
    volumes:
      - ./:$WORKING_DIR
    working_dir: $WORKING_DIR
    tty: true
    ports:
      - 8080:8080
requirements.txt
dbt-bigquery==1.5.1

STEP 2 | dbt プロジェクトを初期化

次のコマンドを実行する:

$ docker compose build
$ docker compose up -d
$ docker exec -it <CONTAINER_ID> bash
[In container]# chmod 744 ./dbt_init.sh
[In container]# ./dbt_init.sh <YOUR_DBT_PJ_NAME> <YOUR_GCP_PJ_LOCATION> <GCP_SA_KEYFILE_NAME>.json
[In container]# exit
$ docker stop <CONTAINER_ID>

dbt の初期設定に関する質問ですが、 keyfile: の回答については任意の文字列で問題ありません( dbt_init.sh 内の処理で自動的に設定されるため)。

コマンドが完了したら次のようなディレクトリ構造になっているはずです:

.
|-- <YOUR_DBT_PJ_NAME>/
    |-- 〜〜( dbt 関連のディレクトリやファイルは省略)〜〜
    |-- .env
    |-- dbt_init.sh
    |-- docker-compose.yml
    |-- Dockerfile
    |-- <GCP_SA_KEYFILE_NAME>.json
    |-- requirements.txt

補足|なぜコンテナを落とす必要があるのか?

docker-compose.yml でのマウント設定:

- ./:$WORKING_DIR

上記手順の docker exec -it <CONTAINER_ID> bash 実行直後のコンテナ内の状態:

$WORKING_DIR
|-- docker-compose.yml
|-- <GCP_SA_KEYFILE_NAME>.json
|-- ...

dbt_init.sh 実行直後のコンテナ内の状態。 docker-compose.yml などが dbt プロジェクトディレクトリ内へ移動しています:

$WORKING_DIR
|-- <YOUR_DBT_PJ_NAME>/
    |-- docker-compose.yml
    |-- <GCP_SA_KEYFILE_NAME>.json
    |-- ...

次回以降、コンテナを起動した時の状態。 dbt プロジェクトディレクトリが作業ディレクトリとしてマウントされています:

$WORKING_DIR
|-- docker-compose.yml
|-- <GCP_SA_KEYFILE_NAME>.json
|-- ...

dbt_init.sh では keyfile:<GCP_SA_KEYFILE_NAME>.json (=次回以降、コンテナを起動した時のパス)で設定しています。そのため、 dbt_init.sh 実行直後の状態だとパスが合いません。そこで、一旦コンテナを落とすことでこの問題を解決しています。

なお、コンテナ内で docker-compose.yml を移動させたため docker compose down ではコンテナを落とせなくなっています。そのため、 docker stop でコンテナを止めています。

(この「 Python イメージ&サービスアカウント認証」の方法では、 dbt_init.sh でやるより手動で操作したほうが良いのかもしれません…)

チェック 1 | dbt debug

STEP 2 が完了した状態で cd <YOUR_DBT_PJ_NAME>/ した後、次のコマンドを実行する:

$ docker compose up -d
$ docker exec -it <CONTAINER_ID> bash
[In container]# dbt debug

実行結果が All checks passed! になれば OK です。

チェック 2 | dbt docs serve

Docker コンテナ内に入った状態で次のコマンドを実行します:

[In container]# dbt docs serve

ブラウザからドキュメントが確認できれば OK です。

検討してみた所感

  • 今回試した中だと方法 1-2 「公式イメージ&サービスアカウント認証」が一番シンプルかなぁ…
    • Docker イメージのビルドが不要だし…
    • Docker コンテナ起動の度に認証求められたりもしないし…
      • これに関しては OAuth 認証でもっと良い方法がありそうな気はしているが…
    • docker compose run --rm [--service-ports] app <DBT_COMMAND> とコマンドが長いのはやっててそのうち面倒に感じ始めそう…
      • Makefile を使えば多少改善できる?
        • 引数が可変になってくると難しそう…
  • とはいえ、  venv で済ませられるならそれが一番手っ取り早い気はする

結び

今回は Docker を使って dbt プロジェクトを始める方法をいくつか試してみました。

ミスやより良い方法などがございましたら、コメント等でご指摘いただければ幸いです :bow:

  1. Connection profiles | dbt Developer Hub を参照

  2. dbt init 時に訊かれる profiles.yml 設定項目は profile_template.yml で定義されています( →参照 )。 dbt-bigquery では次のように定義されています( →参照 )。 dbt-bigquery の profile_template.yml を見てみると、 location の選択肢が USEU しか無いことが確認できます。

  3. Install with Docker | dbt Developer Hub を参照

  4. アプリケーションのデフォルト認証情報を設定する  |  Google Cloud を参照

  5. アプリケーションのデフォルト認証情報の仕組み  |  Google Cloud 参照

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2