OllamaとCeleryで非同期LLMタスクキューを構築する方法|複数ユーザーのリクエストをワーカーで並列処理してサーバー負荷を制御する手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > ローカルLLM > OllamaとCeleryで非同期LLMタスクキューを構築する方法|複数ユーザーのリクエストをワーカーで並列処理してサーバー負荷を制御する手順
「Ollamaのチームサーバーに複数ユーザーが同時にリクエストを送ると、後から来たリクエストが詰まってタイムアウトしてしまう」
「リクエストをキューに積んで順番に処理させたいが、OllamaのAPIにはキュー管理機能がない」
そんな悩みを抱える情報システム担当者やサーバー管理者は多い。この記事では、PythonのタスクキューフレームワークCeleryとRedisブローカーを組み合わせて、OllamaへのLLMリクエストを非同期タスクキューで管理する仕組みを構築する。OllamaのAPIコールをCeleryタスクとして定義し、ワーカーが並列で処理することで、複数ユーザーからの同時リクエストをサーバー過負荷なしにさばけるようになる。systemdでCeleryワーカーをサービスとして登録し、再起動後も自動復旧する本番構成まで一気に進める。

この記事のポイント

・Celery+Redisでollamaリクエストを非同期タスクとしてキュー処理できる
・`worker_concurrency`でGPU/RAM容量に合わせた同時処理数を制御する
・systemdの`After=ollama.service`で起動順序を保証して本番運用に対応する
・バッチ処理とリアルタイム処理をキュー名で分離して優先度制御できる


OllamaとCeleryで非同期LLMタスクキューを構築する方法|複数ユーザーのリクエストをワーカーで並列処理してサーバー負荷を制御する手順

「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜOllamaに非同期キューが必要なのか

OllamaのAPIは1リクエストずつ逐次処理するシンプルな設計だ。単一ユーザーが使う場面では問題ないが、チームサーバーとして運用すると典型的な問題が発生する。

AさんとBさんが同時にAPIを叩いた場合、Ollamaは先着順に1件ずつ処理する。Bさんのリクエストはブロックされて待機するが、クライアントツールによってはタイムアウトエラーで弾かれてしまう。大きなモデル(`llama3.3:70b-instruct-q4_0`等)は1リクエストの処理に数十秒~数分かかるため、チームで共有するほど詰まりが深刻になる。

さらに深刻なのは、夜間バッチ処理(社内文書の一括要約等)とリアルタイムチャットが混在するケースだ。バッチが大量のリクエストを一気に送ると、対話用のリクエストがすべて待機に回される。リアルタイム応答性が失われ、ユーザーの体感品質が大幅に悪化する。

Celeryはこの問題を解決するPythonの非同期タスクキューフレームワークだ。リクエストをタスクとしてキューに積み、ワーカーが設定した並列度で処理する。バッチ処理を低優先キューに入れてリアルタイム用キューを分離するといった制御も、Celeryの設定数行で実現できる。

ローカルLLMをチームで活用する背景については、「社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢」も参考にしてほしい。

前提環境と準備するもの

この手順を進める前に、以下の環境を用意しておく。

1. 前提条件の確認

- Ubuntu Server 22.04 または 24.04 LTS - OllamaがsystemdサービスとしてLAN内で稼働中(`127.0.0.1:11434`でリッスン) - Python 3.10以上がインストール済み - Redisが使えること(次の手順でインストールする) Ollamaのsystemdサービスへの登録がまだの場合は、先に「Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド」を参照してほしい。

まず現在の環境を確認する。

# Python バージョン確認 $ python3 --version Python 3.12.3 # Ollamaが稼働中か確認 $ systemctl status ollama | grep Active Active: active (running) since 2026-10-15 10:00:00 JST # APIへの疎通確認 $ curl -s http://127.0.0.1:11434/api/tags | head -c 100 {"models":[{"name":"llama3.3:70b-instruct-q4_0","modified_at":"2026-10-01T12:00:00Z"...

Ollamaのモデル一覧が返れば前提環境は整っている。

2. プロジェクトディレクトリの作成

