LinuxでDockerを使ったSGLangのデプロイ:RadixAttentionによる高速LLM推論

AI tutorial - IT technology blog
AI tutorial - IT technology blog

SGLang vs vLLM:どちらを使うべきか?

LLMをセルフホストするなら、おそらくすでにvLLMを試したことがあるでしょう。成熟していてドキュメントも充実しており、すぐに使えることから、多くのチームにとってデフォルトの選択肢となっています。一方、SGLangは特に複雑なプロンプト構造やマルチターン会話を含むワークロードで、着実に注目を集めています。

両フレームワークの根本的な違いは、KVキャッシュの扱い方にあります。vLLMはPagedAttentionを使用し、GPUメモリを固定サイズのブロックで管理します。単純な実装と比べれば大きな改善ですが、多くのリクエストが同じプレフィックスを共有する場合には限界があります。SGLangのRadixAttentionはKVキャッシュをRadixツリーとして扱います。同じシステムプロンプトを持つ2つのリクエストがあれば、そのプレフィックスは一度だけ計算されて再利用されます——重複なし、無駄な計算なしです。

単発のクエリであれば、差はほとんど感じられません。しかし、毎回同じ500トークンのシステムプロンプトから始まるチャットボットを動かしてみると、RadixAttentionの恩恵が現れてきます。毎回数百トークンのコードベースコンテキストを先頭に追加するコーディングアシスタントでも同様です。

簡単な比較:

  • vLLM — PagedAttention、大規模なエコシステム、OpenAI互換API、多様なワークロードに対して優れたスループット
  • SGLang — RadixAttention、高速なプレフィックス再利用、ネイティブの構造化出力サポート、成長中のエコシステム

メリットとデメリット

SGLang

  • メリット: 共有プレフィックスのワークロード(チャットボット、エージェント、RAGパイプライン)で大幅な高速化
  • メリット: 構造化出力のサポートが組み込み済み——外部ライブラリなしでJSONスキーマを強制できる
  • メリット: OpenAI互換APIのため、ほとんどのクライアントコードを修正なしで利用できる
  • メリット: 活発な開発で頻繁にリリースされる
  • デメリット: vLLMと比べてコミュニティが小さい
  • デメリット: ドキュメントは改善中だが、エッジケースに関してはまだ不足している部分がある
  • デメリット: デフォルトで使える量子化オプションが少ない

vLLM

  • メリット: 本番環境での実績が豊富
  • メリット: 幅広いモデルと量子化オプション(GPTQ、AWQなど)をサポート
  • メリット: コミュニティが大きく、トラブルシューティングのリソースも豊富
  • デメリット: デフォルトではプレフィックスキャッシュがない(有効化は可能)
  • デメリット: プレフィックスが繰り返されるワークロードではTTFTが高くなる

実際に、毎リクエストに300トークンのシステムプロンプトを先頭に追加するカスタマーサポートチャットボットの本番環境でSGLangを使用しました。同一ハードウェアでvLLMと比較して、TTFTが約40%低下しました。3ヶ月運用してもエラー率は安定したままでした。結果は期待通りでした。

推奨セットアップ

Dockerコマンドを実行する前に、以下の準備を整えてください:

  • Linuxホスト(Ubuntu 22.04以降を推奨)
  • 最低16GB VRAMのNVIDIA GPU(7B以上のモデルをフル精度で動かすには24GB以上)
  • NVIDIAドライバーがインストール済みで動作している(nvidia-smiが出力を返す)
  • Docker Engineがインストール済み
  • NVIDIA Container Toolkitがインストール済み
  • モデルのウェイト保存に50GB以上の空きディスク容量

NVIDIA Container Toolkitが正しく設定されているか確認したい場合は、次のテストを実行してください:

docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi

出力にGPU情報が表示されれば問題ありません。権限またはドライバーエラーが出た場合はここで止まり、先に修正してください——DockerがGPUを認識できない限り、後続の作業はすべて失敗します。

もう一点:Hugging FaceのキャッシュはコンテナではなくホストPC上に保存してください。ボリュームとしてマウントすることで(ステップ3で説明)、イメージを再構築するたびにウェイトを再ダウンロードしなくて済みます。

実装ガイド

ステップ1:SGLang Dockerイメージのプル

SGLangはDocker Hub上に公式イメージを公開しています。最新の安定版を取得しましょう:

docker pull lmsysorg/sglang:latest

CUDA 12.xではなく11.8を使用している場合は、Docker Hub上で利用可能なタグを確認して特定のバージョンを指定してください。本番環境ではlatestを使用しないでください——リリース間で予期せず動作が変わることがあります。

ステップ2:モデルディレクトリのセットアップ

モデルのウェイトを保存するためのディレクトリをホストPC上に作成し、Hugging Faceのキャッシュパスをそのディレクトリへ向けます:

