Django

【Djangoで非同期処理】Celery×Djangoで作ったアプリをHerokuにデプロイするまでの備忘録

CeleryでDjangoアプリケーションにタスクキューを実装して、Herokuにデプロイした方法をまとめます。
Djangoアプリケーションにタスクキューを実装するのは今後も結構やるかなと思うので、他のアプリケーションでも使いやすい形で残そうと思います。

参考にしたのは、Celeryの公式様とか、CeleryをDjangoで動かすとか。
あとはHeroku公式のUsing Celery on Herokuあたりです。

ちなみにHerokuの公式ドキュメントに、HerokuはCeleryは完全にサポートしているよ!、と書かれてました。
頼もしい限りです。
やっぱり公式ドキュメントが充実していると開発がはかどりますね。

ドキュメントといえば、Redisのドキュメントもかなり充実してて助かりました。
django-redis documentation

ぜひ参考にしてみてください。

もくじ

  1. 環境について
  2. CeleryとRedisをインストールしよう
  3. ディレクトリ構成とCelery.py
  4. Setting.pyの編集
  5. Celeryの非同期処理を起動してみる
  6. Herokuにデプロイ
  7. まとめ
  8. 参考

環境について

まず、タスクキューを導入したいDjangoアプリケーションはすでにHerokuにデプロイできる前提で進めていきます。
Djangoのアプリケーションの作成方法とかデプロイ方法に関しては、もうちょいノウハウがたまったら記事化しようと思ってます。

もしDjangoによるアプリケーション開発もこれから勉強するよ、って方はUdemyの動画教材がおすすめです。
僕も、この動画で始めました。

キャンペーン中なら1200円くらいなのでおすすめ。
プログラミング初心者でも安心、Python/Django入門講座

さて、環境に関しては以下の通りです。

Python:3.6.8
Django:2.2.4
OS:WSL2のUbuntu18.04
デプロイ環境:Heroku

インストールされているパッケージのバージョンに関しては、とりあえずrequirements.txtをまんま貼っておきます。
Redisのバージョンは3.3.8、Celeryは4.3.0でした。
たぶん2019年8月時点での最新バージョン。

Herokuってわりと最新のモジュールだと動かないイメージあったんですが、Celeryは問題ありませんでした。
さすが完全なサポートと豪語するだけありますね。

amqp==2.5.1
autopep8==1.4.4
billiard==3.6.1.0
celery==4.3.0
certifi==2019.6.16
chardet==3.0.4
defusedxml==0.6.0
dj-database-url==0.5.0
Django==2.2.4
django-allauth==0.39.1
django-celery-results==1.1.2
django-heroku==0.3.1
django-redis==4.10.0
docutils==0.15.2
gunicorn==19.9.0
idna==2.8
importlib-metadata==0.19
kombu==4.6.4
oauthlib==3.1.0
psycopg2==2.8.3
pycodestyle==2.5.0
PySocks==1.7.0
python3-openid==3.1.0
pytz==2019.2
redis==3.3.8
requests==2.22.0
requests-oauthlib==1.2.0
six==1.12.0
sqlparse==0.3.0
tweepy==3.8.0
urllib3==1.25.3
vine==1.3.0
whitenoise==4.1.3
zipp==0.5.2

では、Celeryでタスクキューを実装したDjangoアプリケーションをHeorkuにデプロイする方法を順にまとめていきます。

CeleryとRedisをインストールしよう

この記事はあくまでDjangoにタスクキューを実装する手順の備忘録を目的としているので、CeleryやRedisについては詳しくは解説しません。

Celeryは、セロリです。

一言でいうとPythonのjobqueue処理のフレームワークです。
非同期処理とか、タイムスケジューリングとかいろいろできます。

Redisは、リモートディクショナリサーバーの略で、データベース、キャッシュ、メッセージブローカー、およびキューとして使用するための高速でオープンソースのメモリ内Key-Valueデータストア、だそうです。

とりあえずこの二つが、Djangoでタスクキューを実装するために必要になります。

まずはCeleryのインストールです。
これは普通にpipで入れてあげればOKです。

pip install celery

次はRedisですね。
今回はUbuntu上にインストールするので、aptでインストールしてあげるのですが、残念ながらデフォルトでは最新のRedisはインストールできません。

以下のコマンドで、最新のRedisのリポジトリを追加してからインストールしましょう。

sudo add-apt-repository ppa:chris-lea/redis-server
sudo apt update
sudo apt install redis-server

無事にインストールに成功したら、このコマンドで正しくインストールができたことを確認しましょう。

redis-cli --version
redis-server --version

