LLM, VRChat, Blog
〖LLLM〗RTX3090でUdonLLMを作ってみる。
はじめに
こんにちは。りんです。 今回は、以前から少しずつ進めていたローカルLLM環境の構築についてまとめたいと思います。 最近、ChatGPTやClaudeのようなAIを使ってコードの相談をしたり、設計を考えたりすることがかなり増えました。 私自身、UnityやVRChat、UdonSharp、サーバー構築などで詰まったときにAIへ相談することが多く、かなり助けられています。 ただ、使っていく中で、 ・自分のPCやサーバーでAIを動かしてみたい ・モデルの違いや性能を自分で試してみたい ・UnityやUdonSharpの知識を入れた自分専用のAI環境を作ってみたい ・外部サービスに依存しすぎない環境も持っておきたい と思うようになりました。 そこで、RTX3090を使ってローカルLLMサーバーを構築してみました。 最初は「Ollamaを入れればすぐ動くでしょ」くらいに思っていたのですが、実際にやってみると、Docker、Linux、ネットワーク、Caddy、HTTPS、SearXNG、ファイアウォールなど、思っていた以上に幅広い知識が必要でした。 かなり詰まった部分も多かったので、今回は構築した内容と、どこで苦労したかを中心に書いていきます。
1. きっかけ
まずローカルLLM環境を作ろうと思った理由としては、単純にAIを自分の環境で動かしてみたかったというのが大きいです。 普段、私はUnityやVRChat関連の制作をしているのですが、UdonSharpやVRChat SDK周りは通常のWeb開発よりも情報が少なく、エラーの原因を探すのに時間がかかることが多いです。 そのため、 ・コードのレビューをしてほしい ・UdonSharpの実装方針を相談したい ・サーバー構築のエラーを切り分けたい ・GitやDockerのコマンドを確認したい ・過去の自分の知識をまとめて参照したい といった用途で、自分専用のAI環境があると便利だなと思いました。 また、ローカルLLMはモデルを自分で選べるので、コーディングに強いモデルや日本語に強いモデルなどを試せるのも魅力でした。 最終的には、単にAIと会話するだけではなく、開発作業を補助してくれる自分用の技術アシスタントのようなものを目指しています。
2. 環境
今回使用した環境はこのような感じです。 ・Ubuntu Server 24.04 ・NVIDIA GeForce RTX 3090 ・Ollama ・Open WebUI ・Docker ・Docker Compose ・Caddy ・SearXNG ・AWS Route 53 ・Windows PCからSSH接続 GPUはRTX3090を使用しています。 VRAMが24GBあるので、ローカルLLMを動かすにはかなり良いGPUだと思います。 もちろん最新のGPUと比べれば性能差はありますが、量子化モデルを使えば14B〜24Bくらいのモデルは現実的に動かせます。 最初はWindows上でやることも考えましたが、最終的にはUbuntu Serverで常時起動できる形にしました。 理由としては、Open WebUIやSearXNGをDockerで動かしたり、CaddyでHTTPS化したりするなら、Linuxサーバーとして構築した方が管理しやすいと思ったからです。
3. 全体構成
今回作成した構成を簡単に書くとこのようになります。 ブラウザ ↓ Caddy ↓ Open WebUI ↓ Ollama ↓ ローカルLLMモデル Web検索を使う場合は、ここにSearXNGが入ります。 Open WebUI ↓ SearXNG ↓ Web検索結果 Open WebUIはChatGPTのような画面でローカルLLMを使えるWeb UIです。 Ollamaは実際にモデルを動かす部分です。 Caddyは外部からHTTPSでアクセスするためのリバースプロキシです。 SearXNGはWeb検索をローカル環境から使うために入れました。 こうして見るとシンプルそうに見えますが、実際はそれぞれの接続設定でかなり詰まりました。 特にDockerコンテナからホスト側のOllamaへ接続する部分と、Caddyで外部公開する部分が難しかったです。
4. Ollamaの導入
まずはOllamaを入れて、ローカルでモデルを動かすところから始めました。 Ollamaはモデルのダウンロードや実行がかなり簡単で、CLIからすぐにモデルを起動できます。 例えば、以下のような感じです。 ollama run qwen2.5-coder:14b 最初に試したモデルは、コーディングに強そうなものを中心に選びました。 ・Qwen系 ・Qwen Coder系 ・Devstral系 ・Gemma系 特に私はUnityやC#、UdonSharpの相談をしたいので、コーディング性能はかなり重視しました。 ただ、ここで最初に感じたのは、モデル選びが意外と難しいということです。 大きいモデルほど性能が高いと思いがちですが、その分VRAMを使いますし、応答も遅くなります。 逆に小さいモデルは軽くて速いですが、複雑な設計相談では物足りないことがあります。 なので、ローカルLLMでは「一番強いモデルを入れる」というよりも、「用途に合わせてモデルを使い分ける」ことが大事だと思いました。
5. モデル選定で考えたこと
モデルを選ぶときに見ていたのは、主に以下です。 ・パラメータ数 ・量子化形式 ・VRAM使用量 ・応答速度 ・日本語性能 ・コーディング性能 ・コンテキスト長 ・Open WebUIとの相性 RTX3090はVRAMが24GBありますが、何でも余裕で動くわけではありません。 14Bくらいのモデルは比較的扱いやすいですが、24Bやそれ以上になると、量子化形式やコンテキスト長によってかなり重くなります。 特に Q4_K_M のような量子化モデルは重要でした。 これはざっくり言うと、モデルの性能をできるだけ保ちながら、VRAM使用量を抑えた形式です。 最初はこのあたりの意味があまり分かっていなかったのですが、モデルをいくつか試していくうちに、ローカルLLMでは量子化の理解がかなり大事だと感じました。 私の用途だと、 ・軽い質問用の高速モデル ・コード相談用のCoder系モデル ・設計やレビュー用の大きめのモデル のように分けるのがよさそうです。
6. Open WebUIの導入
Ollamaだけでもモデルは動かせますが、普段使いするならWeb UIが欲しいと思い、Open WebUIを導入しました。 Open WebUIを使うと、ブラウザからChatGPTのような画面でローカルLLMを使えます。 Dockerで動かせるので、導入自体はそこまで難しくありません。 ただ、問題はOpen WebUIからOllamaへ接続する部分でした。 最初は、Open WebUIの設定に http://localhost:11434 を入れればいいと思っていました。 ですが、Dockerコンテナ内の localhost は、Ubuntuサーバー本体ではなく、コンテナ自身を指します。 ここでかなり詰まりました。 ホスト側ではOllamaが動いているのに、Open WebUIからはOllamaに接続できない。 モデル一覧が出ない。 APIにアクセスできない。 最初はOpen WebUI側の設定が悪いのかなと思っていましたが、実際にはDockerのネットワークの問題でした。
7. 苦労したところ:DockerからOllamaに接続できない
今回、一番最初に大きく詰まったのがここです。 Open WebUIはDockerコンテナで動いています。 OllamaはUbuntuホスト側で動いています。 この場合、コンテナから見た localhost はコンテナ自身なので、ホスト側のOllamaには届きません。 これを解決するには、 ・Ollamaを 0.0.0.0:11434 で待ち受ける ・Dockerコンテナから見えるホスト側のIPを使う ・UFWで必要な通信を許可する ・Open WebUIの OLLAMA_BASE_URL を正しく設定する といった対応が必要でした。 正直、最初はかなり分かりづらかったです。 ローカルで動いているから簡単に繋がると思っていましたが、Dockerを挟むとネットワークが分かれているので、きちんと通信経路を考える必要があります。 ここで、Dockerの localhost とホストの localhost は別物ということをかなり実感しました。
8. Caddyで外部からアクセスできるようにする
次に、外部からOpen WebUIへアクセスできるようにするため、Caddyを使いました。 Caddyはリバースプロキシとして使えるWebサーバーで、HTTPSの証明書も自動で取得してくれます。 最終的には、自分のドメインのサブドメインをOpen WebUIに向ける形にしました。 イメージとしてはこのような感じです。 https://llm.example.com ↓ Caddy ↓ Open WebUI Caddyfile自体はかなりシンプルです。 llm.example.com { reverse_proxy 127.0.0.1:3000 } ただ、ここでもかなり詰まりました。 Caddyのサービスは起動しているように見えるのに、ブラウザからアクセスできない。 systemctl status caddy では動いているように見えるのに、実際には80番や443番で待ち受けていない。 この状態がかなり厄介でした。
9. 苦労したところ:Caddyが起動しているのに接続できない
Caddy周りで一番大変だったのは、サービスが起動しているように見えるのに、実際には接続できない状態です。 このときに確認したことは以下です。 ・Caddyfileの構文が正しいか ・Caddyが80番、443番でlistenしているか ・Open WebUIの3000番が開いているか ・CaddyからOpen WebUIへ接続できるか ・UFWで80番、443番が許可されているか ・ルーターやDNSの設定が正しいか ・systemdでCaddyが正常に起動しているか 最初はDockerやOpen WebUI側を疑っていたのですが、実際にはCaddy側をしっかり確認する必要がありました。 このときに、ss や curl を使って、どのポートで何が待ち受けているかを見るようになりました。 ss -tulpen curl http://127.0.0.1:3000 curl http://localhost 今まではエラーが出ると、そのサービス自体を疑いがちでした。 ですが、この経験で「通信経路を一つずつ確認する」ことが大事だと分かりました。 これはUnityやVRChatのエラー調査にもかなり近いと思います。 例えば、VRChatのギミックでも、 ・スクリプトが動いていないのか ・参照が入っていないのか ・同期されていないのか ・ローカルだけで動いているのか ・SDK側の制約なのか を分けて考える必要があります。 サーバー構築でも同じで、どこで止まっているかを一つずつ見る必要がありました。
10. セキュリティ
外部からアクセスできるようになると、次に気になるのはセキュリティです。 Open WebUIはログイン画面がありますが、インターネットに公開する以上、適当に公開するのは危険だと思いました。 特に気にしたのは以下です。 ・3000番ポートが直接外部に公開されていないか ・Caddy経由だけでアクセスできるようになっているか ・未認証でAPIにアクセスできないか ・管理者アカウントが適切に設定されているか ・不要なポートを開けていないか 最初は、アクセスできるようになることばかり考えていました。 ですが、アクセスできた後は「意図しないアクセスができないか」を考える必要があります。 ここはかなり大事だと思います。 個人用のローカルLLMでも、外部に公開するなら小さなWebサービスを運用しているのと同じです。 なので、最低限のセキュリティ意識は必要だと感じました。 11. SearXNGでWeb検索を使えるようにする 次に、Open WebUIでWeb検索を使えるようにするために、SearXNGを導入しました。 ローカルLLMは基本的に学習時点までの情報しか持っていません。 そのため、最新の情報を扱うにはWeb検索が必要になります。 SearXNGをDocker Composeで起動し、Open WebUIから参照できるように設定しました。 ここでもまたDockerネットワークが関係してきます。 SearXNG自体は起動している。 ブラウザからも開ける。 でもOpen WebUIから検索結果がうまく使えない。 このような状態になりました。 原因としては、Open WebUIから見たSearXNGのURLが正しくなかったり、Web Search設定がうまくできていなかったりする可能性がありました。 単体で動いていることと、他のサービスから使えることは別なのだと感じました。
11. SearXNGでWeb検索を使えるようにする
次に、Open WebUIでWeb検索を使えるようにするために、SearXNGを導入しました。 ローカルLLMは基本的に学習時点までの情報しか持っていません。 そのため、最新の情報を扱うにはWeb検索が必要になります。 SearXNGをDocker Composeで起動し、Open WebUIから参照できるように設定しました。 ここでもまたDockerネットワークが関係してきます。 SearXNG自体は起動している。 ブラウザからも開ける。 でもOpen WebUIから検索結果がうまく使えない。 このような状態になりました。 原因としては、Open WebUIから見たSearXNGのURLが正しくなかったり、Web Search設定がうまくできていなかったりする可能性がありました。 単体で動いていることと、他のサービスから使えることは別なのだと感じました。
12. 苦労したところ:SearXNG連携
SearXNG連携で確認したことは以下です。 ・SearXNGコンテナが起動しているか ・ホストからSearXNGにアクセスできるか ・JSON形式で検索結果が返るか ・Open WebUIコンテナからSearXNGにアクセスできるか ・Open WebUI側でWeb Searchが有効になっているか ・検索結果数などの設定が適切か このあたりは、正直まだ改善の余地があると思っています。 ただ、Web検索をローカルLLMに組み合わせられると、かなり実用性が上がります。 特に、技術情報は更新が早いので、モデル単体の知識だけでは古くなることがあります。 Web検索と組み合わせることで、最新情報を確認しながら回答できるようになるのはかなり便利です。
13. 今回学んだこと
今回のローカルLLM構築で学んだことはかなり多かったです。 Linuxサーバー Ubuntu Server上でサービスを動かすために、systemdやログ確認をよく使うようになりました。 systemctl status systemctl restart journalctl このあたりは最初は少し怖かったのですが、何度も確認しているうちに慣れてきました。 Docker Open WebUIやSearXNGを動かすためにDockerを使いました。 特に、コンテナとホストのネットワークの違いをかなり学びました。 Dockerは便利ですが、ネットワーク周りを理解していないと普通に詰まります。
ネットワーク
ポート、listen、localhost、0.0.0.0、ファイアウォール、リバースプロキシなどを実践的に学びました。 今まではなんとなく知っている程度でしたが、実際に繋がらない問題に当たると理解が深まります。
セキュリティ
外部公開するなら、アクセスできるかだけでなく、余計なところが開いていないかを見る必要があると感じました。 これは今後もっと勉強したい部分です。
LLMモデル
モデルのパラメータ数、量子化、VRAM使用量、用途ごとの使い分けを学びました。 ローカルLLMは、モデルを入れて終わりではなく、用途に合わせて選ぶことが大事だと思います。
14. 今後やりたいこと
今後やりたいこととしては、まず自分用のナレッジベースを作りたいです。 例えば、 ・Unityのエラー解決メモ ・UdonSharpの実装パターン ・VRChat SDKの制約 ・自作したギミックの仕様 ・GitやDockerのトラブルシュート ・サーバー構築メモ などをまとめて、Open WebUIから参照できるようにしたいです。 これができると、自分が過去に詰まった内容をAIが参照してくれるようになるので、かなり便利だと思います。 また、用途ごとにモデルを分けるのもやりたいです。 ・軽い相談用 ・コードレビュー用 ・設計相談用 ・長文整理用 のように分けることで、より実用的な環境になりそうです。
15. 感想
実際にローカルLLM環境を作ってみて、思ったよりも大変でした。 最初はOllamaを入れて、モデルをPullして、Open WebUIを立てれば終わりだと思っていました。 ですが実際には、 ・DockerからOllamaに繋がらない ・Caddyが正しく待ち受けない ・DNSやポート開放を確認する必要がある ・Web検索連携がうまくいかない ・モデル選定が難しい ・セキュリティも考える必要がある といった感じで、かなり幅広い知識が必要でした。 ただ、その分かなり勉強になりました。 特に、今までなんとなく理解していたDockerやネットワーク周りを、実際のトラブルを通して理解できたのが大きかったです。 やはり、実際に詰まって調べながら直すのが一番身につくなと思いました。
16. おわりに
今回はRTX3090を使って、ローカルLLMサーバーを構築した話を書きました。 まだ完全に理想の環境ができたわけではありませんが、Ollama、Open WebUI、Caddy、SearXNGを組み合わせることで、自分専用のAI環境にかなり近づいたと思います。 最初はAIモデルを動かすだけのつもりでしたが、実際にはLinux、Docker、ネットワーク、HTTPS、セキュリティ、モデル選定など、かなり多くのことを学ぶことになりました。 特に、Open WebUIからOllamaへ接続する部分と、Caddyで外部公開する部分はかなり苦労しました。 ですが、その分サーバー運用やネットワークの理解が深まったので、やってよかったと思います。 今後は、この環境にUnityやUdonSharpの知識を入れて、自分専用の開発支援AIのように育てていきたいです。 もしこの記事に質問等ございましたら、コメントしていただけると嬉しいです。 いいね、フォローしてくださると今後のブログ継続の励みになります。 最後まで閲覧いただきありがとうございました。
https://rin0700.com/blog/80c93bb1-9085-47bf-92b7-c422d7b04ed3