所要時間: 約45分 | 難易度: ★★★☆☆
この記事で作るもの
- インターネットから物理的に隔離された「LLM専用サンドボックス」をDocker上に構築します。
- Redditで指摘された「予期せぬ診断データの送信」をネットワークレイヤーで100%防止しつつ、ローカルのPythonから安全に推論APIを叩けるシステムを完成させます。
- 前提知識として、基本的なDocker操作とPythonの実行環境が必要です。
📦 この記事に関連する商品(楽天メインで価格確認)
RTX 3060 12GBVRAM 12GBを搭載し、Qwen 7Bクラスをローカルで安定稼働させるための最低ライン
※アフィリエイトリンクを含みます
先に確認するスペック・料金
Qwenの3B(30億パラメータ)から7Bクラスを動かすには、最低でも8GB以上のVRAMを搭載したGPUが必須です。 私の経験上、RTX 3060 12GBモデルが最もコスパが良く、Qwenシリーズをサクサク動かすためのエントリーラインになります。 もしMacで構築する場合は、メモリ(ユニファイドメモリ)が16GB以上ないと、OS側のスワップが発生してレスポンスが3.0秒を超えてくるため実用的ではありません。 ソフトウェアはすべてオープンソース(Ollama, Docker)を使用するため、月額費用は電気代以外かかりません。 クラウドAPIを使わず、VRAM 24GBのRTX 4090を2枚挿ししている私の環境では、推論時の消費電力は約400W程度になります。
なぜこの方法を選ぶのか
ブラウザ拡張機能やOSのファイアウォール(Windows Defenderなど)で通信を止める方法もありますが、これらはアプリケーションの挙動によってバイパスされるリスクがあります。 特に「診断ファイルの送信」は、OSの標準的なテレメトリ通信に紛れ込ませるケースがあるため、アプリ単位の制限では不十分です。 今回採用するDockerの「internal network」オプションは、コンテナからの外部トラフィックをカーネルレベルで遮断します。 外部との通信を一度すべて断ち切り、特定の「プロキシコンテナ」経由でしかアクセスできない構造を作るのが、SIerの実務でも使われる最も堅牢なアプローチです。
Step 1: 環境を整える
まずは、LLMを動かすための基盤となるDocker環境を準備します。 今回はモデルの管理が圧倒的に楽な「Ollama」をベースに使います。
# Dockerコンテナがインストールされていることを確認
docker --version
# 作業ディレクトリを作成
mkdir qwen-sandbox && cd qwen-sandbox
# ネットワークの状態を監視するためのツールをインストール(Ubuntu/Debianの場合)
sudo apt-get install tcpdump -y
tcpdump は、後ほど本当に通信が止まっているかを「数字とパケット」で証明するために使用します。
「止まっているはず」という憶測を排除し、実際に0バイトであることを確認するのがプロの仕事です。
⚠️ 落とし穴:
Docker DesktopをWindowsで使っている場合、WSL2のバックエンド設定を忘れるとGPUを認識しません。
nvidia-smi コマンドがコンテナ内で動かない場合は、NVIDIA Container Toolkitがインストールされているか必ず確認してください。
Step 2: ネットワーク隔離設定
次に、今回の核心となる docker-compose.yml を作成します。
ここでは、外部接続可能な「frontend」ネットワークと、外部接続不可の「backend」ネットワークを分離します。
version: '3.8'
services:
# 1. ローカルLLM本体 (完全に隔離)
qwen-model:
image: ollama/ollama:latest
container_name: qwen-isolated
volumes:
- ./ollama:/root/.ollama
networks:
- isolated-net
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
restart: always
# 2. 仲介役のプロキシ (ここだけがローカルPCと通信する)
api-proxy:
image: nginx:alpine
container_name: ollama-proxy
ports:
- "11434:11434"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- isolated-net
depends_on:
- qwen-model
networks:
isolated-net:
internal: true # これが最重要。外部へのルーティングを無効化する
internal: true を指定することで、このネットワークに属するコンテナは外の世界(インターネット)へ1パケットも送れなくなります。
たとえQwenのコード内に「診断ファイルを中国のサーバーに送る」という記述があっても、出口がないため通信は失敗します。
Step 3: 動かしてみる
設定ができたら、コンテナを起動してモデルをロードします。 モデルの初回ダウンロード時だけはインターネットが必要なので、ここは一時的に設定を変えて対応した経験がありますが、今回は「事前にダウンロード済みのモデルをマウントする」形をとります。
# コンテナを起動
docker-compose up -d
# モデルをダウンロード(この時だけ一時的にネットワークを繋ぐか、事前に済ませる)
docker exec -it qwen-isolated ollama run qwen2.5:3b
期待される出力
pulling manifest
pulling 8e293932e65d... 100%
verifying sha256 digest
writing manifest
removing unused layers
success
ここで一度、コンテナの中から外部へ ping を打ってみてください。
ping google.com を実行して「Network is unreachable」と出れば、隔離は成功です。
レスポンスが0msではなく、到達不能(unreachable)であることが、ネットワーク層で遮断されている証拠です。
Step 4: 実用レベルにする
隔離した状態で、ローカルのPythonスクリプトからこのセキュアなQwenを叩くコードを書きます。 直接Ollamaを叩くのではなく、Nginxプロキシを経由させることで、将来的にログの記録や認証を追加できるようになります。
import requests
import json
import time
class SecureLLMClient:
def __init__(self, base_url="http://localhost:11434"):
self.base_url = base_url
def ask_qwen(self, prompt):
start_time = time.time()
url = f"{self.base_url}/api/generate"
payload = {
"model": "qwen2.5:3b",
"prompt": prompt,
"stream": False
}
try:
response = requests.post(url, json=payload, timeout=30)
response.raise_for_status()
duration = time.time() - start_time
print(f"Latency: {duration:.2f}s")
return response.json().get("response")
except requests.exceptions.RequestException as e:
return f"Communication Error: {e}"
# 実行例
client = SecureLLMClient()
answer = client.ask_qwen("機密性の高いビジネス戦略のドラフトを作成して")
print(f"Qwenの回答: {answer}")
この構成の肝は、Pythonスクリプト側からは「localhost:11434」に繋いでいるように見えますが、その先のLLM本体は完全にネットから遮断されている点です。 私はこの構成で、顧客から預かった未公開データの要約業務を行っていますが、パケットキャプチャで外部通信が0であることを証明することで、セキュリティチェックをパスしました。
よくあるトラブルと解決法
| エラー内容 | 原因 | 解決策 |
|---|---|---|
could not select device driver "" with capabilities: [[gpu]] | NVIDIA Container Toolkit未導入 | ホストOSにToolkitをインストールし、Dockerを再起動する |
model not found | 隔離後にモデルを引こうとした | 隔離設定(internal: true)を一度外し、pullしてから再度有効にする |
Connection refused | Nginxプロキシが未起動 | docker-compose logs api-proxy で設定ファイルのミスを確認する |
次のステップ
このサンドボックス環境が完成したら、次は「RAG(検索拡張生成)」を組み合わせるのが王道です。
ローカルのPDFファイルやソースコードをベクトル化し、この隔離環境内のQwenに読み込ませることで、情報漏洩のリスクをゼロにした「社内専用AIアシスタント」が構築できます。
LangChainやLlamaIndexを使えば、今回作ったAPIエンドポイントを指定するだけで簡単に連携可能です。
また、余裕があれば docker stats コマンドで、推論時のVRAM消費量とシステムメモリの推移を監視する癖をつけてください。
Qwen 7Bモデルを1枚のRTX 3060で動かす際、量子化(Quantization)を4-bitにするか8-bitにするかで、レスポンス速度が0.5秒単位で変わるのを体感できるはずです。
よくある質問
Q1: Qwen3.8-Flash-Next以外のモデルでも使えますか?
もちろんです。Llama 3やGemmaなど、Ollamaでサポートされているモデルならすべて同じ方法で隔離できます。モデル特有の挙動を気にする前に、インフラ側で防御を固めるのがエンジニアとして正しい姿勢です。
Q2: 隔離ネットワークだとモデルのアップデートができなくて不便では?
その通りです。実務では「管理用コンテナ」を一つ用意し、アップデート時のみネットワークゲートウェイを一時的に開くスクリプトを運用します。常時接続を避けることが、意図しないデータ送信を防ぐ唯一の手段です。
Q3: パフォーマンスへの影響(オーバーヘッド)はありますか?
Dockerネットワークを介すことによるオーバーヘッドは、0.1ミリ秒以下です。LLMの推論には数秒かかるため、人間が体感できる影響は全くありません。むしろ余計なバックグラウンド通信が発生しない分、安定します。
1. X投稿用ツイート本文 (TWEET_TEXT) 2. アフィリエイト商品情報 (AFFILIATE_CONTEXT)
3. SNS拡散用ハッシュタグ (HASHTAGS) **4. SEOタグ (SEO_TAGS) ** 5. URLスラッグ (SLUG)


