Python Celery

提供: ArchWiki
2022年8月5日 (金) 14:24時点におけるKusanaginoturugi (トーク | 投稿記録)による版 (カテゴリを訂正)
ナビゲーションに移動 検索に移動

プロジェクトウェブサイト より:

Celery は分散されたメッセージ受け渡しに基づく非同期のタスクキュー・ジョブキューです。リアルタイム処理に焦点が当てられていますが、スケジューリングもサポートしています。タスクは非同期 (バックグラウンド) または同期的 (準備ができるまで待機) に実行できます。

インストール

python-celery パッケージをインストールしてください。他の python パッケージと同じように Python 3.x と互換性のあるパッケージが入手できます。Python 2.x と互換性のあるパッケージが必要な場合は python2-celery[リンク切れ: アーカイブ: aur-mirror] をインストールしてください。

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.0127.0.0.1 で置き換えると良いでしょう。

設定をシンプルにするために HOME=/var/lib/rabbitmq も追加すると良いでしょう。環境変数について詳しくは RabbitMQ のドキュメント を参照してください。

rabbitmq.service起動有効化してください。

ノート: rabbitmq-service は rabbitmq ユーザーとして実行され、ホームフォルダは /var/lib/rabbitmq の中に保存されます。フォルダとサブフォルダの所有者が rabbitmq ユーザーになっていることを確認してください。

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 となる場合、問題の解決方法が こちらの記事 に載っています。

ノート: su rabbitmq -c "erl" を実行することで erlang のプロンプトをエラーなく表示できます。

セキュリティ

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 など重要なデバイスにアクセスするのが難しくなります。

ヒント:
  • /srv/http/apps/celery は no-dev オプションを付けずにマウントしてください。
  • mknod のオプションについては man mknodls -l /dev/{null,urandom} を見てください。
# 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/
ノート: 64ビットのカーネルを使っている場合、lib64usr/lib64 から usr/lib へのシンボリックリンクを作成してください: cd $JAIL; ln -s usr/lib lib64cd $JAIL/usr; ln -s lib lib64 を実行。
# 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
ノート: linux-vdso.so をコピーしないでください: 本当のライブラリではなく /usr/lib には存在しません。64ビット環境の場合 ld-linux-x86-64.so/lib64 にあります。

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 にコピーしてください。

ヒント: python のパッケージ方法については packagingcelery project structure を参照してください。

プロジェクトが 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
(...)
ノート:
  • easy-install.pth には virtualenv の中にインストールした他のパッケージを記述します。your_project を指し示さないエントリを修正しないでください。
  • python setup.py develop を実行してパッケージをインストールすることもでき、その場合は /srv/http/apps/celery/lib/python3.5/site-packages/setuptools.pth を更新する必要があります。
/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 ユーザーに移行されます。

ノート:
  • 上記のテストを実行する前に RabbitMQ を起動しておく必要があります。
  • プロジェクトのブローカーの認証情報は RabbitMQ の設定と合わせておく必要があります。
  • celery/rabbitmq のトラフィックが通過できるように設定されているかファイアウォールを確認してください。

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"