mcpField
S
serverless-modal

serverless-modal

github.com/wanshuiyinクラウド・DevOps18.2k470 回インストール4 日前に更新
リポジトリ

serverless-modal について

serverless-modalは、PythonスクリプトからModal上でGPUトレーニング、評価、推論を実行するスキルです。単発ジョブ、API、バッチ処理、分散トレーニングの起動パターンを示し、実行前のコスト見積もりを必須とします。

主な機能

  1. 01Modalの起動スクリプトを生成
  2. 02トレーニング、推論、評価に対応
  3. 03バッチ処理とマルチGPUの例を提供
  4. 04実行前にコストを見積もり
  5. 05ボリュームまたは関数出力から結果を収集
  6. 06アプリとログを監視

活用シーン

  1. 01SSHインスタンスを管理せずGPU実験を実行
  2. 02常時稼働する推論APIをデプロイ
  3. 03並列GPU関数でデータセットを処理
  4. 04ファインチューニングやマルチGPU学習を実施

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wanshuiyin/auto-claude-code-research-in-sleep --skill serverless-modal

インストール後、Claude Code で/serverless-modalを入力して読み込まれたか確認します。

詳細

Modal クラウド GPU — 学習と推論

タスク:$ARGUMENTS

概要

Modal はサーバーレス GPU クラウドです。SSH ベースのプラットフォーム(vast.ai、リモートサーバー)に対する主な利点は次のとおりです。

  • 設定不要:SSH、Docker、ポートフォワーディングは不要です。Python を記述して → modal run を実行すれば完了です。
  • 自動でゼロにスケール:コードの実行が終了した瞬間に課金が停止します。アイドル状態のインスタンスはありません。
  • ローカル優先:ノート PC から modal run を実行できます。コード、データ、結果はローカルにとどまり、GPU 関数のみがリモートで実行されます。
  • 再現可能な環境:依存関係はシステムレベルのパッケージではなく、コード内の modal.Image で宣言します。 modal.Image チェーンは、宣言的な環境仕様である ../shared-references/compute-env-contract.md のレンダリング形式として扱います。仕様の各フィールド(ベース、 順序付けられた pip フェーズ、環境変数、スモークプローブ)、.aris/compute/modal.md の 同じ env:<name>@<specHash> 台帳エントリー、長時間実行の前に行う同じ 3 段階の検証が対象です。

最適な用途:ローカル GPU がなく、CUDA コードのデバッグ、小規模テストの実行、実験のすばやい反復が必要なユーザー。5 ドルの無料枠(カード不要)でコードのデバッグに十分対応でき、30 ドル(カード登録が必要)で小規模な実験の実行の多くをカバーできます。

トレードオフ:Modal は一部の GPU クラスでは GPU の時間単価が vast.ai や Lightning より高くなりますが、セットアップ時間とアイドル時の課金が不要になるため、短時間から中時間のワークロードでは結果的に安くなることがよくあります。長時間の学習実行(4 時間超)では、時間単価の低い vast.ai を検討してください。

認証

pip install modal
modal setup          # Opens browser login, writes token to ~/.modal.toml
# Verify:
modal run -q 'print("ok")'
  • 登録:https://modal.com(GitHub/Google ログイン)
  • 無料(カード不要):月額 5 ドル — 簡単なテストに十分です
  • 無料(カード登録あり):月額 30 ドル — https://modal.com/settings で支払い方法を登録すると、無料枠を全額利用できます。誤って超過請求されないよう、ワークスペースの利用上限を設定してください(Settings → Usage → Spending Limit)
  • 学術機関:1 万ドル分のクレジットを申請できます | スタートアップ:2 万 5 千ドル分のクレジットを申請できます
  • シークレット:modal secret create huggingface-secret HF_TOKEN=hf_xxxxx

推奨設定:カードを登録して月額 30 ドルの枠を利用できるようにしたら、すぐに利用上限(例:30 ドル)を設定し、無料枠を超えないようにしてください。上限に達すると、Modal はワークロードを一時停止します。

セキュリティに関する警告:カードの登録と利用上限の設定は、必ずブラウザーで https://modal.com/settings に直接アクセスして行ってください。Claude Code や CLI ツールを通じて、支払い情報、カード番号、請求情報を入力してはいけません。支払い操作に使用しても安全なのは、Modal の公式 Web サイトのみです。

料金(出典:modal.com/pricing、秒単位の課金)

GPUドル/秒≈ドル/時間VRAM帯域幅 GB/s無料枠 → 利用時間
T4$0.000164$0.5916GB300約 8.5 時間($5)/50.8 時間($30)
L4$0.000222$0.8024GB300約 6.3 時間/37.5 時間
A10$0.000306$1.1024GB600約 4.5 時間/27.3 時間
L40S$0.000542$1.9548GB864約 2.6 時間/15.4 時間
A100-40GB$0.000583$2.1040GB1555約 2.4 時間/14.3 時間
A100-80GB$0.000694$2.5080GB2039約 2.0 時間/12.0 時間
H100$0.001097$3.9580GB3352約 1.3 時間/7.6 時間
H200$0.001261$4.54141GB4800約 1.1 時間/6.6 時間
B200$0.001736$6.25192GB8000約 0.8 時間/4.8 時間

