CentOS7 に digdag をインストールする

# 概要
CentOS 7 の環境に digdag をインストールする方法をまとめていきます。

# 前提
* CentOS 7 がインストールされたサーバが手元にあります
* sudo コマンドで root 権限のコマンド実行ができます
* SELinux が無効化されています
* PostgreSQL は同一サーバ上で稼働させます
* digdag に登録されたワークフローは bigdata ユーザ権限で実行します

# インストール手順
## bigdata ユーザの作成

```bash
sudo groupadd bigdata
sudo useradd -m -g bigdata -s /bin/bash bigdata
sudo passwd bigdata
```

## TD CLI のインストールと設定
※TreasureData を使用しない場合には不要です。
### インストール

```bash
curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh
```
※sudo 権限に制限がかかっている、具体的には sudo sh が実行制限されている場合には上記方法ではインストールできないので、一度スクリプトをローカルに持ってきてから、修正して実行するか、root になって実行するかしてください。

### 設定
```bash
sudo mkdir /home/bigdata/.td
sudo vi /home/bigdata/.td/td.conf
```

```/home/bigdata/.td/td.conf
[account]
  user = USERNAME
  apikey = APIKEY
  endpoint = https://api.treasuredata.com
```
USERNAME, APIKEY には適切な値を設定します。

```bash
sudo chown -R bigdata:bigdata /home/bigdata/.td
```
念のため td コマンドが正常に実行できることを確認しておきます。

```bash
td table:list データベース名
```
なお、これはローカルモードで利用する場合に必要となる設定であり、サーバモードで動作させる場合には、後述する「secret 機能の利用」を参照して下さい。

## jdk のインストール
```bash
sudo yum install java-1.8.0-openjdk
```

## digdag の入手と配置
```bash
mkdir ~/work
curl -o ~/work/digdag --create-dirs -L "https://dl.digdag.io/digdag-latest"
chmod +x ~/work/digdag
work/digdag --version
sudo cp ~/work/digdag /usr/local/bin/digdag_0.9.37
sudo ln -s /usr/local/bin/digdag_0.9.37 /usr/local/bin/digdag
```
4行目の version 確認で 0.9.37 と表示されたので、_0.9.37 付きで配置します。このあたりは好みの問題なので、直接 /usr/local/bin/digdag にコピーしてもよいです。

## PostgreSQL のインストール
### パッケージインストール
※INSERT ON CONFLICT が内部で使用されているので、PostgreSQL 9.5 以降のバージョンをインストールして下さい。

```bash
sudo yum install -y https://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/pgdg-redhat96-9.6-3.noarch.rpm
sudo yum install -y postgresql96-server postgresql96-contrib
```

### 設定
#### DB の初期化
```bash
/usr/pgsql-9.6/bin/postgresql96-setup initdb
```

#### 管理者パスワードの設定
```bash
sudo systemctl start postgresql-9.6
su - postgres
psql
```
※su するために postgres ユーザのパスワードが適宜設定されている必要があります。

``` sql
alter user postgres with password 'PASSWORD';
\q
```
PASSWORD には適当なパスワードを設定して下さい。

```bash
exit
```
postgres ユーザから自身に戻ります。

#### 認証方式の変更
```bash
sudo vi /var/lib/pgsql/9.6/data/pg_hba.conf
```

``` /var/lib/pgsql/9.6/data/pg_hba.conf（抜粋）
# "local" is for Unix domain socket connections only
local   all             all                                     md5
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
```
該当する箇所を全て md5 (パスワード認証) に変更します。

```bash
sudo systemctl restart postgresql-9.6
```

#### digdag 用ユーザ、DB作成
管理者でログインして digdag ユーザを作成します。

```bash
psql -U postgres
```

```sql
CREATE ROLE digdag WITH PASSWORD 'PASSWORD' NOSUPERUSER NOCREATEDB NOCREATEROLE LOGIN;
CREATE DATABASE digdag_db WITH OWNER digdag;
```
PASSWORD は適当なものを設定して下さい。
digdag_db に対して拡張機能 uuid-ossp を利用可能状態にします。

```sql
\c digdag_db;
CREATE EXTENSION "uuid-ossp";
\q
```

拡張機能が利用可能であることを確認しておきます。

```bash
psql -U digdag -d digdag_db
```
```sql
select installed_version from pg_catalog.pg_available_extensions where name = 'uuid-ossp';
\q
```
以下のような結果が返ってくればOKです。

```
 installed_version
-------------------
 1.1
(1 行)
```

## digdag サーバの設定
### 設定ファイルの作成
```bash
sudo mkdir /etc/digdag
sudo vi /etc/digdag/digdag.properties
```