mkdir -p /data/hf-cache
export HF_HOME=/data/hf-cache

サーバーを起動する前にモデルを事前にダウンロードしておくことで、初回起動時のタイムアウト問題を回避できます。Hugging Face CLIを使用してください:

pip install huggingface-hub
huggingface-cli download meta-llama/Llama-3.1-8B-Instruct \
  --local-dir /data/hf-cache/hub/models--meta-llama--Llama-3.1-8B-Instruct

デプロイするモデルに応じて変更してください。LlamaのようなゲートモデルにはHugging Faceのトークンが必要です——事前にhuggingface-cli loginを実行してください。

ステップ3:SGLangサーバーの起動

メインのDockerコマンドは以下の通りです:

docker run -d \
  --name sglang-server \
  --gpus all \
  --shm-size 32g \
  -p 30000:30000 \
  -v /data/hf-cache:/root/.cache/huggingface \
  lmsysorg/sglang:latest \
  python -m sglang.launch_server \
    --model-path meta-llama/Llama-3.1-8B-Instruct \
    --host 0.0.0.0 \
    --port 30000 \
    --mem-fraction-static 0.85

よく引っかかる3つのフラグを説明します:

  • --shm-size 32g — SGLangはプロセス間通信に共有メモリを使用します。Dockerのデフォルトは64MBです。これでは全く足りず、クラッシュ時のエラーメッセージも解読困難なものになります。十分なRAMを持つマシンでは、少なくとも32GBに設定してください。
  • --mem-fraction-static 0.85 — GPU VRAMの85%を静的KVキャッシュに割り当てます。モデルのウェイトとランタイムのオーバーヘッドのための余裕を残してください。起動時にOOMが発生する場合は0.75に下げてください。
  • -p 30000:30000 — サーバーはデフォルトでポート30000をリッスンします。そのポートがすでに使われている場合は左側の番号を変更してください。

ステップ4:サーバーの動作確認

コンテナのログを確認して、サーバーが正常に起動したことを確かめます:

docker logs -f sglang-server

The server is fired up and ready to roll!というメッセージが表示されたら、ヘルスエンドポイントにアクセスしてください:

curl http://localhost:30000/health

200レスポンスが返ってきたらサーバーは正常に動作しています。次に実際の推論リクエストをテストします:

curl http://localhost:30000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "messages": [{"role": "user", "content": "RadixAttentionとは何ですか?"}],
    "max_tokens": 256
  }'

ステップ5:Docker Composeを使った管理の簡略化

テスト以外の用途では、Docker Composeを使う方が便利です。docker-compose.ymlを作成してください:

services:
  sglang:
    image: lmsysorg/sglang:latest
    container_name: sglang-server
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    shm_size: 32g
    ports:
      - "30000:30000"
    volumes:
      - /data/hf-cache:/root/.cache/huggingface
    command: >
      python -m sglang.launch_server
      --model-path meta-llama/Llama-3.1-8B-Instruct
      --host 0.0.0.0
      --port 30000
      --mem-fraction-static 0.85

起動するには:

docker compose up -d

restart: unless-stoppedポリシーにより、再起動やクラッシュが自動的に処理されます。本番環境で動かすなら欠かせない設定です。

ステップ6:アプリケーションの接続

OpenAI互換APIにより、OpenAI SDKを使用した既存のコードはベースURLを1か所変更するだけで動作します:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:30000/v1",
    api_key="none"  # SGLangはデフォルトでAPIキーを必要としない
)

response = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[
        {"role": "system", "content": "あなたは親切なアシスタントです。"},
        {"role": "user", "content": "KVキャッシュを1段落で説明してください。"}
    ]
)

print(response.choices[0].message.content)

よくある問題のトラブルシューティング

初回セットアップでほぼ必ず遭遇する問題がいくつかあります:

  • 起動時にOOM — --mem-fraction-staticを0.70か0.75に下げてください。それでもモデルが収まらない場合は、より大きなGPUか量子化モデルが必要です。
  • 初回リクエストが遅い — これは仕様通りです。SGLangは初回実行時にCUDAカーネルをコンパイルしてローカルにキャッシュします。2回目以降の起動は明らかに速くなります。
  • ポートが使用中 — -p 30000:30000を-p 8080:30000(または空いているポート番号)に変更してください。
  • モデルが見つからない — ボリュームマウントを確認し、モデルファイルが/data/hf-cacheにあることを確認してください。次のコマンドで確認できます:docker exec -it sglang-server ls /root/.cache/huggingface

SGLangは、システムプロンプト、RAGコンテキスト、Few-shotサンプルなどプレフィックスの再利用が含まれるワークロードなら試す価値があります。GPUツールキットさえ整っていれば、Dockerのセットアップは簡単で、vLLMからの移行も通常1行の変更で済みます。

Share: