llama.cppルータモデルをすべてアンロードする

llama-serverを停止せずにVRAMを解放する方法

目次

llama.cpp ラーターモード は、llama-server における数年間で最も有用な変更の一つです。これにより、ローカルLLM運用者は、Ollamaで期待されるようなモデル管理体験に近いものをようやく手に入れることができました。同時に、llama-server を使い続ける価値がある生のパフォーマンスと低レベルの制御も維持されています。

ただし、一つ鋭いエッジがあります。すべてのモデルをアンロードする機能は、HTTP APIには単一の魔法のボタンとして存在しないのです。

ルーティング機能はモデルを一覧表示できます。モデルを読み込めます。モデルをアンロードできます。--models-max に達した場合、最も最近使用されなかったモデルを退避できます。しかし、現在、ファーストクラスのエンドポイントとして文書化されているのは、「すべてのモデルを今すぐアンロードする」呼び出しではありません。

laptop and desktop with llms

これは実際のブロック(障害)ではありません。正しいパターンはシンプルで、明確であり、スクリプト化可能です。

  1. ルーターに存在するモデルを問いかけます。
  2. ステータスが loaded(読み込み済み)であるモデルをフィルタリングします。
  3. 読み込み済みのモデルごとに /models/unload を一度呼び出します。

私は、本格的な ローカルLLMワークフロー において、このアプローチを推奨します。これは退屈ですが、可視性が高く、デバッグも簡単です。それは、推論サービス全体を再起動せずにVRAMを解放することを目的とする場合、まさに必要なものです。

llama.cpp ラーティングモードが実際に何をするのか

従来の llama-server の使い方では、1つのサーバーを1つのモデルで起動します。

llama-server \
  --model ./models/qwen3-8b.gguf \
  --port 8080

ルーティングモードはこのモデルを変更します。サーバーを1つのGGUFファイルにバインドする代わりに、ルーティングは複数のモデルのコーディネーター(調整役)になります。キャッシュやモデルディレクトリからモデルを発見し、オンデマンドで読み込み、リクエストを正しいモデルにルーティングし、必要に応じてモデルをアンロードできます。

典型的なルーティングモードの起動は以下のようになります。

llama-server \
  --models-dir ./models \
  --models-max 4 \
  --port 8080

ここで重要なオプションは --models-max です。これは同時に何個のモデルを読み込めるかを制御します。制限に達した場合、llama.cppは最も最近使用されなかったモデルを退避できます。これは有用ですが、意図的なアンロード操作の代わりにはなりません。LRU(Least Recently Used)退避は反応的です。アンロードスクリプトは運用制御です。

私の意見としては、本番環境でローカルモデルを運用する場合、ルーティングモードは玩具のチャットサーバーではなく、推論プロセスマネージャーとして扱うべきです。明示的なライフサイクル操作は重要です。

必要なモデル管理エンドポイント

発見(ディスカバリー)のためのメインエンドポイントは次の通りです。

curl -s http://localhost:8080/models | jq

このエンドポイントは、ルーティングが認識しているモデルとその現在のライフサイクル状態を返します。正確なJSON形状はビルド間でわずかに異なる可能性があるため、自動化を書く前に自身のレスポンスを確認してください。

一般的なレスポンス形状は次のようになります。

{
  "data": [
    {
      "id": "qwen3-8b",
      "status": "loaded"
    },
    {
      "id": "llama-3.2-3b",
      "status": "unloaded"
    }
  ]
}

1つのモデルをアンロードするには、次のように呼び出します。

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

これが基本的な操作です。この記事の残りの内容はすべてこれに基づいています。

ドキュメント化された「すべてアンロード」エンドポイントはありません

これが人々を混乱させる部分です。

以下のようなものが期待されるかもしれません。

curl -X POST http://localhost:8080/models/unload-all

その前提に基づいて構築しないでください。文書化された操作はモデルごとです。モデル識別子を /models/unload に渡し、llama.cppはその1つのモデルをアンロードします。

これは必ずしも悪いAPI設計ではありません。モデルごとの操作はより安全です。呼び出し側が何をアンロードするかを決定させることができます。また、他のクライアントで使用されているウォーム(読み込み済み)モデルをすべて誤って終了するような、予期せぬ本番環境の動作を回避します。

ワークステーションでは、「すべてアンロード」のショートカットは便利でしょう。しかし、マルチユーザーの推論ボックスでは、明示的なループの方が適しています。

まず1つのモデルをアンロードする

何かを自動化する前に、ルーティングが必要とする正確なモデル識別子をテストしてください。

まずモデルを一覧表示します。

curl -s http://localhost:8080/models | jq

出力から読み込み済みのモデルを1つ選び、それをアンロードします。

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

再度モデル一覧を確認します。