CPU:$0.047/コア/時間 | RAM:$0.008/GiB/時間(通常、GPU が総コストの 90%以上を占めます)

!! コスト見積もりが必要です !!

実行のたびに、必ずコストを見積もり、ユーザーに提示して確認を得てください。

主なポイント:

  • 推論のボトルネックは計算処理ではなくメモリ帯域幅です → 帯域幅の広い GPU の方が、全体として安くなることがよくあります
  • 7~8B の BF16 推論には約 22GB の VRAMが必要です(重み 15G + KV キャッシュ 1G +オーバーヘッド)。T4(16GB)では足りません
  • ベンチマークでは、H100 は L4 より安くなることがよくあります(11 倍高速ですが、価格は 5 倍にとどまります)
コスト見積もりテンプレート(実行のたびに必須)
Cost estimate (Modal):
  Model: [name] ([params], [precision])
  VRAM: ~[X]GB (weights + KV cache + overhead)
  GPU: [type] ([VRAM]GB, $[X]/sec = $[X]/hr, bandwidth [X] GB/s)
  Estimate: ~[N] min, ~$[X]
7~8B BF16 ベンチマークのコスト比較
GPU速度 tok/sドル/時間200tok のサンプル 1000 件のコスト所要時間
H100224$3.95$0.9815 分
A100-40GB104$2.10$1.1232 分
L420$0.80$2.22167 分

ワークフロー

ステップ 1:タスクを分析 → コストを見積もる → GPU を選択する

ほかの GPU スキルと同様に分析します。モデルサイズから必要な VRAM を判断し、GPU を選び、所要時間を見積もってコストを計算します。上記の料金表を参照してください。

VRAM の目安:

モデルサイズFP16 VRAM推奨 GPU
≤3B~8GBT4, L4
7-8B~22GBL4, A10, A100-40GB
13B~30GBL40S, A100-40GB
30B~65GBA100-80GB, H100
70B~140GBH100:2, H200
ステップ 2:Modal ランチャーを生成する

タスクの種類に応じて、適切なランチャースクリプトを生成します。

パターン A:単発 GPU 関数(トレーニング、評価、ベンチマーク)

run-experiment との連携で最も一般的なパターンです。既存のトレーニングスクリプトをラップします。

import modal

app = modal.App("experiment-name")
# One .pip_install() call per SPEC PHASE (chained calls install in order, so a
# pinned torch in the first call can't be dragged by packages in the second —
# the rendered form of compute-env-contract.md's ordered pip_phases):
image = (
    modal.Image.debian_slim(python_version="3.11")
    .pip_install("torch")                                        # phase 1: pins
    .pip_install("transformers", "accelerate", "datasets", "wandb")  # phase 2
)

# Mount local project code into the container
local_code = modal.Mount.from_local_dir(".", remote_path="/workspace")
# Persistent volume for checkpoints and results
volume = modal.Volume.from_name("experiment-results", create_if_missing=True)

@app.function(
    image=image,
    gpu="A100-80GB",          # Chosen based on Step 1 analysis
    mounts=[local_code],
    volumes={"/results": volume},
    timeout=3600 * 6,         # 6 hours max
    secrets=[modal.Secret.from_name("wandb-secret")],  # Optional
)
def train():
    import subprocess
    subprocess.run(
        ["python", "train.py", "--output_dir", "/results/run_001"],
        cwd="/workspace",
        check=True,
    )
    volume.commit()  # Persist results to volume

@app.local_entrypoint()
def main():
    train.remote()
    print("Training complete. Results saved to Modal volume 'experiment-results'.")

実行:modal run launcher.py

パターン B:Web API(永続的な推論サービス)
import modal

app = modal.App("inference-api")
image = (
    modal.Image.debian_slim(python_version="3.11")
    .pip_install("torch")                          # phase 1: pins
    .pip_install("transformers", "accelerate")     # phase 2
)

@app.cls(image=image, gpu="L40S")
@modal.concurrent(max_inputs=10)
class InferenceAPI:
    @modal.enter()
    def load_model(self):
        from transformers import AutoModelForCausalLM, AutoTokenizer
        self.tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-1B")
        self.model = AutoModelForCausalLM.from_pretrained(
            "meta-llama/Llama-3.2-1B", device_map="auto"
        )

    @modal.fastapi_endpoint(method="POST")
    def generate(self, request: dict):
        inputs = self.tokenizer(request.get("prompt", ""), return_tensors="pt").to("cuda")
        outputs = self.model.generate(**inputs, max_new_tokens=256)
        return {"text": self.tokenizer.decode(outputs[0], skip_special_tokens=True)}

デプロイ:modal deploy app.py

パターン C:vLLM による高性能推論
import modal, subprocess