Redisのインストールが確認できたら、最後にpipでRedisを動かすのに必要なモジュールをインストールします。

pip install django-redis-cache

これで必要なモジュールのインストールが完了しました。
次はローカルで動作させていきます。

ディレクトリ構成とCelery.py

公式ドキュメントのディレクトリツリーをパクって説明していきます。

以下のディレクトリツリーは、最低限のプロジェクトのツリーを表してます。
Celeryが便利だなと感じたのは、アプリケーション側のコードに特に修正を加える必要がなく、プロジェクト側だけで完結する点でした。

- proj/
  - manage.py
  - proj/
    - __init__.py
    - settings.py
    - celery.py
    - urls.py

まずはプロジェクトディレクトリの中にcelery.pyというファイルを作りましょう。
以下のコードを記述してください。

{{ own_projectname }}には自分のプロジェクトの名前を入れてあげてください。

# celery.py
import os
from celery import Celery

# set the default Django settings module for the 'celery' program.
settings = os.getenv(
    "DJANGO_SETTINGS_MODULE", "{{ own_projectname }}.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings)

app = Celery('{{ own_projectname }}')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks(['{{ own_projectname }}'])

Setting.pyの編集

次にsetting.pyを編集します。
まずはINSTALLED_APPSにdjango_celery_resultsを追加してあげましょう。

INSTALLED_APPS = [
    ・・・
    'django_celery_results',
]

追記が終わったらmigrateを忘れずに。

python manage.py migrate

これでdjango_celery_resultsのモデルが作成されて処理のログを確認できるようになります。

次は、Setting.pyの末尾にこちらを追記してください。
BROKER_URLとRESULT_BACKENDを指定してあげます。

ローカルで動かすときとHerokuにデプロイするときで記述が異なるので注意。

#ローカルのみ
CELERY_BROKER_URL = "redis://localhost:6379/1"
CELERY_RESULT_BACKEND = "django-db"

#Herokuにデプロイするときはこっち
#CELERY_BROKER_URL = os.environ.get("REDIS_URL")
#CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL")

Celeryの非同期処理を起動してみる

ここまでで最低限の設定は完了しました。
いよいよ実際に動かしてみましょう。

まずはRedisを起動。

sudo service redis-server start
sudo systemctl enable redis-server

次にCeleryを起動。

sudo systemctl daemon-reload
sudo service celery start
sudo systemctl enable celery

非同期で処理させたいジョブを指定。
※ここは既存のメソッドで大丈夫です。すでにあるアプリケーションをそのまま編集しましょう。

from {{ own_projectname }}.celery import app

#非同期処理させたいメソッド
@app.task
def job(a,b):
  return a + b

#delayを付けて関数を呼び出すと非同期処理がされる。
job.delay(1,2)

最後に起動コマンド。
runserverはしておく必要があります。

celery worker -A webapp.celery -l INFO

このコマンドが通れば、無事にworkerが起動され、指定した処理が非同期で実行されるはずです。
確認してみましょう。

Herokuにデプロイする

アプリケーションはローカルで正常に実行されている前提とします。

まずはHerokuにRadisアドオンを追加します。
無料枠でも使えるので安心。

heroku addons:create heroku-redis:hobby-dev -a {{ own_herokuapp }}

次に、前述の通りsetting.pyを書き換えましょう。

#ローカルのみ
#CELERY_BROKER_URL = "redis://localhost:6379/1"
#CELERY_RESULT_BACKEND = "django-db"

#Herokuにデプロイするときはこっち
CELERY_BROKER_URL = os.environ.get("REDIS_URL")
CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL")

ここまで出来たら、いつも通りデプロイしましょう。

git push heroku master
heroku ps:scale worker=1

デプロイに成功すれば完成です!
正しく動いてくれているかどうか、ログを確認してみてください。

お疲れ様でした。

まとめ

これで無事にDjangoにタスクキューを実装して、時間のかかる処理をバックで処理させて画面を更新できるようになりました。
非同期処理をできるようにしたら、自分のアプリケーションが一気にグレードアップしたように感じられて、非常に感動しました。

ぜひやってみてください。

今後の課題としては、Redisがちょいちょい落ちてエラーになるのを解消することです。

"Error 110 while writing to socket. Connection timed out."みたいなエラーがたまに返ってきます。
更新すると復活するんですが。

ちなみに、今回タスクキューを実装したのはこちらのWEBアプリケーションです。
よければ使ってみてください。

参考

Celeryの公式様
CeleryをDjangoで動かす
Using Celery on Heroku
django-redis documentation

COMMENT

メールアドレスが公開されることはありません。