Docker
, Python
, Django
, Celery
で1つのコンテナで定期実行を行う。
環境
- Docker version 19.03.8, build afacb8b
- Python 3.7.4
- Django==3.0.8
- celery==4.4.6
なお、docker-compose
を利用している。
コンテナを1つに
docker
は1機能1コンテナが基本だが、そうすると、コンテナ1つのサイズがかさんでしまう。
具体的にはコンテナ1つ1つにPython
ランタイムがあったりするため。(多分)
そのため1つのコンテナでsupervisordを使って定期実行を行うことにした。 本当は分けた方がいいと思うけどね。容量に余裕がないので仕方なし。
Django Appの構造
諸事情でフォルダ構造は以下のようになっている:
. # proj_rootの親フォルダで操作している。
├── # Dockerfileやsupervisord.confなどはここに置いている。
└── proj_root
├── proj_app
│ └── ...
└── app
├── celery # ここのフォルダにタスクを設置する。
└── ...
つまりDjangoアプリのルートフォルダの親フォルダでコンソール操作を行い、celery
はapp
の下で動かす。
定期実行するタスクを用意・動作させるまで
ここでは定期実行するタスクを用意するところまで記述する。
少し長いがここがわかってないと実装は無理だと思うので書いておく。
基本的には、celery
のDocs(First Steps with Celery — Celery 4.4.6 documentation)を参考にすればいい。
Brokerを決める
redis
でいいだろう。alpineなdockerイメージもある。なのでdocker-compose.yml
は、:
version: '3'
services:
djangoapp:
build: .
...
networks:
- ...
- celery_network
# For celery
redis:
image: redis:alpine
networks:
- celery_network
# dbなど
networks:
celery_network:
...
といった感じになる。redis
はデフォルトでポート番号6379
を使用する。
また、pip install redis
も必要なので注意。
celeryとredisのバンドルもあるようだ。pip install -U "celery[redis]"
これは未確認。
celeryをインストール
pip install celery
これだけ。これは開発環境用になるので、Dockerで使うときは何らかの方法でインストールする。この環境では開発環境(実際はpipenv
)のをコピーするようにしているので、開発環境でインストールすればいい。
ブローカーURLをDjangoの設定に追記
定義したブローカーのURLをDjangoのsettings
に追記しておく。Djangoの設定はcelery_appインスタンスから読み込むことができる。
# localのdockerでredisを動かす場合
CELERY_BROKER_URL = 'redis://localhost:6379/0'
# composeで動かす場合(サービス名がホスト名)
CELERY_BROKER_URL = 'redis://redis:6379/0'
URLの形式は、:
redis://:password@hostname:port/db_number
このようになっている。Using Redis — Celery 4.4.6 documentationより。
celery_appとタスクを定義
tasks.py
をproj_root/app/celery/
に配置する。内容は、:
from celery import Celery
celery_app = Celery('tasks')
# CELERY_がプレフィックスになっている設定を読み込む。
celery_app.config_from_object('django.conf:settings', namespace='CELERY')
@celery_app.task
def add(x, y):
return x + y
とりあえずFirst steps with Django — Celery 4.4.6 documentationのサンプルと上述のFirst Steps with Celery
のブレンドで。
DJANGO_SETTINGS_MODULE
などの環境変数の設定は別で行うので不要。開発環境ではVSCode
の.env
読み込みが使える。dockerではdocker-compose.yml
で指定できる。
@shared_task
も今回は使わない。複数appで同一のcelery_app
インスタンスを利用できる模様。
Djangoはセットアップする必要がある
タスク内でmodels.py
などを利用するときは、Django
のロードが終わってないよと怒られることがある(タイミングの問題かもしれない):
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
これを回避するには、
# tasks.py
import djagno
django.setup()
# タスク定義
...
のように、django.setup()
を事前に行っておけばいい。
後から考えれば、celery_app.config_from_object()
をmodels.py
のインポート前に行っていれば問題なかったかもしれない。
celeryワーカーを動作させる
$ celery -A app.celery.tasks worker --loglevel=info
をコンソールで行えばいい。
場合によっては、--workdir
オプションを指定する必要がある。(proj_root/
の親フォルダで操作している場合は、--workdir proj_root
が必要。)このようなエラーが出るときなど:
Error:
Unable to load celery application.
The module proj_app.settings was not found.
タスクの呼び出しはここでは行わないが、:
>>> from app.tasks import add
>>> add.delay(4, 4)
のように.delay
をコーラブルの前に挟むだけで非同期実行できる。
celery beatコマンドで定期実行を行う。
Periodic Tasks — Celery 4.4.6 documentationを参考に。
まずは、設定から。tasks.py
ファイルに記述もできるようだが、そうはしないで、
CELERY_BROKER_URL
と同じようにDjangoのsettings
に追記する。:
# crontab形式で指定できる
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
'add-every-5-minutes': {
'task': 'app.celery.tasks.add',
'schedule': crontab(minute='*/5'),
'args': (3, 4)
},
}
あとは、ワーカーと同じように起動しておけばいい。:
$ celery -A app.celery.tasks beat
ワーカーと同じく必要なら--workdir
オプションを使う。CELERY_BEAT_SCHEDULE
に従って定期実行してくれる。
長かったが、ここまでがcelery
を使った定期実行の定義・設定になる。
以降はここまでで設定したことをもとにdocker
の1コンテナで実行する方法を検討実装する。
走らせるコマンドを考える
以上のことから準備さえ整えれば、
$ celery -A app.celery.tasks worker --loglevel=info
$ celery -A app.celery.tasks beat
この2つのコマンドで定期実行は行えることがわかる。
あとはDjango
を走らせるためにgunicorn
も使うので、それ用のコマンドを合わせて、同時実行するコマンドは3つとなる。
supervisordを使う
Dockerの1つのコンテナで3つのコマンドを同時に実行するなら、supervisord
を使うのがいいだろう。Supervisor: A Process Control System — Supervisor 4.2.0 documentation
Django
自体もPythonなので、python:3-alpine
イメージを利用して、Dockerfileの中でpip install supervisor
できる。
supervisord.conf
は、echo_supervisord_conf
のものに追記していく:
[supervisord]
; フォアグラウンドで動作させる
nodaemon=true ; start in foreground if true; default false
...
[program:django_app]
command=gunicorn --bind :8000 --log-file - proj_app.wsgi:application
directory=proj_root/
...
[program:celery_worker]
command=celery -A app.celery.tasks worker --loglevel=info
directory=proj_root/
...
[program:celery_beat]
command=celery -A app.celery.tasks beat
directory=proj_root/
...
--workdir
のオプションはdirectory
で指定すればOK。フォルダ構造については、当記事の最初の方で。
Dockerfileを書く
関係のありそうなところだけ抜粋して書いておく。
FROM python:3-alpine
# ...
RUN apk update && \
# ...
# pipでsupervisorをインストール
pip install --no-cache-dir supervisor && \
# ...
# ... supervisord.confなどをコピー
# コピーしたconfファイルでsupervisordを起動するようにする
CMD ["supervisord", "-c", "./supervisord.conf"]
起動する
docker-compose up -d
などで起動すればOK。
django-celery-beatもある
celery
のデフォルトスケジューラを、django-celery-beat
モジュールのスケジューラに変更するとDjango
のadmin
サイトでタスクの実行状況を確認できる。参考: Periodic Tasks — Celery 4.4.6 documentation
確認する頻度はそこまで多くないので、これはとりあえず導入はしないでおく。
コンテナを分ける場合
おそらく、celeryのワーカー用のコンテナでもコードなどのリソースが必要になるので、Dockerのvolume
を作成し、そこにリソースを入れて、複数のコンテナで共有するのがいいと思う。
容量に余裕がないのでこの方法は試していない。
以上です。
広告