Python Celery
プロジェクトウェブサイト より:
- Celery は分散されたメッセージ受け渡しに基づく非同期のタスクキュー・ジョブキューです。リアルタイム処理に焦点が当てられていますが、スケジューリングもサポートしています。タスクは非同期 (バックグラウンド) または同期的 (準備ができるまで待機) に実行できます。
目次
インストール
python-celery パッケージをインストールしてください。他の python パッケージと同じように Python 3.x と互換性のあるパッケージが入手できます。Python 2.x と互換性のあるパッケージが必要な場合は python2-celery をインストールしてください。
Celery のドキュメント より: "Celery はメッセージを送信・受信するための手段を必要とします"。選択肢のひとつとして RabbitMQ が存在し、公式リポジトリからインストールできます。
設定
Celery
設定ファイルを保存するためのディレクトリ /etc/celery/
を作成する必要があります。サンプル設定ファイルが Celery のドキュメント に記載されています。
celery@celery.service
を起動・有効化してください。
RabbitMQ
RabbitMQ は設定を /etc/rabbitmq/rabbitmq-env.conf
に保存します。
デフォルト設定:
NODENAME=rabbit@rakieta NODE_IP_ADDRESS=0.0.0.0 NODE_PORT=5672 LOG_BASE=/var/log/rabbitmq MNESIA_BASE=/var/lib/rabbitmq/mnesia
RabbitMQ は Unix ソケットをサポートしていないため 0.0.0.0
を 127.0.0.1
で置き換えると良いでしょう。
設定をシンプルにするために HOME=/var/lib/rabbitmq
も追加すると良いでしょう。環境変数について詳しくは RabbitMQ のドキュメント を参照してください。
rabbitmq.service
を起動・有効化してください。
RabbitMQ のドキュメント に従ってユーザーとバーチャルホストを追加:
$ cd /var/lib/rabbitmq $ su rabbitmq -c 'rabbitmqctl add_user myuser mypassword' $ su rabbitmq -c 'rabbitmqctl add_vhost myvhost' $ su rabbitmq -c 'rabbitmqctl set_user_tags myuser mytag' $ su rabbitmq -c 'rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*"'
詳しくは RabbitMQ の管理者ガイド を読んでください。
su rabbitmq -c "rabbitmqctl status"
を実行したときに badrpc,nodedown
となる場合、問題の解決方法が こちらの記事 に載っています。
セキュリティ
Celery ドキュメント のセキュリティセクションを読んでください。
サンプルタスク
Celery アプリケーション
Celery ドキュメント に従って python のサンプルタスクを作成:
test.py
from celery import Celery app = Celery('tasks', backend='amqp', broker='amqp://myuser:mypassword@localhost:5672/myvhost') @app.task def add(x, y): return x + y
amqp://myuser:mypassword@localhost:5672/myvhost
は RabbitMQ を設定したときに作成したユーザー・バーチャルホストと同じにしてください。
Celery によって使われるデフォルトのブローカーは RabbitMQ であるため backend='amqp'
パラメータは任意です。
テスト
test.py
のあるディレクトリで以下のコマンドを実行:
$ celery -A task worker --loglevel=info
そして (同一ディレクトリに) 以下のファイルを作成:
call.py
from test import add add.delay(4, 4)
実行:
$ python call.py
コンソールからワーカーの情報が出力されます:
Received task: task.add[f4aff99a-7477-44db-9f6e-7e0f9342cd4e] Task task.add[f4aff99a-7477-44db-9f6e-7e0f9342cd4e] succeeded in 0.0007182330009527504s: 8
Celery サービスのモジュールの準備
Celery ドキュメント に書かれていることとは少し異なる手順で説明しています。
test_task
モジュールを作成:
# mkdir /lib/python3.5/site-packages/test_task # touch /lib/python3.5/site-packages/test_task/__init__.py # touch /lib/python3.5/site-packages/test_task/test_task.py # touch /lib/python3.5/site-packages/test_task/celery.py
/lib/python3.5/site-packages/test_task/celery.py
from __future__ import absolute_import from celery import Celery app = Celery('tasks', backend='amqp', broker='amqp://myuser:mypassword@localhost:5672/myvhost') if __name__ == '__main__': app.start()
/lib/python3.5/site-packages/test_task/test_task.py
from __future__ import absolute_import from test_task.celery import app @app.task def add(x, y): return x + y
コンソールで python
を起動して以下のコマンドを実行できるはずです:
>>> from test_task import celery
/etc/celery/celery.conf
の中の以下の行を:
CELERY_APP="proj"
以下のように置き換えてください:
CELERY_APP="test_task"
celery@celery.service
を再起動してください。
タスクを定期的に実行
Celery Beat を使うことでタスクを定期実行することができます。基本的なセットアップは Celery のドキュメントページ で説明されています。
celery.py
の中で CELERYBEAT_SCHEDULE
を指定したい場合、Celery がタスクを認識できるように app.conf
プレフィックスを追加する必要があります。その後 Celery デーモンを起動するときに --beat --schedule=/var/lib/celery/celerybeat-schedule
パラメータを追加してください。さらに、/var/lib/celery
ディレクトリが存在している必要があり所有者は celery を実行するユーザーでなければなりません。
chroot で Celery を実行 (実験的)
chroot に celery をインストールすることでセキュリティを向上させることができます。セキュリティを高めるために、chroot には Celery アプリケーションを実行するのに必要なファイルだけを含めて、ファイルのパーミッションは出来る限り最小にします。例えば /usr/bin
などのディレクトリは所有者を root にして読み込み・書き込みができないようにします。
このセクションは Nginx#chroot でインストールを Celery 用に改変しています。
chroot ディレクトリとデバイスの作成
Arch にはデフォルトで http
ユーザーとグループが存在し、celery を動かすのに使うことができます。chroot は /srv/http/apps/celery
に作成します:
# mkdir -p /srv/http/apps/celery # cd /srv/http/apps/celery
Celery は /dev/null
と /dev/urandom
を必要とします。/dev/random
が存在しなくても Celery は起動時にクラッシュしません。chroot にインストールするには /dev/
ディレクトリを作成して mknod でデバイスを追加してください。/dev/
を全てマウントしないようにすることで、攻撃者は /dev/sda1
など重要なデバイスにアクセスするのが難しくなります。
# mkdir /srv/http/apps/celery/dev # mknod -m 0666 /srv/http/apps/celery/dev/null c 1 3 # mknod -m 0666 /srv/http/apps/celery/dev/random c 1 8 # mknod -m 0444 /srv/http/apps/celery/dev/urandom c 1 9
必要なディレクトリの作成
python-virtualenv を使って必要な python のファイルを持ち込むのが元のアイデアですが、virtualenv は半分ほどの作業しか行わず、chroot で Celery を実行するには他の依存関係を解消する必要があります。virtualenv で作成した環境に調整を加えます:
# virtualenv --always-copy /srv/http/apps/celery
必要なディレクトリを作成:
# cd /srv/http/apps/celery # mkdir {usr,dev,etc,run,tmp,var,proc} # mv {lib,bin,include} usr # ln -s usr/lib lib # ln -s usr/bin bin # ln -s usr/lib lib64 # ln -s usr/include include # cd usr/
# ln -s lib lib64
Celery は /proc/loadavg
を必要とします。/srv/http/apps/celery/tmp
と /srv/http/apps/celery/run
でバインドマウントしてください。攻撃者がメモリを占有できないように容量は制限します:
# touch /srv/http/apps/celery/proc/loadavg # mount --bind /proc/loadavg /srv/http/apps/celery/proc/loadavg # mount -t tmpfs none /srv/http/apps/celery/run -o 'noexec,size=1M' # mount -t tmpfs none /srv/http/apps/celery/tmp -o 'noexec,size=100M'
再起動後もマウントを維持するため、以下のエントリを /etc/fstab
に追加してください:
/etc/fstab
tmpfs /srv/http/apps/celery/run tmpfs rw,noexec,relatime,size=1024k 0 0 tmpfs /srv/http/apps/celery/tmp tmpfs rw,noexec,relatime,size=102400k 0 0 /proc/loadavg /srv/http/apps/celery/proc/loadavg none bind
celery のログフォルダを作成:
# mkdir -p /srv/http/apps/celery/var/log/celery # chown http:http /srv/http/apps/celery/var/log/celery
chroot の作成
python の依存パッケージをコピー:
# cp $(ldd /usr/bin/python | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') /srv/http/apps/celery/lib
chroot から実行するため、通常の virtualenv は機能しません。完全な python のライブラリフォルダをコピーする必要があります (site-packages は除く):
# mv /srv/http/apps/celery/lib/python3.5/site-packages /tmp # rm -r /srv/http/apps/celery/lib/python3.5/* # mv /tmp/site-packages /srv/http/apps/celery/lib/python3.5/ # # cp -r -p /usr/lib/python3.5 /tmp # rm -r /tmp/python3.5/site-packages # mv /tmp/python3.5/* /srv/http/apps/celery/lib/python3.5/
celery をインストール:
# source /srv/http/apps/celery/bin/activate # pip install celery
Celery は libssl
を必要とします:
# cp $(ldd /usr/lib/libssl.so.1.0.0 | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') /srv/http/apps/celery/usr/lib # cp /usr/lib/libssl.so* /srv/http/apps/celery/usr/lib
マルチスレッディングを使用する場合 Celery は libgcc_s
を必要とします:
# cp /usr/lib/libgcc_s* /srv/http/apps/celery/usr/lib
# cp $(ldd /usr/lib/libssl.so.1.0.0 | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') /srv/http/apps/celery/usr/lib # cp /usr/lib/libssl.so* /srv/http/apps/celery/usr/lib
Celery は /bin/getent
を必要とし、さらに libnss_files
も必要です:
# cp /bin/getent /srv/http/apps/celery/bin # cp /lib/libnss_files* /srv/http/apps/celery/lib
chroot 後に celery を起動する前に $HOME
を登録するのに /bin/env
が必要です:
# cp /bin/env /srv/http/apps/celery/bin
タスクモジュールをコピー (以下の例ではタスクモジュールの名前が test_task
で /lib/python3.5/site-packages/test_task
に保存されています):
# cp -r /lib/python3.5/site-packages/test_task lib/python3.5/site-packages
必要なライブラリやシステムファイルなどをコピー:
# cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf} /srv/http/apps/celery/etc
chroot 用に制限したユーザー・グループファイルを作成します。システムユーザーやグループが攻撃者から操られないようにするためです。
/srv/http/apps/celery/etc/group
http:x:33: nobody:x:99:
/srv/http/apps/celery/etc/passwd
http:x:33:33:http:/:/bin/false nobody:x:99:99:nobody:/:/bin/false
/srv/http/apps/celery/etc/shadow
http:x:14871:::::: nobody:x:14871::::::
/srv/http/apps/celery/etc/gshadow
http::: nobody:::
chroot のパッケージ
パッケージを chroot にコピーしてください。
プロジェクトが your_project
ディレクトリに存在し以下のような構造となっている場合:
. |-setup.py |-CHANGES.txt |-MANIFEST.in |-README.txt |-package_name |-__init__.py |-celery.py |-task_1.py |-task_2.py |-(...)
開発モードでパッケージをインストール:
# cd /srv/http/apps/celery/your_project # source ../bin/activate # pip install -e .
以下のファイルを作成してください:
/srv/http/apps/celery/lib/python3.5/site-packages/package_name.egg-link
/your_project .
/srv/http/apps/celery/lib/python3.5/site-packages/easy-install.pth
/your_project (...)
/srv/http/apps/celery/lib/python3.5/site-packages/setuptools.pth
/usr/lib/python3.5/site-packages
chroot のテスト
以下のコマンドを実行することで chroot が正しく設定されているか確認できます:
# /usr/bin/chroot --userspec=root:root /srv/http/apps/celery env -i HOME=/ /usr/bin/python -m celery worker -c 10 -A package_name --uid=33 --gid=33 --pidfile=/run/celery.pid --logfile=/var/log/celery/celery.log --loglevel="INFO"
Celery は root で起動しますが、http ユーザーに移行されます。
systemd の chroot ユニット
systemd ユニットを作成:
/etc/systemd/system/celery.service
[Unit] Description=Celery Nodes Daemon After=network.target [Service] Type=oneshot ExecStart=/usr/bin/chroot --userspec=root:root /srv/http/apps/celery /usr/bin/env -i HOME=/ /usr/bin/python -m celery multi start 2 -A package_name --uid=33 --gid=33 --pidfile:1=/run/celery1.pid --pidfile:2=/run/celery2.pid --logfile=/var/log/celery/celery.log ExecStop=/usr/bin/chroot --userspec=root:root /srv/http/apps/celery /usr/bin/python -m celery multi stopwait 2 --uid=33 --gid=33 --pidfile:1=/run/celery1.pid --pidfile:2=/run/celery2.pid --logfile=/var/log/celery/celery.log --loglevel="INFO" ExecReload=/usr/bin/chroot --userspec=root:root /srv/http/apps/celery /usr/bin/python -m celery multi restart 2 -A package_name --uid=33 --gid=33 --pidfile:1=/run/celery1.pid --pidfile:2=/run/celery2.pid --logfile=/var/log/celery/celery.log KillMode=control-group RemainAfterExit=yes [Install] WantedBy=multi-user.target
celery.service
を起動してください。
トラブルシューティング
#systemd の chroot ユニットが特に問題を報告しないのに celery サービスが機能しない場合、コンソールから chroot の中の celery を起動して詳しくログを確認することができます。例:
# /usr/bin/chroot --userspec=root:root /srv/http/apps/celery /usr/bin/env -i HOME=/ /usr/bin/python -m celery worker -A package_name --uid=33 --gid=33 --pidfile=/run/celery.pid --logfile=/var/log/celery/celery.log --loglevel="INFO"