はじめに
この記事はMicroAd Advent Calendar 2017の1日目の記事です。
Digdagはジョブをコンテナで実行する事ができ、スケールが容易なワークフローエンジンです。
本エントリではそんなDigdagサーバ自体もコンテナで動かしながらジョブもコンテナで動かす際の設定やハマり所を紹介します。
使用ソフトウェアとバージョン
- digdag: 0.9.21
- docker: 1.12.6
全体像
先にどんな感じの構成を実現しようとしているか、全体像を図示します。
_export:
docker:
image: python:3
+-------------+
| |
| |
+---+----+ +----v---+
| | | |
docker run | digdag | | python |
+---------> server | | (job) |
| | | | |
| +--------+ +--------+
|
| +-------------------------+
| | docker |
| +-------------------------+
| +-------------------------+
+-------+ HostOS |
+-------------------------+
こんな感じです。
digdagサーバ自体もコンテナとして動きつつ、ジョブのコンテナも兄弟コンテナとして立ち上げます。
だもんでdigdagサーバはdocker.sock
をマウントする感じになります。
digdagサーバとジョブのどちらもコンテナ化する事により、スケールなど容易になります。
ちなみに今回紹介するサンプルは以下に突っ込んでいます。
https://github.com/kanga333/digdag-server
https://hub.docker.com/r/kanga333/digdag-server/
digdagサーバーのコンテナを作る
digdagサーバのDockerイメージはこんな感じになります。
FROM openjdk:8-jdk
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64
RUN apt-get update && apt-get install -y \
curl gettext-base postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# Installin docker client
ENV DOCKER_CLIENT_VERSION=1.12.6 \
DOCKER_API_VERSION=1.24
RUN curl -fsSL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_CLIENT_VERSION}.tgz \
| tar -xzC /usr/local/bin --strip=1 docker/docker
# Installing digdag server
RUN curl -o /usr/local/bin/digdag --create-dirs -L "https://dl.digdag.io/digdag-latest" && \
chmod +x /usr/local/bin/digdag
# Environment variable for default setting
ENV POSTGRES_USER=digdag \
POSTGRES_PASSWORD=digdag \
POSTGRES_HOST=postgresql \
POSTGRES_PORT=5432 \
POSTGRES_DB=digdag \
LOG_TYPE=local \
ENCRYPTION_KEY=MDEyMzQ1Njc4OTAxMjM0NQ==
COPY files/entrypoint.sh /usr/local/bin
COPY files/server.properties /etc/digdag/server.properties
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 65432 65433
ENTRYPOINT ["/usr/local/bin/entrypoint.sh","/usr/local/bin/digdag","server","--config","/etc/digdag/server.properties"]
server.bind = 0.0.0.0
server.port = 65432
server.admin.bind = 0.0.0.0
server.admin.port = 65433
server.access-log.pattern = json
database.type = postgresql
database.user = $POSTGRES_USER
database.password = $POSTGRES_PASSWORD
database.host = $POSTGRES_HOST
database.port = $POSTGRES_PORT
database.database = $POSTGRES_DB
log-server.type = $LOG_TYPE
log-server.s3.bucket = $LOG_S3_BUCKET
log-server.s3.path = $LOG_S3_PATH
log-server.s3.credentials.access-key-id = $AWS_ACCESS_KEY_ID
log-server.s3.credentials.secret-access-key = $AWS_SECRET_ACCESS_KEY
digdag.secret-encryption-key = $ENCRYPTION_KEY
#!/usr/bin/env bash
set -e
# rendering server.properties
envsubst < /etc/digdag/server.properties > /etc/digdag/server.properties
# rendering pgpass file
echo "$POSTGRES_HOST:$POSTGRES_PORT:$POSTGRES_DB:$POSTGRES_USER:$POSTGRES_PASSWORD" > ~/.pgpass
chmod 600 ~/.pgpass
# wait for postgresup
until psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -p "$POSTGRES_PORT" -c '\l'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 10
done
>&2 echo "Postgres is up - executing command"
exec "$@"
ポイントは以下3点です。
- Dockerクライアントをインストールする
- DB接続先などの設定をある程度環境変数で差し替えれるようにする
- PostgreSQLの立ち上がりを待つ
それぞれについての詳細です。
Dockerクライアントをインストールする
digdagサーバを動かすコンテナはジョブ実行コンテナを起動させる必要があります。
今回の方法ではマウントしたdocker.sockを通じてホストのDockerデーモンとやり取りすることを想定しています。
そのためDockerクライアントのインストールをしています。
Dockerクライアントのインストールについては以下の記事に詳しく書かれており参考になります。
https://qiita.com/minamijoyo/items/c937fb4f646dc1ff064a
DB接続先などの設定をある程度環境変数で差し替えれるようにする
Dockerイメージを作るに際し、DB接続先やパスワードなんかの設定は環境変数で変更できるようにしておくと、手元の実行時や本番での実行時に接続先を簡単に切り替えることができて何かと便利です。
Digdagサーバは現状では設定を環境変数で変更できるようにはなってはいないので、entrypoint.shみたいinitスクリプトとenvsubstを使ってコンテナ起動時に設定ファイルを環境変数からレンダリングするようにしました。
ちなみに先程参考にした記事と同じ人の記事ですが、envsubstについては以下に詳しく書かれており参考になります。
https://qiita.com/minamijoyo/items/63ae57b99d4a4c5d7987
シグナルのハンドルを真面目に考えるとinitはもう少しちゃんと考えないといけないのですが、一旦まぁこれで。
PostgreSQLの立ち上がりを待つ
docker-composeとかでdigdagとPostgreSQLを同時に起動したりすると、PostgreSQLの起動が完了する前にdigdagがPostgreSQLに接続に行き、例外を起こしてダウンしてしまうケースがあります。
本番で使う場合はPostgreSQLは別立てするなりマネージド使ったりするケースが殆どだと思うので、あまり気にはならないですが、手元で確認するときに若干面倒なので、initスクリプトにdigdagの起動を待つ処理を入れています。
docker-composeのサンプル
作ったDockerイメージをdocker-composeで起動するならこんな感じになります。
手元で動かす場合
version: '2'
services:
digdag:
image: kanga333/digdag-server
ports:
- "65432:65432"
- "65433:65433"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /tmp:/tmp
postgresql:
image: postgres:9
environment:
- POSTGRES_USER=digdag
- POSTGRES_PASSWORD=digdag
- POSTGRES_DB=digdag
volumes:
- data:/var/lib/postgresql/data
command:
- postgres
- -c
- superuser_reserved_connections=30
- -c
- max_connections=1000
volumes:
data:
本番で動かすなら
version: '2'
services:
digdag:
image: kanga333/digdag-server
ports:
- "65432:65432"
- "65433:65433"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /tmp:/tmp
environment:
- POSTGRES_USER=digdag
- POSTGRES_DB=digdag
- POSTGRES_HOST=production_pg
- LOG_TYPE=s3
- LOG_S3_BUCKET=hogehoge-digdag
- LOG_S3_PATH=logs/
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- POSTGRES_PASSWORD
- ENCRYPTION_KEY
クレデンシャルな情報はホストの環境変数から渡す想定です。
/tmpのマウント意図
最後にこれが一番重要というかハマり所なんですが、Digdagサーバもジョブもコンテナで動かす場合、digdagサーバはホストの/tmpをマウントする必要があります。
digdagはPyオペレータなどのジョブをコンテナで動かそうとした場合、一旦digdagサーバの/tmp配下にワークディレクトリを作ってPythonスクリプトを出力し、ジョブコンテナはそのワークディレクトリをマウントすることで必要なPythonスクリプトを参照する、といった動きをします。
digdagサーバもコンテナで起動している場合は、コンテナ内部で動いているdigdagサーバのファイルシステム空間と、ジョブコンテナを実際に起動させるdockerデーモンがいるホストのファイルシステム空間が異なるためスクリプトがうまく渡せないという事象が発生します。
/tmpをマウントすることでコンテナ内のDigdagサーバもホストのファイルシステムの/tmpを認識するので上手くファイルを受け渡す事ができるようになります。
ちなみにこの問題をissueに上げた所、回避策を@hiroysatoさんと@mapk0yさんに教えて頂きました。ありがとうございました。
終わりに
以上、Digdagサーバをコンテナ化する際の一例でした。
余裕があれば、いつか他のDigdag運用TIPSも記事にできたらなと思います。