数ヶ月前、Digdag serverを導入した。
これまではタスクの依存関係だけ解決できればいいよね、というスタンスでDigdagを使っていた。
が、タスクのモニタリングやRetry、並列処理をスムーズにやりたい等が重なりDigdag serverを使うことになった。
ついでにCentosが6だったので7にアップグレードしている。
自分が躓いた点について書いてみた。
DigdagをDaemon化するためのSystemd設定
[Unit]
Description=digdag
[Service]
Type=simple
PIDFile=/run/digdag.pid
ExecStart=/bin/bash -l -c 'EMBULK_ROOT=/apps/ipros-embulk/current ~/bin/digdag server --max-task-threads 2 --config /home/app/.config/digdag/config -O /apps/ipros-embulk/shared/log -A /apps/ipros-embulk/shared/log'
User=app
Group=app
WorkingDirectory=/apps/ipros-embulk/current/
Restart=always
RestartSec=5
KillMode=process
TimeoutStopSec=1200
SyslogIdentifier=digdag
[Install]
WantedBy=multi-user.target
Systemd備考
- --max-task-threadsはEmbulkが大量に動いても困るので2に制限
- KillModeのデフォルトはcontrol-groupだが、これだと再起動した際に問答無用でDigdagとタスクを殺してしまう。
- Digdagはprocessをstopする際にタスクの終了を待つようになっている。
- SyslogIdentifierはjournald経由でSyslogに転送する際のident。
- Digdag自体のアップグレードをカジュアルにやりたいためroot権限では動作させず、ユーザー権限で動かしている。
@frsyuki 先生のご教授
KillMode=オプションが色々あるようです。あとTimeoutStopSecとかも関係している可能性も。
— Sadayuki Furuhashi (@frsyuki) 2017年11月7日
--params-file(-P)オプションをやめて環境変数を丸ごとinclude
Digdagはserver modeでもClient modeでも--params-file yamlとするとyamlの中をDigdag.envに展開してくれる。
以下が参考になる。
例えば実行方法は以下のような感じ。
- Client mode
digdag run --project workflow -P env.yml test.dig
- Server mode
digdag server -o workflow/.digdag -P env.yml
ただ、この場合毎回workflowをdeployする度にDigdagをrestartしなければならずバッチが常時起動しているようなワークロードでは致命的。
実行中のタスクがあれば再起動がタスク待ちになってしまうので、そう簡単に再起動は出来ない。
そこで全てのworkflowに以下のような記述をしている。
HOGE: test
_export:
!include : shared/env.dig
!include : shared/base.dig
+task1:
sh>: echo ${HOGE}
このように環境変数自体をincludeにしてしまえばdigdag push時に読み込まれるため再起動が不要。
ただし、~/.config/digdag/configを編集した場合は再起動が必要。
勿論env.digを環境(Stage)毎に変えてDeployすることを前提としている。
includeには以下の注意点がある。
- 親ディレクトリにあるdigはinclude出来ない。
- symlinkにしても親ディレクトリに実態があれば不可
- hardlinkにすれば当然いけた。
- 拡張子が.digでないとinclude出来ない。
database.type = postgresql について
~/.config/digdag/configに以下のように接続情報を書いておくと、Digdag serverを複数台で分散して動かせる。
要はHAできる。
database.type: postgresql
database.user: application
database.password: xxxxxxxxx
database.host: digdag01
database.port: 5432
database.database: embulk
database.maximumPoolSize: 32
サーバー間で設定を合わせる必要がある。
またローカルに一時ファイルを置くようなタスクの依存関係を取っている場合は当然不整合が起きる。
弊社はこのケースが多かったので、一時ファイル置き場にはNFSを噛まして対応した。
Digdagのconfigを書き換えた際には当然restartが必要。(digdag pushでは変更が反映されないので注意)
以下のようなワークフローの場合、task1がサーバーAでtask2がサーバーBで動くといった感じになる。
+task1:
sh>: echo ${HOGE}
+task2:
sh>: echo ${HOGE}
Postgresqlのコネクション数について
RDSのt2.microでpostgresqlを作成したがそれでも負荷的には全く問題ない。
が、接続数がDefaultでavailable CPU cores * 32
とかなり多めなので抑えるためにdatabase.maximumPoolSize
を32にした。
@frsyuki 先生曰く、コネクション数はかなり少なくしても問題はないとのこと。
トランザクションが入って以降、少なくしても問題ないと思うので、少なくしてみてください! デフォルト値変えてないだけです
— Sadayuki Furuhashi (@frsyuki) 2017年11月28日
--max-task-threadsを絞っている状況であれば、なおさら減らしても大丈夫です。schedulerに1本、task threadsとexecutor loop用にCPUコア数分程度のコネクションがあれば、
— Sadayuki Furuhashi (@frsyuki) 2017年11月28日
自分のケースでは主にEmbulkをシングルサーバで実行するのが目的なので--max-task-threadsで同時実行数を制限している。
分散実行は現状別の仕組みで実行しているが、これはDigdagに寄せていきたい。
Ruby operatorを使う際に嵌った点
まず、ディレクトリ階層が以下のようになっている場合。(というか自分のケース)
root/
workflow/
task1.dig
task2.dig
shared/
base.dig
lib/
environment.rb
Gemfile
script.rb
serverにpushする場合は
$ digdag push myproject --project workflow
とする。
これを実行すればディレクトリ配下のdigがまとめてpushされてDigdagのワークスペースに配置される
。
このDigdagのワークスペース
というのが少し曲者だった。
bundle execなどでRubyのscriptを実行しても、このワークスペースがcurrent directoryになってしまいfailしてしまう。
Gemfileがdigdag pushの対象外なため。
なので、以下のようにcdするか、Bundler.setup
をscript内で動かす必要がある。(自分は後者)
+task2:
sh>: |
cd /apps/root
bundle exec script.rb