Django + v(irtual)env + Celery + Supervisor

まとめる時間をとりたくないのであっさりとメモっぽく。


# 環境

```
> lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 7.6 (wheezy)
Release:	7.6
Codename:	wheezy
```

別件でSupervisorをsidからソース取ってきた```3.0r1-1~bpo70+1```にしています。
Ubuntu 12.04でもそうですが、元々の3.0a8というのが相当古いらしいためです (2010年)。
Ubuntu 14.04だと3.0b2で、ちょっと新しい (2013年？) 

今なら 3.1.2 でしょうか。……リリース日、2014-09-07って(本稿執筆の)3日前じゃん

参考: http://supervisord.org/changes.html

# 何がしたいか

当初は以下のような感じで恐る恐るDjangoと戯れていました。

* Python 2.7 -> apt
* Django -> apt
* Celery -> apt
* django-celery -> apt

それでもいいと思うんですが、少しアグレッシブに

* Python 2.7 -> venv中でpip
* Django -> venv中でpip
* Supervisor -> apt
* Celery -> venv中でpip

とかしてみたかったのです。OS標準パッケージへの依存を減らしたい。

ここで Supervisor だけ pipではないのは /etc/init.d/supervisor とか欲しいからです。
pipはディストリビューションの流儀に沿ったOS側の設定ファイルを入れないです。
ディストリビューションの方、ありがとうございます。

開発環境の別のテストプロジェクトとか
(Apacheからリバースプロキシ的に起動する)tornadoのプロジェクトとかも
このaptにくっついたSupervisorで一括して管理する点では問題はなさそうなので、
このようにします。

で、要は「Supervisorを使ってvenv内のDjango経由でCeleryのワーカを常駐させる」必要があります。

もともとの(dj)celeryの設定は```/etc/default/celeryd```にあります。
あいにくこれはbash(```/etc/init.d/celeryd```)から呼ばれる事実上bashファイルなので、
Supervisorの設定に変換しなければなりません。

これを行う際、注意する必要があったポイントをさっくり書いとくので何かヒントになれば幸いです。

## その(環境)変数は誰のものか

CELERY_OPTSなどをSupervisorの設定に入れても意味がありません。
言い換えると、```/etc/init.d/celeryd```内の定数っぽいものには

* Supervisorにも渡す必要がある環境変数
* /etc/init.d/celeryd が食うだけの一時的な環境変数

の2種類がしれっと混じっています。区別はソースを見るしかありません。

## manage.py celeryd_multi は分解してmanage.py celery worker にわけてSupervisorの設定とする。

特に(Celeryの)ワーカが複数あったりする場合、
キューやらワーカやらの設定を```/etc/default/celeryd```に書くんですが、
celeryd_multiはSupervisordと相性はあんまりよくないようです。

djceleryのceleryd_multi実態はCeleryのcelery-multi
をDjangoの設定を受け継ぎつつ実行するものです (celeryd_multi.py 見るとそうなってる)。

Celeryのcelery-multiコマンドは「workerを起動する」です。

単純な"Django + Celery"構成でも、
ps等で見える残留プロセスに ".../manage.py celery-multi" といったものはありません。
celery-multiはデーモンじゃねぇ、ってことです。
代わりに".../manage.py celery worker" が複数デーモンとして生きているはずです。

という感じで、manage.py celeryd_multi をSupervisor上で動かそうとするのは、ダメそう。

参考:http://stackoverflow.com/questions/15558875/running-celeyd-multi-with-supervisor

## 設定例

