Runpod の GPU Pod へコンテナを 1コマンドでデプロイ + 後始末 までやる CLI。
- Runpod REST API v1 (
https://rest.runpod.io/v1) を直接叩く。SDK 依存なし - Tailscale auth-key を都度発行 (ephemeral / 1度きり / 10分有効)
- 同名 Offline デバイスを事前削除 → Tailscale 上の重複名衝突を回避
- GPU type / DC / 国 / 価格 で自動候補検索 (REST が単一リクエストで在庫マッチング)
- 登録待機 (
--wait) → 成功で auth-key を自動 revoke - destroy 時に Runpod 側消滅確認 + Tailscale デバイス削除
- ラベル指名 (
ollama/comfyui/studio) でも pod id でも操作可
app/
config.py - 設定 / プロファイル読み込み (pydantic v2)
runpod.py - Runpod REST v1 クライアント (httpx)
tailscale.py - Tailscale REST クライアント
deploy.py - 一連の処理オーケストレーション
cli.py - typer ベースの CLI (deploy-gpu-cli)
api.py - FastAPI HTTP ラッパー (POST /deploy, GET /pods)
templates/
_shared/onstart.sh
ollama/profile.json
comfyui/profile.json
studio/{profile.json,onstart.sh}
.env - API キー (gitignore 対象)
cd /opt/deploy-gpu-cli
pip install -e .
cp .env.example .env
# RUNPOD_API_KEY / TAILSCALE_API_KEY / TAILSCALE_TAILNET を埋めるpip install -e . で deploy-gpu-cli コマンドが PATH に登録されます。python -m app でも同じです。
RUNPOD_API_KEY=... # 必須。Runpod console で 2024-11-11 以降に発行したキー
TAILSCALE_API_KEY=tskey-api-...
TAILSCALE_TAILNET=- # `-` で自分の tailnet を自動解決
TAILSCALE_TAGS=tag:cloud-gpu-pods
SSH_PUBLIC_KEY="ssh-ed25519 AAAA... user@host"重要: Runpod の API キーは 2024-11-11 より前に発行されたもの (legacy key) だと REST API で 401 を返します。古いキーは Runpod console から再発行してください。CLI は 401/403 時にその旨を案内します。
deploy-gpu-cli --install-completion bash
exec bash- https://login.tailscale.com/admin/settings/keys で API access token (PAT) を発行
- ACL に使用するタグを
tagOwnersとして登録"tagOwners": { "tag:cloud-gpu-pods": ["autogroup:admin"] }
- https://www.runpod.io/console/user/settings で API key を発行 (REST API 対応キー)
.envのRUNPOD_API_KEYに設定SSH_PUBLIC_KEYを.envに入れると Pod のPUBLIC_KEYenv 経由で~/.ssh/authorized_keysに追記される
既定は実行 (deploy)。計画のみ確認は
--dry-run。 Tailscale 登録待機は デフォルト off (--no-wait)。--waitで最大 300秒ブロック。
# 本実行
deploy-gpu-cli deploy ollama
deploy-gpu-cli deploy comfyui -v
# 計画のみ表示
deploy-gpu-cli deploy ollama --dry-run
# Tailscale 登録まで待機
deploy-gpu-cli deploy ollama --wait
# Pod 名 (label / Tailscale hostname) を上書き
deploy-gpu-cli deploy ollama --name ollama-dev
# GPU type を直接指名 (検索スキップ)
deploy-gpu-cli deploy ollama --gpu-type-id "NVIDIA GeForce RTX 5090"
# 待機タイムアウトを延長
deploy-gpu-cli deploy comfyui --wait --wait-timeout 600nameを DNS-safe に正規化- Pre-flight: Runpod 上に同名 Pod があれば即エラー (二重課金回避)
- Tailscale で同名 Offline デバイスを削除
- ephemeral auth-key を発行 (
expirySeconds=600) search.gpu_nameを Runpod REST のgpuTypeIds列挙にマップ (_GPU_ALIASによる別名解決)- Probe: フィルタ条件で
POST /podsを試行し、Runpod が割当てたgpuTypeId/dataCenterIdを回収して即 terminate - 回収した正解の GPU/DC を
data_center_priority="custom"で固定し、本番のPOST /podsを発行 - onstart スクリプトを base64 で
dockerArgsに埋め込み (Runpod は onstart 専用フィールドが無い) exposed_portsは空配列であっても明示送信 (送らないとイメージのEXPOSEが拾われ Jupyter 等が出る)- Tailscale 登録待機 → 成功で auth-key を即 revoke
<target> には pod id または label を渡せる。
deploy-gpu-cli pods # Pod 一覧
deploy-gpu-cli status # Pod 詳細一覧
deploy-gpu-cli status ollama
deploy-gpu-cli status <pod-id>
deploy-gpu-cli logs ollama # runtime/status の要約のみ
# (詳細ログは `runpodctl get logs <id>` か Web Console)
deploy-gpu-cli stop ollama
deploy-gpu-cli start ollama
deploy-gpu-cli destroy ollama # 確認プロンプトあり (default N)
deploy-gpu-cli destroy ollama -y
deploy-gpu-cli destroy ollama --skip-tailscale
pods/statusはGET /pods?includeMachine=trueを使い、gpu_name/geolocation(= dataCenterId) を表示します。
destroy の処理順:
- label / id を捕捉
- Runpod 側
terminate_pod - 消滅をポーリングして確認
- 同名 Tailscale デバイスを online/offline 問わず 全削除
deploy-gpu-cli offers ollama --limit 20 # フィルタ済 GPU type ID の優先順リスト
deploy-gpu-cli ts-find ollama # Tailscale 上の同名/類似デバイス
deploy-gpu-cli ts-purge ollama # 同名 Offline デバイスを掃除REST API には公開 GPU カタログ (価格 / VRAM) のエンドポイントが無いため、
offersはgpuTypeIdsの優先リストのみ返します。価格・在庫の確定は実際の create_pod 試行で行います。
deploy-gpu-cli serve --host 127.0.0.1 --port 8000
# デプロイ
curl -X POST localhost:8000/deploy \
-H 'content-type: application/json' \
-d '{"target":"ollama","wait_for_tailscale":true,"wait_timeout":300}'
curl localhost:8000/pods
curl localhost:8000/healthztemplates/<target>/profile.json:
| フィールド | 意味 |
|---|---|
name |
プロファイル名 (CLI でのターゲット名と一致させる) |
image |
Runpod に渡す Docker image |
onstart_script |
templates/ 配下の onstart スクリプトパス |
runtype |
ssh / ssh_proxy / args / jupyter |
num_gpus |
Pod に割り当てる GPU 枚数 |
container_disk_gb |
コンテナの ephemeral disk (GB) |
volume_gb |
永続ボリュームの容量 (GB)。0 で無し |
volume_mount_path |
永続ボリュームのマウント先 |
env |
コンテナ env (TAILSCALE_AUTHKEY / PUBLIC_KEY は自動上書き) |
exposed_ports |
公開ポート。空配列でも明示送信される |
search |
下表の検索/フィルタ条件 |
| フィールド | 意味 |
|---|---|
gpu_name |
GPU type id / 別名 (rtx5090, h100 等)。_GPU_ALIAS で正規化 |
gpu_ram_gb_min |
クライアント側の参考下限値 (REST 検索には未使用) |
max_dph |
上限 USD/hour |
region |
DC ID (AP-JP-1 等、- を含む) または ISO 国コード (JP) の優先リスト |
cloud_type |
Runpod の cloud_type (SECURE 推奨) |
network_volume_id |
既存 network volume を attach する場合の ID |
min_vcpu_per_gpu / min_ram_per_gpu_gb |
UI 同名項目。REST には num_gpus 倍した pod 全体値で送信 |
min_download_mbps / min_upload_mbps |
Runpod の minDownload / minUpload (Mbps) |
support_public_ip |
Runpod に Public IP を要求するか (Tailscale 利用なら通常 false) |
cuda_versions |
許容 CUDA バージョンのリスト (allowedCudaVersions)。空なら無指定 |
SSH_PUBLIC_KEY を .env に入れると PUBLIC_KEY env としてコンテナに渡され、公式 Runpod イメージが ~/.ssh/authorized_keys に追記します。
Runpod には onstart 専用フィールドが無いため、onstart_script で指定したスクリプトを base64 化して以下の形で dockerArgs に注入します:
bash -lc 'echo <BASE64> | base64 -d | bash'
イメージの ENTRYPOINT/CMD を上書きしたくない場合は最小のダミー (_shared/onstart.sh 等) を指定してください。
- ephemeral auth-key: Pod 停止 = Tailscale ノード自動削除。
stop→startの再接続ではtailscaledが node-key を保持しているので auth-key 不要 - Runpod の
RTX 5090 SECUREは ~$0.99/h、COMMUNITYは ~$0.69/h。max_dphを極端に絞ると候補ゼロ destroyの確認プロンプトは-yで抑止可- Runpod の公開 API では container ログを取得できない。
logsコマンドは runtime/status の要約のみを返す。詳細はrunpodctl get logs <id>か Web Console を参照 - API キーは 2024-11-11 以降に発行されたもの が必要。旧 GraphQL 専用キーは 401 になる
{ "name": "ollama", "image": "m10i1986/ollama-running-on-gpupods:latest", "onstart_script": "_shared/onstart.sh", // base64 で dockerArgs に注入 "runtype": "ssh", // ---- Pod 作成設定 (検索条件ではない) ---- "num_gpus": 1, "container_disk_gb": 32, // コンテナの ephemeral disk "volume_gb": 0, // 永続ボリューム GB (0 なら無し) "volume_mount_path": "/workspace", // 永続ボリュームのマウント先 (volume_gb > 0 の場合必須) "env": { "TAILSCALE_HOSTNAME": "ollama", // Pod 名 + Tailscale hostname "TAILSCALE_TAG": "cloud-gpu-pods" // prefix `tag:` は自動付与 // TAILSCALE_AUTHKEY は deploy 時に自動注入 // SSH_PUBLIC_KEY が設定済なら PUBLIC_KEY も自動注入 }, "exposed_ports": [], // 空でも明示送信される // ---- 検索/フィルタ条件 ---- "search": { "gpu_name": ["RTX 5090"], // 別名/部分一致で gpuTypeIds に解決 "gpu_ram_gb_min": 32, // (REST 側でなくクライアント側の参考値) "max_dph": 1.0, // 上限 USD/hour "region": ["AP-JP-1", "JP"], // DC ID(`-` 含む) または ISO 国コードの優先リスト。空なら任意 "cloud_type": "SECURE", // "SECURE" | "COMMUNITY" | "ALL" "network_volume_id": null, // 既存 network volume を attach する場合 "min_vcpu_per_gpu": 4, // UI "vCPUs / GPU"。pod 全体には num_gpus 倍で送信 "min_ram_per_gpu_gb": 16, // UI "RAM / GPU"。同上 "min_download_mbps": 400, // UI "Internet speed" (Mbps) "min_upload_mbps": null, // 同上 "support_public_ip": false, // Tailscale 利用なら通常 false "cuda_versions": ["12.8", "13.0"] // UI "CUDA versions" } }