``` /etc/digdag/digdag.properties
database.type = postgresql
database.host = localhost
database.port = 5432
database.user = digdag
database.password = PASSWORD
database.database = digdag_db
database.maximumPoolSize = 20
```
PASSWORD には設定したパスワードを記入します。最後の maximumPoolSize は digdag executor が postgreSQL サーバとの接続をプールする数なので、postgreSQL サーバの同時接続数÷ executor の数で設定するとよいです。このオプションが利用できるようになったのは v0.9.8 以降となります。

```bash
sudo vi /etc/sysconfig/digdag-server
```

```/etc/sysconfig/digdag-server
# configuration file for PostgreSQL
CONFIG_FILE=/etc/digdag/digdag.properties

# port number for web service (port をデフォルトから変えると push できない)
PORT=65432

# binding address (default: 127.0.0.1)
BINDING=0.0.0.0

# log file
ACCESS_LOG=/var/log/digdag-server/access
TASK_LOG=/var/log/digdag-server/task
```
待ち受けポートは 65432 から変更可能ですが、変えると digdag push でジョブの登録ができないようなのでデフォルトポートで待ち受けるようにします。

### Unit ファイルの作成
```bash
sudo vi /usr/lib/systemd/system/digdag-server.service
```

``` /usr/lib/systemd/system/digdag-server.service
[Unit]
Description=Digdag server daemon
After=network.target postgresql.service

[Service]
User=bigdata
EnvironmentFile=/etc/sysconfig/digdag-server
ExecStart=/usr/bin/java -jar /usr/local/bin/digdag server -n ${PORT} -b ${BINDING} -O ${TASK_LOG} -A ${ACCESS_LOG} -c ${CONFIG_FILE}
ExecStop=/bin/kill -s SIGTERM ${MAINPID}

[Install]
WantedBy=multi-user.target
```

```bash
systemctl daemon-reload
```

### ログディレクトリの作成
```bash
sudo mkdir -p /var/log/digdag-server/access /var/log/digdag-server/task
sudo chown -R bigdata:bigdata /var/log/digdag-server
```

### サービス起動
```bash
sudo systemctl start digdag-server
```

## nginx のインストールと設定
### インストール
```bash
sudo vi /etc/yum.repos.d/nginx.repo
```

``` /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
```

```bash
sudo yum install nginx
```

### 設定
サイトファイルを conf.d 以下に配置すればいいのかもしれませんが、Debian の apache パッケージのような感じに変更していきます。
```bash
cd /etc/nginx/
sudo mv conf.d/default.conf conf.d/default.conf.dist
sudo mv nginx.conf nginx.conf.dist
sudo vi global.conf
```

``` /etc/nginx/global.conf
user nginx;

pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;

worker_processes auto;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}
```

```bash
sudo vi nginx.conf
```

``` /etc/nginx/nginx.conf
include /etc/nginx/global.conf;

http {
    server_tokens off;
    access_log /var/log/nginx/access.log combined;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
```

```bash
sudo mkdir sites-available sites-enabled
sudo vi sites-available/digdag-server
```

```/etc/nginx/sites-available/digdag-server
upstream digdag_server {
  server localhost:65432;
}

server {
  listen 80 default_server;

  location / {
    proxy_pass http://digdag_server;
  }
}
```

```bash
cd sites-enabled
sudo ln -s ../sites-available/digdag-server
```

### サービス起動
```bash
sudo systemctl start nginx
```

これで http://IP_ADDRESS/ にアクセスすれば digdag の画面が出てくるはずです。ただし IP_ADDRESS はこのサーバのIPアドレスになります。

# サーバ/ワーカの別立て
digdag をタスクをキューイングするだけのサーバと、キュー内のタスクを実行するワーカとで別立てする場合には以下のように設定変更する必要があります。

## 変更内容
### /etc/sysconfig/digdag-server（サーバ側）
``` /etc/sysconfig/digdag-server
# server mode
MODE="--disable-local-agent"
```
を追記します。
### /etc/sysconfig/digdag-server（ワーカ側）
```/etc/sysconfig/digdag-server
# server mode
MODE="--disable-executor-loop"
```
を追記します。

### /usr/lib/systemd/system/digdag-server.service
サーバ、ワーカ双方で以下のように ExecStart を修正します。

```/usr/lib/systemd/system/digdag-server.service
ExecStart=/usr/bin/java -jar /usr/local/bin/digdag server -n ${PORT} -b ${BINDING} -O ${TASK_LOG} -A ${ACCESS_LOG} -c ${CONFIG_FILE} ${MODE}
```

## ログの集約
web UI をサーバ側だけで提供する場合にはワーカにある標準出力および標準エラー出力ファイルをサーバ側に転送する必要があります。そのために rsync/lsync を導入します。