$ sudo mkdir -p /opt/ollama-queue $ sudo chown $USER:$USER /opt/ollama-queue $ cd /opt/ollama-queue # Python仮想環境を作成 $ python3 -m venv venv $ source venv/bin/activate (venv)$ pip install --upgrade pip

`/opt/ollama-queue`はシステム全体から参照しやすい場所だ。後述のsystemdサービスもこのパスを使う。

CeleryとRedisブローカーをインストールする

Celeryはメッセージブローカーを介してタスクをやり取りする。本記事ではRedisをブローカーとして使う。設定がシンプルで、1台のサーバーに同居させやすいためだ。RabbitMQと比べて運用コストが低く、小規模チームには最適な選択になる。

1. Redisのインストールと起動

$ sudo apt update && sudo apt install -y redis-server $ sudo systemctl enable redis-server $ sudo systemctl start redis-server # Redisの疎通確認 $ redis-cli ping PONG # バージョン確認 $ redis-server --version Redis server v=7.0.15

`PONG`が返れば正常だ。RedisのデフォルトポートはTCP 6379番。`/etc/redis/redis.conf`のバインドアドレスは`127.0.0.1`のままにしておく。チームサーバーのLocalhost専用で十分であり、外部に開放する必要はない。

2. CeleryとPythonライブラリのインストール

(venv)$ pip install celery[redis] requests # インストール確認 (venv)$ celery --version celery v5.3.6 (emerald-rush) (venv)$ python -c "import redis; print(redis.__version__)" 5.0.1

`requests`はOllamaのREST APIを叩くために使う。LangChainやLlamaIndexとの統合を考えている場合は、それぞれのパッケージも合わせてインストールしておく。依存関係の整理には`pip freeze > requirements.txt`で管理するとよい。

Celeryタスクとしてollamaリクエストを定義する

Celeryの中心はタスク定義ファイルだ。`@app.task`デコレータを付けたPython関数がそのままキューに積めるタスクになる。

1. Celeryアプリケーションを定義する

`/opt/ollama-queue/tasks.py`を作成する。

$ cat > /opt/ollama-queue/tasks.py << 'EOF' import requests from celery import Celery # Redisをブローカー兼バックエンドに指定 app = Celery("ollama_queue", broker="redis://127.0.0.1:6379/0", backend="redis://127.0.0.1:6379/1") app.conf.update( task_serializer="json", result_expires=3600, # 結果を1時間保持 worker_concurrency=2, # 同時処理数(GPU/RAMに合わせて調整) task_acks_late=True, # ワーカー死亡時のタスク再投入を有効化 ) @app.task(bind=True, max_retries=3) def generate(self, model: str, prompt: str, options: dict = None): payload = { "model": model, "prompt": prompt, "stream": False, "options": options or {}, } try: resp = requests.post( "http://127.0.0.1:11434/api/generate", json=payload, timeout=300, ) resp.raise_for_status() return resp.json().get("response", "") except requests.RequestException as exc: raise self.retry(exc=exc, countdown=10) EOF echo "tasks.py created"

`worker_concurrency=2`はワーカーが同時に処理するタスク数だ。GPUメモリが16GB以上あれば2に、8GB程度なら1に設定するのが安全側の典型的な値になる。`task_acks_late=True`にしておくと、ワーカーが途中でクラッシュしたタスクを別のワーカーに再投入できる。

使用するモデルの選び方については「ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント」で詳しく解説しているので参照してほしい。

2. タスクを送信する呼び出し側コードを確認する

同ディレクトリに動作確認用のクライアントスクリプトを作成しておく。

$ cat > /opt/ollama-queue/submit_task.py << 'EOF' from tasks import generate # タスクを非同期で投入(即座にタスクIDを返す) result = generate.delay( model="llama3.3:70b-instruct-q4_0", prompt="LinuxのsystemdとSysVinitの違いを100字で説明してください", ) print(f"Task ID: {result.id}") # 結果を待機して取得(最大300秒) response = result.get(timeout=300) print(f"Response: {response}") EOF echo "submit_task.py created"

