Digdag serverを入れた際の躓きポイント(CentOS7対応)


数ヶ月前、Digdag serverを導入した。
これまではタスクの依存関係だけ解決できればいいよね、というスタンスでDigdagを使っていた。
が、タスクのモニタリングやRetry、並列処理をスムーズにやりたい等が重なりDigdag serverを使うことになった。

ついでにCentosが6だったので7にアップグレードしている。

自分が躓いた点について書いてみた。

## DigdagをDaemon化するためのSystemd設定
```ini:/usr/lib/systemd/system/digdag.service
[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 先生のご教授

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">KillMode=オプションが色々あるようです。あとTimeoutStopSecとかも関係している可能性も。</p>&mdash; Sadayuki Furuhashi (@frsyuki) <a href="https://twitter.com/frsyuki/status/927695462773145600?ref_src=twsrc%5Etfw">2017年11月7日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## --params-file(-P)オプションをやめて環境変数を丸ごとinclude

Digdagはserver modeでもClient modeでも--params-file yamlとするとyamlの中をDigdag.envに展開してくれる。
以下が参考になる。

* [Digdagで環境毎に設定値を変えたい時のTips \| iroilog\.net](https://iroilog.net/2017/09/digdag-params-tips/)

例えば実行方法は以下のような感じ。

* 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に以下のような記述をしている。


```yaml:env.dig
HOGE: test
```

```yaml:base.dig
_export:
  !include : shared/env.dig
```

```yaml:task.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 先生曰く、コネクション数はかなり少なくしても問題はないとのこと。


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">トランザクションが入って以降、少なくしても問題ないと思うので、少なくしてみてください！ デフォルト値変えてないだけです</p>&mdash; Sadayuki Furuhashi (@frsyuki) <a href="https://twitter.com/frsyuki/status/935387588671574016?ref_src=twsrc%5Etfw">2017年11月28日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">--max-task-threadsを絞っている状況であれば、なおさら減らしても大丈夫です。schedulerに1本、task threadsとexecutor loop用にCPUコア数分程度のコネクションがあれば、</p>&mdash; Sadayuki Furuhashi (@frsyuki) <a href="https://twitter.com/frsyuki/status/935388844295258113?ref_src=twsrc%5Etfw">2017年11月28日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


自分のケースでは主に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
```