curl -s http://localhost:8080/models | jq

モデルのステータスが unloaded に変更された場合、エンドポイント、ポート、モデル識別子は正しいです。

うまくいかない場合は、推測しないでください。JSONを検査してください。ルーティングエイリアス、GGUFファイル名、モデルIDは、しばしば同じ文字列ではありません。

curlとjqで読み込み済みのすべてのモデルをアンロードする

単一モデルのアンロードが機能すれば、「すべてアンロード」のパターンは単なるシェルループです。

/models レスポンスに .data[].id.data[].status がある場合は、これを使用します。

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "{\"model\":\"$model\"}" \
      | jq
  done

これが全てのコツです。派手ではありませんが、管理者操作として正しい形状です。

  • 実際に読み込まれているモデルのみをアンロードします。
  • 何をしているかを出力します。
  • 1つの不透明な操作の背後で全てを隠す代わりに、モデルごとに失敗します。
  • cron、systemdフック、SSH、またはCIジョブから機能します。

本番環境用の再利用可能なスクリプト

2回以上実行するものについては、ワンライナーを貼り付けるのをやめましょう。スクリプトを保存します。

llama-router-unload-all.sh を作成します。

#!/usr/bin/env bash
set -euo pipefail

LLAMA_SERVER_URL="${LLAMA_SERVER_URL:-http://localhost:8080}"

models_json="$(curl -fsS "$LLAMA_SERVER_URL/models")"

loaded_models="$(printf '%s' "$models_json" \
  | jq -r '.data[] | select(.status == "loaded") | .id')"

if [ -z "$loaded_models" ]; then
  echo "No loaded models found."
  exit 0
fi

printf '%s\n' "$loaded_models" | while IFS= read -r model; do
  [ -z "$model" ] && continue

  echo "Unloading: $model"

  curl -fsS -X POST "$LLAMA_SERVER_URL/models/unload" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model\"}" \
    | jq

done

echo "Done. Current model state:"
curl -fsS "$LLAMA_SERVER_URL/models" | jq

実行可能にします。

chmod +x llama-router-unload-all.sh

デフォルトのローカルサーバーに対して実行します。

./llama-router-unload-all.sh

別のホストに対して実行します。

LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh

これが、実際にツールディレクトリに保存しておきたいバージョンです。 curl -f を使用しているため、HTTPエラーでスクリプトが失敗し、ファイルを編集せずにサーバーURLを上書きできます。

JSON形状にスクリプトを適応させる

盲目的に、すべてのllama.cppビルドが永遠に正確に同じフィールドを返すと想定しないでください。ルーティングモードは依然として進化しており、ビルドによってわずかに異なるJSON形状が公開される場合があります。

まずレスポンスを検査することから始めます。

curl -s http://localhost:8080/models | jq

スクリプトはこのフィルターを使用しています。

jq -r '.data[] | select(.status == "loaded") | .id'

モデル識別子が .name にある場合は、以下に変更します。

jq -r '.data[] | select(.status == "loaded") | .name'

ステータスフィールドが別の値を使用している場合は、それに応じてフィルターを調整します。重要なのは原則です:読み込み済みのモデルを選択し、/models/unload で受け入れられる識別子を抽出し、それぞれのモデルに対してアンロードを呼び出します。

アンロード後にもモデルが読み込まれる理由

これが最も一般的な混乱の源です。

ルーティングモードはオンデマンド読み込みをサポートしています。現在アンロードされているモデルに対してクライアントがチャット完了リクエストを送信した場合、ルーティングは自動的にそれを再度読み込む可能性があります。

つまり、以下のシーケンスが可能であることを意味します。

  1. すべてのモデルをアンロードします。
  2. Open WebUI、テストスクリプト、またはエージェントがリクエストを送信します。
  3. llama.cppがリクエストされたモデルを再度読み込みます。
  4. アンロードが失敗したと思うかもしれませんが、実際には失敗していません。

修正は運用上の問題であり、技術的なものではありません。VRAMを自由に保つのが目的であれば、まずクライアントトラフィックを停止してください。

例えば:

  • ベンチマークスクリプトを停止します。
  • エージェントとcronジョブを一時停止します。
  • Open WebUIセッションを閉じたり切断したりします。
  • 誤って本物のモデルリクエストを実行するヘルスチェックを無効にします。

アンロードはファイアウォールではありません。クライアントがモデルを要求し続ける場合、ルーティングモードはそれらを提供することでその役割を果たしています。

Open WebUIとEject(取り出し)ボタン

Open WebUI は、llama.cppのモデルアンロードサポートと統合できます。プロバイダーが llama.cpp として構成されている場合、Open WebUIは読み込み済みモデルの状態を表示し、管理者に対してEject(取り出し)アクションを公開できます。