`generate.delay()`は即座にタスクIDを返す。実際の推論はワーカー側で行われる。`result.get()`で結果が届くまでブロック待機する。非同期で動かしたい場合は、タスクIDだけを記録してポーリングで後から取りに行く形にする。

Celeryワーカーを起動してタスクを処理させる

設定ファイルが完成したら、まずフォアグラウンドでワーカーを起動して実際に動くことを確認する。

1. ワーカーをフォアグラウンドで起動する

$ cd /opt/ollama-queue && source venv/bin/activate # ワーカーをフォアグラウンドで起動(Ctrl+Cで停止) (venv)$ celery -A tasks worker --loglevel=info --concurrency=2 [config] .> app: ollama_queue:0x7f1234567890 .> transport: redis://127.0.0.1:6379/0 .> results: redis://127.0.0.1:6379/1 .> concurrency: 2 (prefork) [tasks] . tasks.generate [2026-10-15 12:00:00,000: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0 [2026-10-15 12:00:00,100: INFO/MainProcess] celery@hostname ready.

`celery@hostname ready.`が表示されればワーカーは待機状態だ。

2. 別ターミナルからタスクを投入して確認する

# 別ターミナルで実行 $ cd /opt/ollama-queue && source venv/bin/activate (venv)$ python submit_task.py Task ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890 Response: systemdはLinuxカーネル起動後に最初に実行されるPID1のシステム初期化デーモンで...

ワーカー側のログに`Task tasks.generate succeeded in X.XXXs`と表示されれば正常だ。タスクIDはUUID形式で、WebアプリやAPIからキューに積む場合はこのIDを使って後から結果を取りに行く。

複数タスクを同時に投入する場合も`generate.delay()`を繰り返し呼ぶだけでよい。`worker_concurrency=2`なら最大2件が並列処理され、残りはキューで待機する。Ollamaのリクエスト処理能力についての詳細は「Ubuntu ServerでローカルLLMを構築する方法」のパフォーマンスセクションを参照してほしい。

systemdでCeleryワーカーをサービスとして登録する

コンソールでの動作確認が取れたら、systemdサービスとして登録する。サーバー再起動後も自動起動し、クラッシュ時も自動で再起動するようになる。

1. systemdユニットファイルを作成する

$ sudo tee /etc/systemd/system/ollama-celery.service << 'EOF' [Unit] Description=Celery Worker for Ollama Queue After=network.target redis-server.service ollama.service Requires=redis-server.service [Service] Type=forking User=ubuntu Group=ubuntu WorkingDirectory=/opt/ollama-queue ExecStart=/opt/ollama-queue/venv/bin/celery \ -A tasks worker \ --loglevel=info \ --concurrency=2 \ --logfile=/var/log/ollama-celery.log \ --pidfile=/var/run/ollama-celery.pid \ --detach ExecStop=/bin/kill -TERM $MAINPID Restart=on-failure RestartSec=10s [Install] WantedBy=multi-user.target EOF echo "service file created"

`After=redis-server.service ollama.service`で依存関係を明示することで、再起動時にOllamaとRedisが起動してからワーカーが立ち上がる順序を保証している。`User=ubuntu`は実行ユーザー名なので、サーバーの実際のユーザーに合わせて変更する。

2. ログファイルの権限設定とサービス有効化

$ sudo touch /var/log/ollama-celery.log $ sudo chown ubuntu:ubuntu /var/log/ollama-celery.log $ sudo systemctl daemon-reload $ sudo systemctl enable ollama-celery $ sudo systemctl start ollama-celery # ステータス確認 $ systemctl status ollama-celery | grep Active Active: active (running) since 2026-10-15 12:00:00 JST

`active (running)`と表示されれば本番用のsystemdサービスとして稼働している。

3. タスクキューの動作を本番環境でテストする

# Celeryワーカーに実際にpingを送って確認 $ cd /opt/ollama-queue && source venv/bin/activate (venv)$ celery -A tasks inspect ping -> celery@hostname: OK pong # キューの状態を確認 (venv)$ celery -A tasks inspect active_queues -> celery@hostname: OK * exchange=celery(direct) key=celery

`pong`が返れば本番サービスとしてのワーカーが正常に動いている。タスクの投入や結果取得は`submit_task.py`と同じ方法で行える。