app = modal.App("vllm-server")
image = modal.Image.debian_slim(python_version="3.11").pip_install("vllm")
VOLUME = modal.Volume.from_name("model-cache", create_if_missing=True)
MODEL = "Qwen/Qwen3-4B"

@app.function(image=image, gpu="H100", volumes={"/models": VOLUME}, timeout=3600)
@modal.concurrent(max_inputs=100)
@modal.web_server(port=8000)
def serve():
    subprocess.Popen(["python", "-m", "vllm.entrypoints.openai.api_server",
                      "--model", MODEL, "--download-dir", "/models", "--port", "8000"])
パターン D:バッチ並列処理(データセットに対する map 処理)
@app.function(image=image, gpu="T4", timeout=600)
def process_item(item: dict) -> dict:
    # ... process one item ...
    return {"result": "processed"}

@app.local_entrypoint()
def main():
    results = list(process_item.map([{"id": i} for i in range(1000)]))
パターン E:LoRA ファインチューニング
@app.function(
    image=image, gpu="A100-80GB", volumes={"/output": volume},
    timeout=3600 * 6, secrets=[modal.Secret.from_name("huggingface-secret")],
)
def train():
    # ... transformers + peft + trl training code ...
    trainer.save_model("/output/final")
    volume.commit()
パターン F:マルチ GPU 分散トレーニング
@app.function(image=image, gpu="H100:4", volumes={"/output": volume}, timeout=3600 * 12)
def train_distributed():
    import subprocess
    subprocess.run(["accelerate", "launch", "--num_processes", "4",
                    "--mixed_precision", "bf16", "train.py"], check=True)
ステップ 3:実行する
modal run launcher.py     # One-shot execution (most common for experiments)
modal deploy app.py       # Persistent service deployment
ステップ 4:検証と監視
modal app list            # List running apps
modal app logs <app-name> # Stream logs
ステップ 5:結果を収集する

結果の収集方法は、使用したパターンによって異なります。

ボリュームベース(トレーニングに推奨):

# Download results from volume after run completes
# Option A: In the launcher script, copy results to local mount before exit
# Option B: Use modal volume commands
modal volume ls experiment-results
modal volume get experiment-results /run_001/results.json ./results/

標準出力/戻り値ベース(評価/ベンチマーク用): 結果はターミナルに出力されるか、関数から返されます。すでにローカルにあります。

ステップ 6:クリーンアップ

Modal は自動的にゼロまでスケールダウンするため、インスタンスを手動で破棄する必要はありません。ただし、使用していないリソースはクリーンアップしてください。

modal app stop <app-name>     # Stop a deployed service
modal volume rm <volume-name> # Delete a volume when done

CLI リファレンス

modal run app.py          # Run once
modal deploy app.py       # Deploy persistent service
modal app logs <app>      # View logs
modal app list            # List apps
modal app stop <app>      # Stop
modal volume ls           # List volumes
modal volume get <vol> <remote> <local>  # Download from volume
modal secret create NAME KEY=VALUE       # Create secret

主なヒント

  • GPU のフォールバック:gpu=["H100", "A100-80GB", "L40S"] — Modal は順番に試します
  • マルチ GPU:gpu="H100:4"(最大 8 GPU、コストは GPU 数に比例して増加します)
  • ボリューム:永続ストレージには modal.Volume.from_name("x", create_if_missing=True) を使用します
  • @modal.enter() はコンテナごとに一度モデルを読み込みます | 並行リクエストには @modal.concurrent() を使用します
  • 長時間のトレーニング:timeout=3600 * N を設定します(デフォルトは 5 分です)
  • ローカルコード:modal.Mount.from_local_dir(".", remote_path="/workspace")
  • W&B との連携:secrets=[modal.Secret.from_name("wandb-secret")] を指定し、スクリプト内で wandb.init() を呼び出します

他のスキルとの組み合わせ

/run-experiment "train model"       <- detects gpu: modal, calls /serverless-modal
  -> /serverless-modal              <- analyzes task, generates launcher, runs
  -> Results returned locally or to Modal Volume
  -> No destroy step needed (auto scale-to-zero)

/serverless-modal                   <- standalone: any Modal GPU workload
/serverless-modal "deploy vLLM"     <- inference service deployment

CLAUDE.md の例

## Modal
- gpu: modal                 # tells run-experiment to use Modal serverless
- modal_gpu: A100-80GB       # optional: override GPU selection (default: auto-select)
- modal_timeout: 21600       # optional: max seconds (default: 6 hours)
- modal_volume: my-results   # optional: named volume for results persistence

SSH キー、Docker イメージ、インスタンス管理は不要です。pip install modal && modal setup を実行するだけです。

コスト保護:modal setup の後、ブラウザーで https://modal.com/settings にアクセスし(CLI 経由では絶対に行わないでください)、支払い方法を登録すると、月額 30 ドルの無料枠が利用できます(カードを登録しない場合は月額 5 ドルのみ)。次に、ワークスペースの利用上限を無料枠と同額に設定してください。上限に達すると Modal がワークロードを自動的に一時停止するため、予期しない請求を防げます。

ドキュメント

コメント