内部的には、このアクションはOpen WebUI自身のアンロードAPIを呼び出し、それが構成された接続上のllama.cppの /models/unload エンドポイントを呼び出します。

手動操作には素敵なことですが、私は依然としてシェルスクリプトを保持することをお勧めします。UIボタンは便利です。しかし、スクリプトは監査可能で、繰り返し実行可能であり、深夜2時のヘッドレスボックスでも使用可能です。

「すべてアンロード」を使用すべきタイミング

読み込み済みのすべてのモデルをアンロードするのは、以下の場合に役立ちます。

  • より大きなモデルを開始する前にGPUメモリを解放する場合。
  • llama-server を再起動せずに開発ボックスをリセットする場合。 -クリーンなメモリ状態でのベンチマーク実行を準備する場合。
  • メンテナンスの前にローカル推論ワークロードをドレイン(排出)する場合。
  • 多くのモデルがウォームされた状態の乱雑なセッションから回復する場合。

アクティブなユーザーがウォームモデルに依存している場合、それは適切なツールではありません。その場合、--models-max を調整し、意図的なルーティングを使用し、LRU退避に作業の一部を行わせます。モデルごとのライフサイクル制御を備えたよりスマートなタイムアウトベースのアンロードが必要な場合は、llama-swap は、まさにその機能を任意の llama-server 設定の上にレイヤーする目的作りのプロキシです。

私のルールはシンプルです。通常の負荷にはLRUを使用し、オペレーター(管理者)の意図には明示的なアンロードを使用します。

トラブルシューティング

モデルエンドポイントが404を返す

ルーティング対応ビルドを実行していないか、または間違ったポートを呼び出している可能性があります。

サーバープロセスと利用可能なオプションを確認します。

llama-server --help | grep -i models

次に、両方のエンドポイントをテストします。

curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq

/v1/models エンドポイントはOpenAI互換のモデルリストです。/models エンドポイントはルーティングのモデル管理エンドポイントです。これらは関連していますが、同じものではありません。

jq がインストールされていない

JSON解析をスクリプト化する前に、jqをインストールしてください。

UbuntuまたはDebianの場合:

sudo apt-get update
sudo apt-get install jq

Homebrewを使用するmacOSの場合:

brew install jq

アンロード呼び出しがエラーを返す

ほとんどの失敗は、誤ったモデル識別子を渡すことから来ます。ファイル名ではなく、/models が返した正確な識別子を使用してください。

また、モデル名に引用符、スラッシュ、またはスペースが含まれていないか確認してください。上記のスクリプトは通常の文字列をうまく処理しますが、不自然な名前はより慎重なJSON構築を必要とする場合があります。

最大の安全性のために、jq でPOSTボディを構築できます。

jq -n --arg model "$model" '{model: $model}'

より防御的なアンロードループは、手動でエスケープされたJSONの代わりにそのボディを使用します。

VRAMがすぐに解放されない

まず、モデルのステータスが変わったことを確認してください。次に、別のリクエストがそれを再読み込みしなかったか確認します。また、GPUメモリツールは遅延したり、即座のアプリケーションレベルの意図ではなくアロケーター(メモリ割り当て)の動作を報告したりすることがあることを覚えておいてください。

実用的なテストはシンプルです。トラフィックを停止し、モデルをアンロードし、モデルステータスをリストし、その後GPUメモリを検査します。llama.cppにおけるモデルサイズとコンテキストウィンドウ全体にわたる測定されたVRAM使用量については、16 GB VRAM llama.cpp ベンチマーク で、健全性チェックに対する具体的な数値が示されています。

より安全なJSONボディバージョン

モデル識別子に不自然な文字が含まれている場合、JSONリクエストボディを生成するために jq を使用してください。

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    body="$(jq -n --arg model "$model" '{model: $model}')"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "$body" \
      | jq
  done

これは、リポジトリスタイルの識別子、カスタムエイリアス、またはパスで命名されたモデルを使用する場合に使用するバージョンです。

最終的な見解

llama.cppのルーティングモードは、ローカルLLM運用にとって大きな前進です。llama-server の直接的さを犠牲にすることなく、動的読み込み、モデル切り替え、メモリ対応の退避を提供します。

しかし、完璧な「すべてアンロード」エンドポイントを待たないでください。クリーンなソリューションはすでに存在します:読み込み済みのモデルをリストし、それらを1つずつアンロードします。

このパターンは明示的です。スクリプト化可能です。SSH経由で機能します。Open WebUIとも良く連携します。そして最も重要なのは、ルーティングを再起動せずにVRAMを解放するのです。

ローカルAIインフラストラクチャにとって、それはまさにあなたが望むような退屈な制御インターフェースです。

購読する

システム、インフラ、AIエンジニアリングの新記事をお届けします。