### サーバ側
```bash
sudo yum install rsync
sudo vi /etc/rsyncd.conf
```

```/etc/rsyncd.conf
[digdag_task]
        comment  = digdag-tasklog
        hosts allow = ワーカのIPアドレスリスト
        hosts deny = *
        path = /var/log/digdag-server/task
        use chroot = no
        read only = false
        list = true
```

```bash
sudo systemctl start rsyncd
sudo systemctl enable rsyncd
```

### ワーカ側
```bash
sudo yum install lsyncd
sudo vi /etc/lsyncd.conf
```

```/etc/lsyncd.conf
settings {
        logfile     = "/var/log/lsyncd/lsyncd.log",
        pidfile     = "/var/log/lsyncd/lsyncd.pid",
        statusFile  = "/var/log/lsyncd/lsyncd.status",
        statusInterval = 1,
        maxProcesses = 2,
}

sync {
        default.rsync,
        source="/var/log/digdag-server/task/",
        target="bigdata@サーバ側IP::digdag_task",
        delete = "false",
        rsync = {
                rsh = "/usr/bin/ssh -i /home/bigdata/.ssh/id_rsa -p 22",
        },
}
```
ワーカからサーバに対して bigdata ユーザが ssh ログインできるように予め設定しておきます。また複数のワーカが存在する場合には sync ブロックの delete を false にしておかないと完全に同期されませんので注意が必要です。

```bash
sudo vi /etc/sysctl.conf
```

```/etc/sysctl.conf
fs.inotify.max_user_watches = 819200
```
lsync が監視するファイルの数の上限値を増やしておきます。デフォルト値が小さいので、運用開始後いずれ sync されなくなってしまいます。その場合には /var/log/lsyncd/lsyncd.log に以下のようなメッセージが出ているはずです。

``` /var/log/lsyncd/lsyncd.log
Wed Aug 15 10:37:20 2018 Error: Terminating since out of inotify watches.
Consider increasing /proc/sys/fs/inotify/max_user_watches
```

```bash
sudo sysctl -p
sudo systemctl start lsyncd
sudo systemctl enable lsyncd
```

# 注意点
## タスクステータスログについて
digdag サーバは -O オプションなしで起動するとタスクステータスのログが出力されません。そのため、digdag log コマンドでの確認もできません。digdag log の結果を利用している管理画面上の Logs の部分も当然何も記載されません。

## secret 機能を利用するための設定
```bash
sudo vi /etc/digdag/digdag.properties
```
以下を追記します。

``` /etc/digdag/digdag.properties
digdag.secret-access-policy-file = /etc/digdag/secret-access-policy.yaml
digdag.secret-encryption-key = 暗号化シークレットキー
```
暗号化シークレットキーは以下のようにして作ります。

```bash
echo -n シークレットキー | base64
```
```bash
sudo vi /etc/digdag/secret-access-policy.yaml
```

```/etc/digdag/secret-access-policy.yaml
operators:
  mail:
    secrets:
      - mail.*
  pg:
    secrets:
      - pg.*
  s3_wait:
    secrets:
      - aws.*
  td:
    secrets:
      - td.*
  td_load:
    secrets:
      - td.*
  td_for_each:
    secrets:
      - td.*
  td_run:
    secrets:
      - td.*
  td_ddl:
    secrets:
      - td.*
  td_partial_delete:
    secrets:
      - td.*
  td_table_export:
    secrets:
      - td.*
      - aws.*
  td_wait:
    secrets:
      - td.*
  td_wait_table:
    secrets:
      - td.*
```
設定が完了したらアプリケーションの再起動をします。

```bash
sudo systemctl restart digdag-server
```

## Treasure Data の API KEY の登録
td オペレータを利用可能にするために API KEY を登録します。注意すべき点は、digdag に登録するプロジェクトごとに API KEY を登録する必要があるという点です。

登録方法は以下の通りです。

```bash
digdag secrets --project プロジェクト名 --set td.apikey=APIキー
```

## pyenv 環境下にある Python の利用
[マルチユーザ環境のための pyenv](https://qiita.com/takkeybook/items/4640799385e0fc4be915) で紹介した環境設定をして Python 3 のプログラムを実行させたい場合、以下を設定する必要があります。

```bash
sudo vi /etc/sysconfig/digdag-server
```
以下を追記します。

``` /etc/sysconfig/digdag-server
# Python 用環境変数
PYENV_VERSION_FILE=/home/bigdata/.pyenv/version
PYENV_ROOT=/usr/local/pyenv
PATH=/home/bigdata/.pyenv/shims:/usr/local/pyenv/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
PYENV_SHELL=bash
```