鱒身(Masu_mi)のブログ

知った事をメモする場所。

Supervisordの練習(Airflow)

daemontoolsは設定が色々あり辛いのでSupervisord を覚えることにした。 練習としてAirflowをデーモン化した。

簡単に説明するとSupervisordUNIX likeなシステム上で複数のプロセスの起動を管理を提供するserver/clientシステムで 起動停止の他にイベント検知などもサポートしている

完成イメージはこんな感じ。

../../../_images/airflow-DAGs.png

Supervisordのインストール

$ yum install superviso
$ sudo systemctl start supervisord.service
$ sudo systemctl status supervisord.service
$ sudo systemctl enable supervisord.servicer

Supervisordの設定確認

systemdからSupervisordがどのように呼ばれるか確認する。

$ grep ExecStart /etc/systemd/system/multi-user.target.wants/supervisord.service
ExecStart=/usr/bin/supervisord -c /etc/supervisord.conf

Supervisordに渡される設定ファイルは/etc/supervisord.confだった。 中を読むと/etc/supervisord.d/*.iniを読み込んでいる事が想像できる。

$ grep -A 1 '^\[include\]' /etc/supervisord.conf
[include]
files = supervisord.d/*.ini

Airflowのインストールと設定

マニュアル通り入れてみる。 Airflowの設定ファイルは$AIRFLOW_HOME/airflow.cfgでコマンドは下の通り。 生成される設定ファイルについては今回は関与しない。

$ yum install python-devel
$ pip install airflow mysql
$ export AIRFLOW_HOME=/home/airflow
$ airflow initdb

AirflowをSupervisordで管理する

今回は自動リスタートによって1プロセス起動を維持させる。 vagrantユーザーで実行する。またAirflowはフォアグラウンドで実行されるのでstdout,stderrをログファイルに出力させる。 Supervisordはファイルサイズによるログローテートを提供しているみたいだけど今回は日付でローテートさせたいのでlogrotateコマンドを利用する。

SupervisordにAirflowを登録する

設定ファイルは下のようになった。 Airflowでは$AIRFLOW_HOMEが重要なのでenvironmentで設定しておく(airflow initdbの出力先に合わせる)。

[program:airflow]
command=/usr/bin/airflow webserver -p 8080
process_name=%(program_name)s
user=vagrant
numprocs=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopsignal=QUIT
stdout_logfile=/var/log/airflow/airflow-stdout.log
stderr_logfile=/var/log/airflow/airflow-stderr.log
environment=HOME="/home/vagrant",AIRFLOW_HOME="/home/vagrant/airflow"

[program:airflowscheduler]
command=/bin/airflow scheduler
process_name=%(program_name)s
user=vagrant
numprocs=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopsignal=QUIT
stdout_logfile=/var/log/airflow/airflow-scheduler-stdout.log
stderr_logfile=/var/log/airflow/airflow-scheduler-stderr.log
environment=HOME="/home/vagrant",AIRFLOW_HOME="/home/vagrant/airflow"

logrotateにローテートの方針を設定する

Supervisordではサイズベースのログローテーションしかサポートされてない。 日時ベースで行うためにlogrotateコマンドを利用する。

日毎(daily)のローテートでファイル名の末尾に日付を含め(dateext)、過去ログが62ファイル(rotate)以上ある場合はアラートを出し 圧縮(compress)しローテートと圧縮のタイミングを分ける(delaycompress)様に設定してみた。

logrotateによってログが移動されても書き込みプロセスのファイルディスクリプタの向き先は同一ファイルに向いていて移動先ファイルに書き込まれ続ける。 そのためログを出力しているプロセスに開き直してもらうためにローテーとしたことを伝える必要がある。

前述の様にAirflow は標準出力・標準エラー出力に出力していてログファイルに向けるのはSupervisordのためローテートを伝えるべきプロセスはSupervisordになる。 SupervisordSIGUSR2を投げるとログファイルを開き直すので以下の様にpostrotateディレクティブを使えばいい。 またdelaycompressで圧縮するタイミングを分けるのはローテートした直後はログ出力元プロセスがファイルディスクリプタを握っていてファイルが壊れる可能性があるためである。

/var/log/airflow/airflow-*.log {
  daily
  dateext
  rotate 62
  compress
  delaycompress
  postrotate
    /bin/kill -SIGUSR2 `cat /var/run/supervisord.pid` > /dev/null 2>/dev/null || true
  endscript
}

下みたいにローテート実行すると実行されていることが確認できる。

$ cat /var/lib/logrotate.status | grep airflow
"/var/log/airflow/airflow-stderr.log" 2017-4-8-15:21:47
"/var/log/airflow/airflow-scheduler-stdout.log" 2017-4-8-15:21:47
"/var/log/airflow/airflow-scheduler-stderr.log" 2017-4-8-15:21:47
"/var/log/airflow/airflow-stdout.log" 2017-4-8-15:21:47

$ sudo /usr/sbin/logrotate -v /etc/logrotate.d/airflow

$ ls /var/log/airflow/airflow-s*
/var/log/airflow/airflow-scheduler-stderr.log           /var/log/airflow/airflow-scheduler-stdout.log-20170408  /var/log/airflow/airflow-stdout.log
/var/log/airflow/airflow-scheduler-stderr.log-20170408  /var/log/airflow/airflow-stderr.log                     /var/log/airflow/airflow-stdout.log-20170408
/var/log/airflow/airflow-scheduler-stdout.log           /var/log/airflow/airflow-stderr.log-20170408

ローテートが実現できたものの残念な事が2つある。

  • Supervisord管理下のログファイルのディスクリプタ全てでclose/openされてしまうので少しシステムコールが無駄
  • logrotateによるローテートでは厳密に出力をコントロールできない

logrotateを定期実行する

logrotateを定期的に実行しないとログローテートされない。 crontab, anacronなどに登録されているか確認する。自分の環境ではanacronに登録されていた。

$ sudo cat /etc/anacrontab | grep daily
1       5       cron.daily              nice run-parts /etc/cron.daily
$ ls /etc/cron.daily/
logrotate  man-db.cron

サイズベースのログローテート

今回とは異なりログファイルをサイズベースでローテートするのはSupervisordがサポートしている。 以下の様に設定すれば良い(50MBごとにローテーション、最大バックアップ数:10)。

[program:x]
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=10