リモートGPUワーカー
この文書は、OpenFaceの管理本体をProxmox LXCから移動せず、ローカルマシンの GPUを利用する実装構成と運用手順を記録します。
TIP
pull型worker protocol、PostgreSQL永続化、capability scheduler、lease、 HTTP/WebSocket runtime proxy、独立Docker Composeは実装済みです。 OPENFACE_GPU_WORKERS_ENABLED=falseが既定なので、既存CPU Spaceの配置は 明示的に有効化するまで変わりません。
実装状況
| 項目 | 状態 |
|---|---|
| worker enrollmentと失効可能なcredential | 実装済み |
| heartbeat、GPU capability、VRAM条件 | 実装済み |
| FIFO claim、短期lease、期限切れ回収 | 実装済み |
| commit SHA固定のclone、Docker build、GPU run | 実装済み |
| 認証付きHTTP/WebSocket runtime gateway | 実装済み |
| stop要求とcontainer cleanup | 実装済み |
| 複数worker/workerごとの同時実行上限 | 実装済み |
| 管理画面でのworker drain/revoke | 未実装(API・DB拡張予定) |
| VRAMの動的予約 | 未実装(現在はclaim時の空きVRAM判定) |
実装はspaces-runner/gpu_control.py、gpu-worker/worker.py、 docker-compose.gpu-worker.ymlに分離されています。
目的
- Forgejo、PostgreSQL、ポータル、認証、スケジュール状態をLXCに残す。
- 選択したSpace、推論、ベンチマークを1台以上の信頼できるGPUマシンで実行する。
- overlay networkを追加せず、既存の信頼できるLANを使う。
- 現在の
/run/{owner}/{repo}/URLとOpenFaceの権限確認を維持する。 - GPUマシンが停止しても、リポジトリや永続状態を失わない。
- 後からGPUマシンを追加しても、管理本体を再設計しなくてよい構成にする。
初期版では、不特定ユーザー向けの強固なマルチテナント隔離、クラウド横断の 自動配置、分散学習、実行中コンテナのワーカー間移動は対象外です。
目標アーキテクチャ

