前回はThinkCentreをWorkerとして動かすところまでやった。
Redisのキューを監視して、タスクを処理して、結果をPostgreSQLに保存する。
最小構成は動いた。
次のステップは決まっていた。
「RTX 3070 TiをGPU Workerとして追加する」
Windows機をWorkerにする
手持ちのRTX 3070 Ti搭載PCはWindows 11だ。
ThinkCentreはUbuntu Serverで動いているので、同じように使えるかと言うと少し事情が違う。
選択肢は3つあった。
- WSL2(Ubuntu)で動かす
- Windows上でネイティブに動かす
- Hyper-VでUbuntu VMを立てる
GPUを最大限活かしたい。そうなると、Hyper-V VMはGPUパススルーの設定が面倒だ。Windowsネイティブはサービス化が煩雑になる。
WSL2が現実的な選択だった。
WSL2のインストール
まずWSL2が入っていなかったので、PowerShellで1行叩く。
wsl --install
再起動後にUbuntuの初期設定が走る。
ユーザー名はThinkCentreと揃えて sagawa にした。パスが共通化されて後が楽になる。
GPUが見えるか確認する
WSL2からGPUが使えるかどうかは自明ではない。
Ubuntuを起動して nvidia-smi を叩く。
| NVIDIA GeForce RTX 3070 Ti On |
| CUDA Version: 13.2 |
問題なく見えた。
WSL2はWindowsのNVIDIAドライバをそのまま使う設計になっている。ドライバが新しければ基本的に動く。
Ollamaのインストール
インストールスクリプトを叩く。
curl -fsSL https://ollama.com/install.sh | sh
ここで一度止まった。
ERROR: This version requires zstd for extraction.
zstd が入っていなかった。先にインストールしてから再実行で通った。
モデルの選定
ThinkCentreには1Bクラスの軽量モデルしか入れていない。
RTX 3070 TiはVRAM 8GB。せっかくGPUがあるなら、もう少し大きいモデルを入れたい。
入れたのはこの3つ。
gemma3:12b(8.1GB)qwen2.5:7b(4.7GB)llama3.2:3b(2.0GB)
ThinkCentreの1Bモデルと役割を分けることで、Coordinatorがタスクの重さで振り分けられる構成に近づく。
WSL2特有の問題
ThinkCentreはUbuntu Serverなので、OllamaをLAN公開するだけで済んだ。
WSL2は少し違う。
WSL2内のIPはLANから直接見えない。WindowsがNAT的に動いているため、外からアクセスするにはポートフォワーディングが必要だ。
PowerShellで設定する。
netsh interface portproxy add v4tov4 listenport=11434 listenaddress=0.0.0.0 connectport=11434 connectaddress=172.28.138.1
172.28.138.1 はWSL2のIP。ここにWindowsの11434番ポートへのアクセスを転送する。
さらにWindowsファイアウォールで11434を開放する。
netsh advfirewall firewall add rule name="Ollama WSL2" dir=in action=allow protocol=TCP localport=11434
AI-CoreからcurlでモデルのJSONが返ってきたとき、ようやく繋がった実感があった。
PostgreSQLの接続許可も必要だった
worker_gpu.pyを起動してみると、推論は走るがDBに保存できない。
FATAL: no pg_hba.conf entry for host "192.168.0.200"
ThinkCentreのIPはpg_hba.confに追加済みだったが、RTX 3070 Ti PCのIPは未登録だった。
AI-Coreのpg_hba.confに1行追加して再起動。これで通った。
「Workerを増やすたびにこの作業が発生する」
もう少し自動化できる余地がある。今後の課題だ。
動いた
設定が揃ったところで比較タスクを投げる。
{
"prompt": "日本の首都はどこですか?",
"models": ["gemma3:12b", "qwen2.5:7b", "llama3.2:3b"]
}
Workerのログに結果が流れた。
[compare] done model=gemma3:12b 45087ms
[compare] done model=qwen2.5:7b 16769ms
[compare] done model=llama3.2:3b 3186ms
APIで確認する。
{
"progress": "3/3",
"results": [
{ "model": "gemma3:12b", "response": "日本の首都は東京都です。", "duration_ms": 45087 },
{ "model": "qwen2.5:7b", "response": "日本の首都は東京です。", "duration_ms": 16769 },
{ "model": "llama3.2:3b", "response": "東京です。", "duration_ms": 3186 }
]
}
progress: 3/3。全モデル正答。
llama3.2:3bの3秒に対してgemma3:12bは45秒。モデルサイズと速度のトレードオフが数字で見えた。
Worker自己登録の仕組みを作った
ここまでで2台のWorkerが動いている。
ただ、Coordinatorはどのマシンが今動いているかを把握していない。
「Coordinatorが有効なWorkerを知っている」
状態にしたかった。
設計はシンプルにした。
Worker起動
↓
サニティチェック(各モデルに簡単な質問を投げる)
↓
正常応答 → Coordinatorの /worker/register に登録
失敗 → そのモデルは除外
↓
Redisキュー監視へ
サニティチェックの質問は「1+1は?」。厳密な正答チェックはしない。何かしら返ってくればOKとした。
登録情報はRedisにTTL付きで保存する。TTLは5分。Workerが落ちれば自動的に失効する。
登録結果の確認
2台ともWorkerを再起動してから /workers を叩く。
{
"workers": [
{
"worker_id": "moon",
"host": "192.168.0.4",
"models": ["hf.co/LiquidAI/LFM2.5-1.2B-JP-GGUF:Q4_K_M", "gemma3:1b"]
},
{
"worker_id": "rtx3070ti",
"host": "192.168.0.200",
"models": ["gemma3:12b", "qwen2.5:7b", "llama3.2:3b", "gemma3:1b", "hf.co/LiquidAI/LFM2.5-1.2B-JP-GGUF:Q4_K_M"]
}
]
}
2台が自分のモデル情報をCoordinatorに伝えている。
構想段階で描いていた、
「Workerが自律的に参加する」
形に少し近づいた気がした。
現在の構成
ThinkPad P50(192.168.0.2)
└─ Hyper-V
└─ AI-Core VM(192.168.0.40)
├─ FastAPI Coordinator :8000
├─ Redis :6379
└─ PostgreSQL :5432
ThinkCentre moon(192.168.0.4)
├─ Ollama
│ ├─ LFM2.5-1.2B-JP-GGUF:Q4_K_M
│ └─ gemma3:1b
└─ ollama-worker.service(常時稼働)
RTX 3070 Ti PC(192.168.0.200)
└─ Windows 11 + WSL2
├─ Ollama
│ ├─ gemma3:12b
│ ├─ qwen2.5:7b
│ ├─ llama3.2:3b
│ ├─ gemma3:1b
│ └─ LFM2.5-1.2B-JP-GGUF:Q4_K_M
└─ ollama-worker-gpu.service(常時稼働)
次にやること
残っている課題がいくつかある。
WSL2のIPは再起動で変わる可能性がある。ポートフォワーディングの自動更新が必要だ。
Worker登録のTTLが5分なので、長時間稼働するWorkerは定期的に再登録するHeatbeat的な仕組みも欲しい。
そして本来やりたかったこと、
「Coordinatorがモデル一覧を見て、タスクを自動で振り分ける」
機能がまだ未実装だ。
登録情報は揃った。あとは使う側を作るだけ。