この記事は何?
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 ) |
サービスアカウント認証 |
なお、どの方法でもやることはおおよそ下記の通りで同じです:
- docker-compose.yml など各種ファイルを作成
- Docker コンテナを起動して
dbt init
- 作成された 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
各ファイルには次の内容を記載します:
WORKING_DIR=/usr/src/app
#!/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 は US
と EU
しかありません 2 。そのため、この2つ以外の location を設定したい場合は dbt init
完了後に profiles.yml
を直接編集する必要があります。この操作を sed -i 's/location: US/location: '$PJ_LOCATION'/' $PROJECT_NAME/profiles.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
へ変更しています。
# ベースイメージ
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 コマンドのインストール
下記公式ドキュメントに実行ステップが記載されています:
# 好きなライブラリを記述してください
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
各ファイルには次の内容を記載します:
WORKING_DIR=/usr/src/app
#!/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
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
# 好きなライブラリを記述してください
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
WORKING_DIR=/usr/src/app
#!/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
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
# ベースイメージ
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
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 run
で dbt_init.sh
を実行しない理由
docker compose run
で dbt_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
各ファイルには次の内容を記載します:
WORKING_DIR=/usr/src/app
#!/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
# ベースイメージ
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
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
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 を使えば多少改善できる?
- 引数が可変になってくると難しそう…
- Makefile を使えば多少改善できる?
- とはいえ、 venv で済ませられるならそれが一番手っ取り早い気はする
結び
今回は Docker を使って dbt プロジェクトを始める方法をいくつか試してみました。
ミスやより良い方法などがございましたら、コメント等でご指摘いただければ幸いです