この図の編集可能な正本は gpu-cluster-topology.drawioです。 PNGとSVGはdraw.io Desktop CLIから生成し、SVG overlap lintとPNG目視確認を 通しています。
LXCをコントロールプレーン、GPUマシンを一時的な実行ノードとして扱います。 ワーカー側からOpenFaceへ接続してjobを取得し、LXCからワーカーのDocker socketを 直接操作しません。
検証済み構成では、memory throughput fixture向けRTX 3060と、より大きいcompute fixture向けRTX 4090を独立して配置できます。同じprotocolでworkerを追加でき、 図は2枚のGPUが同じDocker hostに必須であることを意味しません。
責務の分離
| コンポーネント | 役割 |
|---|---|
LXC gateway | 公開URL、TLS、認証入口、HTTP/WebSocket中継 |
LXC frontend | カタログ、ワーカー状態、実行状態、操作UI |
LXC spaces-runner | スケジュール、認可、ワーカー登録、job状態、経路選択 |
| LXC Forgejo/LFS | ソース、revision、大きな永続成果物、Issue、権限 |
| LXC PostgreSQL | ワーカー、heartbeat、lease、job、runtime経路、監査イベント |
| GPU worker | capability報告、job取得、clone/build/run、log、cleanup |
| GPU Docker engine | Spaceコンテナ、image layer、破棄可能なbuild cache |
永続成果物はForgejo/LFSまたはLXC管理のストレージへ戻します。ワーカー上の checkout、image、cacheは再生成でき、削除可能でなければなりません。
ネットワークとセキュリティ
LXCとワーカーの通信には、既存の信頼できるLAN上で予約済みIPまたは固定IPを使います。 worker API portはLXCとGPUホスト間だけ許可します。Docker daemon、Docker socket、 PostgreSQL、認証のないワーカーポートを他のLAN端末やインターネットへ公開しません。
推奨するのはpull型のプロトコルです。
- 管理者が一度だけ使えるワーカー登録tokenを発行する。
- ワーカーが登録tokenを失効可能なワーカーcredentialへ交換する。
- 安定したworker IDとGPU capabilityを登録する。
- heartbeatを送り、実行可能なjobを問い合わせる。
- schedulerが1件のjobと短いleaseを返す。
- build、runtime、health、完了状態をワーカーが報告する。
ワーカーcredentialはworker APIだけに限定し、Git外で管理し、可能なものはhash化して 保存します。個別に失効できる必要があります。runtime proxyも既存のForgejo権限確認を 必ず通します。
capabilityとスケジュール
heartbeatでは次を報告します。
- worker IDと表示名
- OSとarchitecture
- Dockerの利用可否とversion
- GPU vendor、model、台数、合計/空きVRAM
- driverとCUDA runtimeの互換性
- 実行中job、同時実行上限、空きdisk
nvidia、cuda、cpuなどの対応feature
gpu、cuda、vram-12gb、local-gpuのようなrepository topicは検索と分類に 利用します。実際の配置要件と管理者overrideは、自由度を損なう固定schemaではなく OpenFaceのdatabaseで管理します。初期schedulerは次の順で判定します。
- 管理者が指定した実行先
gpuまたはcputopic- 最低VRAM
- onlineかつ空き容量のある互換worker
- 同条件ならFIFO
CPU Spaceは従来どおりLXCを既定にします。互換GPUワーカーがない場合はCPUへ 黙ってfallbackせず、待機状態にします。
job lifecycle
各遷移にはworker、repository revision、image ID、時刻、理由を記録します。 heartbeatが途絶えたruntimeはUnavailableに変更し、停止中のSpaceをRunningと表示しません。
ワーカーAPI案
全endpointをprivate、認証必須、version付き、rate limit付きにします。
| endpoint | 目的 |
|---|---|
POST /api/v1/workers/enroll | 一度限りのtokenをworker credentialへ交換 |
POST /api/v1/workers/register | identityと初期capabilityを登録 |
POST /api/v1/workers/{id}/heartbeat | 生存、resource、実行jobを更新 |
POST /api/v1/workers/{id}/jobs/claim | 互換jobを1件lease付きで取得 |
POST /api/v1/workers/{id}/jobs/{job}/events | build、health、log、完了状態を報告 |
POST /api/v1/workers/{id}/jobs/{job}/lease | 実行中jobのleaseを延長 |
DELETE /api/v1/workers/{id} | workerを失効して新規job取得を停止 |
ワーカーは上限付きのlog chunkを送信し、LXCへ任意のfilesystem accessを与えません。 APIにはidempotency keyを付け、通信再試行でcontainerが重複起動しないようにします。
ローカルGPUホスト
WindowsのGPUマシンでは、WSL2のGPU対応Docker、またはNVIDIA Container Toolkitを 導入したLinuxを推奨します。登録前にGPUを確認します。
docker run --rm --gpus all nvidia/cuda:12.6.3-base-ubuntu24.04 nvidia-smidocker-compose.gpu-worker.ymlはLXC本体のComposeから分離されています。
services:
gpu-worker:
build: ./gpu-worker
restart: unless-stopped
environment:
OPENFACE_URL: http://192.168.1.50:8090
WORKER_NAME: local-gpu-01
WORKER_TOKEN_FILE: /run/secrets/openface-worker-token
MAX_GPU_JOBS: "1"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- gpu-worker-cache:/data
- ./secrets:/run/secrets:ro
volumes:
gpu-worker-cache:個々のSpaceではなく、ワーカーが検証済みcontainerを起動するときにGPU deviceを 割り当てます。VRAM管理とcancelを負荷環境で確認するまでは同時GPU jobを1件にします。
セットアップ
1. GPUホストを確認
nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.6.3-base-ubuntu24.04 nvidia-smi2. コントロールプレーンを有効化
LXCの.envへ追加し、runnerを再構築します。
OPENFACE_GPU_WORKERS_ENABLED=true
OPENFACE_GPU_WORKER_LEASE_SECONDS=90
OPENFACE_GPU_WORKER_STALE_SECONDS=120docker compose up -d --build spaces-runner gateway3. 一度限りのenrollment tokenを発行
tokenをブラウザへ露出しないよう、LXC上で発行します。
docker compose exec -T spaces-runner python -c \
"import gpu_control; print(gpu_control.issue_enrollment_token('local-gpu-01')['token'])"GPUホストにsecrets/openface-worker-enrollment-tokenとして保存します。tokenは enrollment成功時に消費され、永続credentialはworker volumeへ保存されます。
4. GPU workerを起動
Copy-Item gpu-worker/.env.example gpu-worker/.env
docker compose --env-file gpu-worker/.env `
-f docker-compose.gpu-worker.yml up -d --buildOPENFACE_URLにはLXC gatewayのLAN URL、WORKER_PUBLIC_URLにはLXCから到達できる workerの予約済みLAN URLを指定します。GPU_WORKER_BIND_ADDRESSはworker PCのLAN IPに 限定し、host firewallでTCP 8787の接続元をLXCのIPだけに制限します。
5. SpaceをGPUへ配置
対象repositoryへspaceとgpu topicを付けます。必要に応じてnvidia、cuda、 vram-12gbのようなtopicを加えます。OpenFaceの通常のStart操作でGPU jobがqueuedに なり、互換workerがclaimします。ブラウザURLは従来どおり /run/{owner}/{repo}/です。
対話型Spaceの中継
ブラウザは従来どおり/run/{owner}/{repo}/へ接続します。LXC runnerが実行先を 参照します。
- LXC runtime:既存のローカルcontainerへproxy
- GPU runtime:workerのLAN endpointへHTTP/WebSocketをproxy
- offlineまたは期限切れ:明示的なUnavailable/再起動状態を表示
leaseされたOpenFace jobが作成したportだけを中継します。経路情報には短い有効期限を 設け、job停止時に削除します。
導入計画と現在地
Phase 0 — ホスト準備
- GPUマシンとLXCがLAN内で双方向に到達できることを確認する。
- Docker container内で
nvidia-smiを確認する。 - GPU、VRAM、driver、CUDA、storage、想定稼働時間を記録する。
- 機密性のないGPU SpaceをE2E fixtureとして1本選ぶ。
状態:完了。 gpu-worker/fixtures/gpu-diagnostic、 gpu-worker/fixtures/rtx3060-vector-lab、 gpu-worker/fixtures/rtx4090-matrix-labを実GPUで検証済みです。
Phase 1 — コントロールプレーン
- worker、capability、heartbeat、job、lease、audit tableを追加する。
- token登録と認証付きworker endpointを追加する。
- 現在のCPU配置を変えず、worker状態画面を追加する。
- stale heartbeat cleanupとidempotency testを追加する。
状態:実装済み。 worker、job、event、leaseはopenface_metrics databaseへ 起動時migrationされます。
Phase 2 — pull型ワーカー1台
- 独立した
gpu-workerserviceとCompose例を追加する。 - claim、clone、build、GPU run、health、log、stop、cleanupを実装する。
- Forgejoの正確なcommit SHAへjobを固定する。
- restart、cancel、build失敗、worker切断を検証する。
状態:実装済み。 credentialとruntime routeはworker volumeに保存され、 再起動時に実行中containerを再採用します。
Phase 3 — 対話型proxy
- 既存LXC gateway経由で
/run/のHTTPとWebSocketを中継する。 - sessionとrepository権限確認を維持する。
- Queued、Building、Running、Offline、Failedをポータルに表示する。
- CPUまたは特定workerを選べる管理者overrideを追加する。
状態:一部完了。 HTTP/WebSocket中継と状態APIは実装済みです。管理画面からの worker固定overrideは今後追加します。
Phase 4 — 複数workerと運用
- capability選択、同時実行上限、VRAM予約を追加する。
- workerのdrain、disable、revokeを追加する。
- queue時間、build時間、runtime health、失敗、GPU使用率を記録する。
- backup、token rotation、upgrade、incident手順を文書化する。
状態:基盤実装済み、運用UIは未完了。 複数worker、上限、失効可能credential、 監査eventは利用できます。
検証
GPUを必要としないprotocol E2E:
powershell -ExecutionPolicy Bypass -File scripts/test-gpu-worker-e2e.ps1NVIDIA GPUを実際にSpace containerへ渡すE2E:
powershell -ExecutionPolicy Bypass -File scripts/test-gpu-worker-nvidia-e2e.ps1RTX 3060でCUDA計算を行い、build、起動、device固定、計算結果まで検証するfixture:
powershell -ExecutionPolicy Bypass -File scripts/test-rtx3060-vector-lab.ps1RTX 4090でタイル行列積を行い、UUID選択、build、起動、device/VRAM、 数値結果まで検証するfixture:
powershell -ExecutionPolicy Bypass -File scripts/test-rtx4090-matrix-lab.ps12026-07-24の実機検証では、worker登録、2 GPU検出、単一deviceのclaim、build、 runtime proxy、stop、cleanupが完走しました。schedulerはjobの空きVRAM要件を 満たすGPUを1基だけ選び、Space container内からは割り当てresourceだけを確認しました。
- NVIDIA GeForce RTX 4090 — 24,564 MiB
- runtime response:
openface-remote-gpu

2026-07-25のRTX 3060検証では、CUDAとnvidia-smiでdevice順が異なる場合にも 誤割り当てしないよう、番号ではなくGPU UUIDで固定しました。16,777,216要素に 対するSAXPYを100回実行し、次を確認しています。
- NVIDIA GeForce RTX 3060 — 12,287 MiB、compute capability 8.6
- 計算結果sample:
5.000 - 撮影時の実効帯域:約305 GB/s
- desktopと390 px mobile:横overflowなし


同じfixtureをopenface/rtx3060-vector-labとして登録し、OpenFace APIから起動する 実運用経路も検証しました。ポータルはGPU · 実行中を表示し、通常の/run/ iframe 内へアプリを読み込みます。RUN AGAIN後もdevice_match: trueと計算結果を確認しました。


2026-07-25のRTX 4090検証では配置条件を22 GiBにしました。12 GB GPUを除外し、 24 GB card上のdisplay driver分を残すためです。OpenFaceはworkerをclaimし、 正確なrevision bac7105ff404ec7594a68c014680cdacc28e2f16をcloneしてDocker Spaceをbuildし、 jobをqueued、building、runningへ遷移させました。
4,096 × 4,096のタイル行列積を8回実行し、次を確認しました。
- NVIDIA GeForce RTX 4090 — 24,563 MiB、compute capability 8.9
- reference kernelの推定性能:6.60〜7.04 TFLOPS
- 計算結果sampleと期待値:
512.0 - 通常の
/run/proxy経由でdevice_match: true - PC 1,600px/モバイル390pxで横overflowなし


同じfixtureをopenface/rtx4090-matrix-labとして公開し、OpenFaceの管理APIから 起動したportal全体も確認しました。



この検証ではworkerのpollingをLAN上に残し、runtimeの戻り経路だけ既存のTailscale interfaceを利用しました。Windows側にTCP 8787のLAN受信ruleがないためです。 OpenFace APIはSpaceのstart/stopを操作できますが、Windows Firewallは変更しません。 runtimeもLANへ統一する場合は、対象LXCだけを許可する受信ruleを1件追加するか、 今回のように既存の信頼済み経路を利用してください。
完了条件
最初のproduction-ready版は次を満たした時点で完了です。
- 既存CPU Spaceの動作が変わらない。
- GPU SpaceがローカルGPUマシンでbuild・実行され、通常のOpenFace URLで表示できる。
- start、stop、files、runtime accessをForgejo権限で保護できる。
- GPU containerから割り当てたGPU resourceだけが見える。
- worker停止後、heartbeat期限内にSpaceがUnavailableへ変わる。
- 再接続でcontainerやjobが重複しない。
- LXC再起動とworker再起動から予測可能に復旧する。
- Docker API、database port、再利用可能な平文登録tokenを公開しない。
- logとauditからrepository SHAとworkerを特定できる。
- PC/モバイルのruntime状態をスクリーンショットと操作テストで確認する。
rollback
worker機能はserver-side feature flagで無効化できるようにします。無効化時は新規remote claimを止め、remote runtimeをUnavailableへ変更し、現在のLXC-only配置へ戻します。 workerを削除してもForgejo repository、LFS object、metrics、job auditを削除しません。
