Edited at

digdag タスクの docker 実行について

ドキュメントに細かいことが書いてなかったのでメモしておく。

digdag のバージョンは v0.9.31


まえおき: digdag タスクの docker 実行

_export に docker オプションを指定すると各タスクが docker run で実行されるようになる。この機能についての解説。digdag 自体を Docker に入れ込むとかは別の話。

_export:

docker:
image: ubuntu

+task1:
py>: tasks.MyWorkflow.task1


digdag docker オプションのオプション

以下の2つが使える


  • build: String

  • pull_always: Boolean


build

build を指定すると docker build が走る

_export:

docker:
image: ubuntu:latest
build: |
apt install -y git

+task1:
py>: tasks.MyWorkflow.task1


pull_always

pull_always: true とすると各タスクが実行される度に docker pull を試すようになる。

_export:

docker:
image: ubuntu:latest
pull_always: true

+task1:
py>: tasks.MyWorkflow.task1

いまのところ、ワークフロー1つで1回だけ docker pull するオプションはない。


コンテナ内で実行されるのは CommandExecutor なオペレータのみ

コマンドとして実行されるオペレータのみ docker run される。現状では以下の4つのみ


  • py>:

  • rb>:

  • sh>:

  • embulk>: (deprecated)

他のオペレータ (echo, s3_wait, gcs_wait など) は java ライブラリとして実装されていることもあり、docker コンテナ内ではなく、docker ホスト上で実行される。

docker コンテナに GCP の鍵を登録してあっても、例えば gcs_wait はその鍵を使ってくれないので、認識しておく必要がある。


コンテナ内のファイルシステムに書いたファイルは消える

タスクそれぞれが docker run されるので、コンテナ内のファイルシステムに書いたファイルは消える。

_export:

docker:
image: ubuntu

+task1:
sh>: touch /tmp/test

+task2:
sh>: ls /tmp/test # <- Not found

そのため && でつなげて実行するなどしないといけない。

_export:

docker:
image: ubuntu

+task1:
sh>: |
touch /tmp/test &&
ls /tmp/test


docker run に渡されるオプション

現行のv0.9.31では、タスクごとに以下のような docker run を実行しているようだ。

FYI: digdag 実行時に -l debug オプションをつけると確認できる。

$ docker run -i --rm -v プロジェクトパス:プロジェクトパス \

-w プロジェクトパス -e 環境変数=値... イメージ名 コマンド

ポイントは以下のあたり


  1. プロジェクトパスが docker コンテナにマウントされる

  2. ワーキングディレクトリが Dockerfile の WORKDIR から digdag のプロジェクトパスに変更される

  3. ホストの環境変数がコンテナに引き継がれる (HOMEUSER も変更される)

プロジェクトパスは、digdag run の場合は実行しているプロジェクトのパスで、digdag server の場合は /tmp 下のてきとうな tmp ディレクトリにプロジェクトファイルが撒かれて使われる。その tmp ディレクトリはコマンド実行が終わったら消される。

そのため digdag run の時はカレントディレクトリ以下に書いたファイルは docker ホストに永続化できる。digdag server の時は結局消されるので永続化のつもりでは使えない。

また、digdag server が作る tmp ディレクトリを docker コンテナから読み書きできるようにしておく必要がある。docker コンテナ内を root (uid: 0) ユーザにしておくとか、digdag server を動かすユーザの uid と、Dockerfile の USER で指定するユーザの uid を合わせておく必要がある。

3によって、環境変数が上書きされるのは注意が必要。特に HOME 環境変数が変わっているので、

_export:

HOME: もともとDockerfileで想定していたWORKDIR
docker:
...

のようにして戻してあげたくなることが多そう。

所感: これは ... 余計なお世話度が高いような...


digdag secrets で登録した値を docker から参照したい

digdag secrets で渡した値を _env で環境変数として渡せばなんとかできる。

