Dockerで試すローカルLLMのWeb UI — llama.cpp入門の続きとして作る実践編
ローカルLLMを動かすとき、モデルを端末から起動してコマンドで試す方法があります。今回はそこに小さなWeb画面を付けて、ブラウザーから入力できるようにしました。
前回の記事「ローカルLLMを動かす llama.cpp 入門」では、llama.cppの役割やGGUF、量子化、CPU・GPUで動かすときの違いを整理しました。今回はその続きとして、前回触れたllama-serverをWeb UIから呼び出し、Docker Composeでまとめて動かすところまで試します。
画面だけならデモモードで確認できます。GGUFモデルを接続して実際に推論する構成は、GPUを使う前提です。
まずは構成から
画面と推論エンジンを分け、Web APIが間をつなぎます。llama.cpp自体の全体像は、次の図のようになります。
今回の試作では、画面と推論エンジンの間にPythonのWeb APIを置きました。
Python側はWeb画面の配信と入力検査、llama-serverへの中継を担当します。llama.cppがモデル推論を受け持つ構成です。
デモモードではモデルに接続しません。固定の応答を返し、画面にもデモであることを表示します。モデルを動かしていないのに、動いているように見せることはしません。
ディレクトリ構成
参考のために、ZIPファイルを置いておきますので、ダウンロードして活用してください。
ggml-web-prototype/
├── app/
│ ├── Dockerfile
│ ├── server.py
│ └── static/
│ └── index.html
├── docs/
│ ├── assets/
│ │ └── llama_cpp_architecture.png
│ └── qiita_article.md
├── models/
├── tests/
│ └── test_server.py
├── .env.example
├── .gitignore
├── compose.yaml
└── README.md
Python側に外部パッケージは使っていません。HTTPサーバー、JSON処理、llama-serverへのHTTPリクエストを標準ライブラリで処理します。試作の入り口として、構成を追いやすくするためです。
デモ画面を起動する
Docker EngineとDocker Composeが使える環境で、プロジェクトのルートから実行します。
docker compose up --build
ブラウザーで http://localhost:8080 を開きます。右上にDEMO MODEと表示されていれば、LLMはまだ起動していません。入力した質問を含む確認用の応答が返ります。このモードは画面の確認用です。
画面にはチャット欄のほか、プロンプト例、Temperature、最大出力トークン数を置きました。会話履歴はブラウザーのメモリ上にだけ置いているため、再読み込みすると消えます。
デモではなくモデルを実行する
デモ以外でも実行できます。GGUFモデルを用意し、llama-serverとWeb UIを同じComposeネットワークで起動すると、画面から送った質問を実際のモデルが処理します。
注意点
この試作で用意した実推論用Compose設定は、NVIDIA GPU向けのCUDAイメージを使います。そのため、デモ以外で動かす場合は基本的にGPUが必要です。Docker EngineからNVIDIA GPUが見える環境を用意してください。GGUF形式のモデルをmodelsフォルダーに置き、設定ファイルを作ります。
cp .env.example .env
.envを開き、モデルファイル名などを設定してください。
LLAMA_BASE_URL=http://llama:8080/v1
LLAMA_MODEL=local-model
MODEL_FILE=your-model.gguf
LLAMA_CTX_SIZE=4096
LLAMA_TIMEOUT_SECONDS=120
your-model.ggufは実際のファイル名に置き換えます。モデル名はComposeのllama-serverにaliasとして渡し、Web APIからも同じ値を送ります。
次のコマンドで、CUDA版llama-serverを含めて起動します。
docker compose --profile llm up --build
Composeのllamaサービスはllmプロファイルに入れてあります。画面だけ試すときは起動せず、モデルを使うときにだけ有効にできます。llama-serverのポートはホストに公開していません。WebコンテナからComposeネットワーク内のサービス名llamaを使って接続します。
Web画面のポートは127.0.0.1:8080だけに公開しています。別のPCからアクセスさせる設定にはしていません。画面はPC向けの固定レイアウトです。スマートフォン向けのレイアウト調整は入れていません。
Composeのgpus: allは、利用可能なGPUをllamaコンテナへ割り当てる指定です。llama.cpp自体はCPUでも動かせます。公式にはCUDAを含まないghcr.io/ggml-org/llama.cpp:serverイメージも用意されていますが、このプロジェクトのComposeはGPU構成を標準にしています。CPUで使う場合は、イメージを:serverへ替え、gpus: allを削除してください。CPU推論はGPUより応答が遅くなりやすく、モデルをメモリに載せられるかも確認が必要です。詳しくはllama.cpp公式Dockerガイドを参照してください。
APIでは何をしているか
ブラウザーからWeb APIへ送るデータは、会話履歴と生成設定です。
{
"messages": [
{"role": "user", "content": "llama.cppとは何ですか?"}
],
"temperature": 0.7,
"max_tokens": 512
}
APIはその前に、次の項目を確認します。
- メッセージ件数が上限内か
- roleがsystem、user、assistantのいずれかか
- 本文が文字列で、長さが上限内か
- Temperatureと出力トークン数が指定範囲内か
- リクエスト本文が大きすぎないか
検査に通ったら、設定済みのLLAMA_BASE_URLへChat Completionsのリクエストを送ります。ブラウザーから接続先URLは受け取りません。ユーザー入力を使って任意のホストへアクセスする形にしないためです。
DockerfileとComposeで決めたこと
WebコンテナにはPython 3.12のslimイメージを使い、root以外のユーザーで起動します。Pythonの外部依存パッケージはなく、ファイルを置いてHTTPを受けるだけなので、Web側にモデルファイルをコピーする必要もありません。
モデルAPIはホストへ公開せず、Webコンテナから内部ネットワーク経由で呼び出します。ホストに公開するポートもローカルアドレスに限定しました。画面サーバーのヘルスチェックは/healthzを使います。
テスト
モデルサーバーに依存しない入力検査やデモ応答は、Python標準のunittestで確認できます。
python3 -m unittest discover -s tests -v
python3 -m py_compile app/server.py tests/test_server.py
Docker実行環境でGPU推論まで確認するときは、コンテナ起動だけでなく、選んだモデルがVRAMに収まること、GPUがコンテナ内から見えることも確かめます。ここはホストのドライバーやモデルによって変わります。
使う前に
これはローカルで試すためのプロトタイプです。認証、アクセス制御、TLS、複数ユーザーの管理は含んでいません。ネットワーク越しに公開する場合は、それらを別途追加してください。
llama.cppへ接続すると、入力した文章は設定したモデルサーバーへ送られます。モデル自体がローカルにあっても、Dockerの接続先やログ設定は確認しておくと安心です。
おわりに
前回はllama.cppの仕組みを整理し、今回はその推論APIをWeb UIにつなぎました。画面だけの確認はデモモードで、用意した実推論構成はGPUを使います。モデルを置いてComposeの設定を合わせれば、同じ画面から実際の推論を試せます。
まずデモモードで画面を見てから、小さなモデルを一つつなぐ。ローカルLLMの構成を試す入り口としては、このくらいのサイズが扱いやすいと思います。
参考資料
- llama.cpp公式リポジトリ
- llama.cpp Dockerガイド
- llama.cpp llama-server API
- Docker ComposeのGPUサポート
- Docker Compose servicesリファレンス