```text:/etc/default/celery
ENABLED="true"
CELERYD_LOG_LEVEL="DEBUG"
CELERY_TIMEZONE='Asia/Tokyo'
CELERYD_NODES="manage build"
CELERYD_CHDIR="/opt/griflet/"

CELERYD_MULTI="$CELERYD_CHDIR/manage.py celeryd_multi"
CELERYCTL="$CELERYD_CHDIR/manage.py celeryctl"
# XXXX は数字にする
CELERYD_OPTS="--time-limit=XXXX --time-limit:build=XXXX -c 1 -Q:build build -Q manage"
CELERY_CONFIG_MODULE="myproject.celeryconfig"

CELERYD_LOG_FILE="/var/log/celery/%n.log"
CELERYD_PID_FILE="/var/run/celery/%n.pid"

CELERYD_USER="myproject"
CELERYD_GROUP="myproject"

export DJANGO_SETTINGS_MODULE="myproject.settings"
```

なら、だいたい以下のような感じ。ただし検証はしてません。

```text:conf.d/myproject.conf
[program:celery-myproject-manage]
command=/opt/myproject/venv/bin/python /opt/myprojcet/manage.py celery worker
  --loglevel=DEBUG
  -Q manage
  --logfile=/var/log/celery/manage.log
  --pidfile=/var/run/celery/manage.pid
  --hostname manage@hostname
  --concurrency 1
  --time-limit=XXXX
  --workdir=/opt/myproject/

user=myprojcet
directory=/home/myprojcet
numprocs=1
stdout_logfile=/var/log/celery/manage.stdout.log
stderr_logfile=/var/log/celery/manage.stderr.log
autostart=true
autorestart=true
startsecs=10
environment =
  DJANGO_SETTINGS_MODULE="myprojcet.settings",
  CELERY_TIMEZONE="Asia/Tokyo"


[program:celery-myprojcet-build]
command=/opt/myprojcet/venv/bin/python /opt/myprojcet/manage.py celery worker
  --loglevel=DEBUG
  -Q build
  --logfile=/var/log/celery/build.log
  --pidfile=/var/run/celery/build.pid
  --hostname build@hostname
  --concurrency 1
  --time-limit=XXXX
  --workdir=/opt/myprojcet/

user=myprojcet
directory=/home/myprojcet
numprocs=1
stdout_logfile=/var/log/celery/build.stdout.log
stderr_logfile=/var/log/celery/build.stderr.log
autostart=true
autorestart=true
startsecs=10
environment =
  DJANGO_SETTINGS_MODULE="myprojcet.settings",
  CELERY_TIMEZONE="Asia/Tokyo",
```

## Supervisord側で動いたら

この方法で動いたワーカプロセス環境変数にSUPERVISORなんちゃらというのが入るようです。
もしCeleryのみ構成とSupervisor付き構成で挙動を変えたい場合にはこれが役に立つかもしれません。
デバッグにもきっと使えます。

後はマニュアルを読めばこの記事より正しい記述があるはずです。

chkconfigやsysv-rc-conf等でCeleryを止めてSupervisorが起動する点は確認しときます。

＃ systemdな世界ならそれに従います。

以上なのです。

＃ コメント等いただければ、説明が足りないところを後で追加するやも。
＃ 間違っていたら毎度のごとくご連絡ください。

# 追記: GID周り (2014-09-10 17:12)

celeryd_multiをかました場合と上記の方法でgroupの扱いが変わって困ったのでそれも一言。
celeryd_multiで出来たCELERYD_GROUPによるGIDの指定がSupervisorですと出来ません。
単純にそのuserのGIDが再利用されます。


celery worker には --uidと--gidを指定する方法があるので実際これでプロセスのUIDとGIDは変更できます。
ただし、これを有効に機能させるにはまずSuperviserのuser指定をやめ (rootで起動させ) る必要がある上、
ログファイルの生成はworkerがそのオプションを食う「前」に行われるために
新たに作られるログのパーミッションがroot:rootになります。
RotatingFileHandlerなどを使っていると、あるとき突然WSGIから書き込めなくなって落ちたりします。
もしログファイルのパーミッション管理でも工夫があるなら注意です。

自分の場合だとそのユーザが壊れていてGID=nogroupだった状態でSupervisorに変更したら、
なぜか作られるファイルがことごとくnogroup groupに所属して意味がわからないことに。

これは全体的にSupervisorの話と言うよりはUnix的な話ですが、念の為。