oO(_env はタスク毎に設定しないといけないのでかなり面倒

+docker_run:

_env:
secret_gcp_credential: ${secret:gcp.credential}
secret_aws_access_key_id: ${secret:aws.access_key_id}
secret_aws_secret_access_key: ${secret:aws_secret_access_key}
sh>: echo test

FYI: ちなみに digdag secrets --set gcp.credentail=@gcpcredential.json のようにファイルで渡したものは ${secret:gcp.crecential} ではファイル内容が文字列として展開される。


Dockerfile では docker イメージに入れ込まないようにして、ENTRYPOINT で起動時に配置するようにすると digdag secrets から値を取れたことになる。


Dockerfile

....

COPY docker-entrypoint.sh docker-entrypoint.sh
ENTRYPOINT ["./docker-entrypoint.sh"]


docker-entorypoint.sh

#!/bin/bash

# Add aws credential
mkdir -p .aws
cat > .aws/credentials <<EOF
[default]
aws_access_key_id =
${secret_aws_access_key_id}
aws_secret_access_key =
${secret_aws_secret_access_key}
EOF

# Add gcp credential and activate
mkdir -p .credential
echo "${secret_gcp_credential}" > .credential/gcpcredential.json
gcloud auth activate-service-account xxxxx --key-file .credential/gcpcredential.json --project xxxxx

exec "$@"

(AWSクレデンシャルに関しては、aws cli も aws sdk も環境変数 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY に対応してるのでそちらを使っても良さそう)

SEE ALSO: digdag secrets を global な環境変数にマップする - Qiita


docker pull の前に $(aws ecr get-login) したい

Amazon ECR レジストリ に docker イメージを push している場合、$(aws ecr get-login) のようにして docker login してからでないと docker pull できない。12時間でログイントークンが expire されるので厄介なやつ。

+docker_login:

sh>: eval $(aws ecr get-login --no-include-email)

+docker_run:
_export:
docker:
image: ubuntu:latest
pull_always: true

+task1:
py>: tasks.MyWorkflow.task1

このように書けば1台構成の場合は、docker login してから、以降のタスクを実行できるので、確実に docker pull できる。

しかし、複数台構成の場合、タスク毎にどのホストで実行されるかが不定なので、docker login していないホストで docker pull && docker run しようとして失敗する可能性がある。

色々調べた結論としては、https://github.com/awslabs/amazon-ecr-credential-helper を使うと、docker login を省いて docker pull できるようになり、digdag 側で頑張らなくても解決できるのでオススメする。

その際 ~/.docker/config.json には

{"credsStore": "ecr-login"}

よりも

{

"credHelpers": {
"<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
}
}

のように設定するのをオススメする。前者の場合、ECR 以外の例えば docker hub のイメージを pull する場合も ecr-login を試して ERROR メッセージが表示されてしまう(実際には docker pull は成功するが).


docker run のオプションを設定したい

EDIT: 0.9.37 から docker run のオプションを指定できるようになりました。https://github.com/treasure-data/digdag/pull/1025

以下、古い文書


いまのところ、docker run のオプションを自由に設定する _export: docker: のオプションはない。

発想の転換で、_export: docker: を使わず sh>: で docker run してしまえば自由度は得られる。好きなタイミングで docker pull も docker login もできる。

_export:

docker_image: ubuntu
docker_run: docker run -i --rm -v /tmp:/tmp ${docker_image}

+docker_pull:
sh>: docker pull ${docker_image}

+task1:
sh>: ${docker_run} コマンド


ポイントまとめ


  • コンテナ内で実行されるのは CommandExecutor なオペレータ(py, rb, sh) のみ

  • コンテナ内でローカルに書いたファイルは揮発する (--volume オプションの設定もできない)

  • ホスト側の環境変数でコンテナ側の環境変数が上書きされる

  • (今の所)docker のオプションを自由に設定することはできないので、どうしても必要なら _export: docker: をやめて sh>: docker run するしかない...


要望まとめ


  • ホスト側の環境変数が全部引き継がれるのはちょっと余計なお世話度が高いような...

  • docker run の --volume オプションとか色々オプション追加サポートして欲しい。とりあえず EFS マウントしたい。

  • docker run の前に確実に docker login するようなオプションもあっても良いかもしれない (その時は $(aws ecr get-login) が実行できるようにシェル実行サポートして欲しい)