よくあるトラブルと対処法

本番環境でのよくある詰まりポイントをまとめる。

ワーカーが起動するがタスクが処理されない

Redisへの接続失敗が多い。接続状態を確認する。

# Redisの起動確認 $ systemctl status redis-server | grep Active Active: active (running) # ポートのリッスン確認 $ ss -tlnp | grep 6379 LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",...)) # Celeryから接続確認 $ cd /opt/ollama-queue && source venv/bin/activate (venv)$ celery -A tasks inspect ping -> celery@hostname: OK pong

`pong`が返れば接続は正常だ。Redisが起動していない場合は`sudo systemctl start redis-server`で起動する。

generate.delay()実行時にImportErrorが出る

`PYTHONPATH`が設定されていないか、仮想環境が有効化されていない状態で実行しているケースが多い。

# 仮想環境を有効化してから実行する $ cd /opt/ollama-queue && source venv/bin/activate (venv)$ python -c "import tasks; print(tasks)"

インポートできることを確認してから`submit_task.py`を実行する。

リクエストが途中で切れる(長文生成時)

`tasks.py`の`timeout=300`(5分)が短い場合は600に延ばす。`llama3.3:70b-instruct-q4_0`のような大型モデルのCPU推論では5分超えが発生することがある。

# tasks.py の timeout を延長する # 変更前: timeout=300 # 変更後: timeout=600 $ sed -i 's/timeout=300/timeout=600/' /opt/ollama-queue/tasks.py $ sudo systemctl restart ollama-celery

複数タスクが1件ずつしか処理されない

`worker_concurrency`が1になっている。GPUメモリとの相談になるが、CPUのみの構成では`worker_concurrency=4`程度まで増やせる場合がある。Ollamaがメモリ不足でクラッシュする場合は逆に下げる。

# tasks.py の worker_concurrency を確認・変更 $ grep worker_concurrency /opt/ollama-queue/tasks.py worker_concurrency=2, # 変更後はサービスを再起動する $ sudo systemctl restart ollama-celery

systemdサービスが起動に失敗する

ユーザー名の不一致やPIDファイルの権限エラーが典型的な原因だ。

# エラーログを確認 $ sudo journalctl -u ollama-celery -n 30 --no-pager # 実行ユーザーを確認 $ whoami ubuntu # PIDファイルのディレクトリ権限を確認 $ ls -la /var/run/ | grep ollama

PIDファイルの書き込みに失敗している場合は、`/var/run/`ではなく`/tmp/`をpidfileのパスに変更するか、`/var/run/ollama-celery.pid`を事前に作成して権限を付与する。

まとめ

手順コマンド・ファイルポイント
Redisインストールsudo apt install -y redis-serverブローカー本体・127.0.0.1バインドで安全
Celeryインストールpip install celery[redis] requests仮想環境内でインストール
タスク定義/opt/ollama-queue/tasks.py@app.taskデコレータでollamaリクエストをキュー化
ワーカー確認起動celery -A tasks worker --loglevel=infoフォアグラウンドで動作確認してから本番へ
systemd登録/etc/systemd/system/ollama-celery.serviceAfter=ollama.serviceで起動順序を保証
サービス有効化sudo systemctl enable ollama-celery再起動後も自動起動・クラッシュ時に自動復旧
OllamaをチームサーバーとしてLinux本番運用するなら、Celeryによるキュー管理は必須の仕組みだ。同期APIの限界はCeleryで超えられる。バッチ処理とリアルタイム処理を分離したい場合は、`@app.task(queue="batch")`のようにキュー名を使い分けることで優先度制御まで実現できる。さらに発展させるなら、Flower(`pip install flower`)でCeleryのキュー状態をブラウザからリアルタイムに監視する構成もある。

ローカルLLMのチーム運用を2日間のハンズオンで体験する

Celeryによるキュー管理やsystemdの本番設定など、実機GPU環境でチームサーバー構成を手を動かしながら習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。

>> ローカルAIマスターセミナーの詳細を確認する
ローカルLLMの構築・運用に関する関連記事もあわせて参考にしてください。

・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。