OCI無料サーバーでLLMをOpenAI互換APIとして公開した話
OCIの無料枠サーバーにLLMを載せて、OpenAI互換のAPIとして外部に公開するまでの記録です。初心者がつまずきながら完成させた手順をそのまま残しています。
構成
| 項目 | 内容 |
|---|---|
| サーバー | OCI Always Free(Ubuntu、ARMアーキテクチャ) |
| LLMサーバー | shimmy v1.8.1 |
| モデル | Hy-MT2-1.8B-GGUF(Tencent製多言語翻訳モデル) |
| リバースプロキシ | nginx + Let's Encrypt(SSL) |
| 認証プロキシ | FastAPI + uvicorn(自作) |
| ドメイン | 独自ドメイン + Cloudflare |
STEP 1|システム更新とツールインストール
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget nginx certbot python3-certbot-nginx python3 python3-pip
huggingface-cli(モデルダウンロード用)をインストール:
pip3 install huggingface_hub --break-system-packages
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
STEP 2|shimmyをビルド(ARMの場合はコンパイルが必要)
OCIの無料枠はARMアーキテクチャなので、x86用バイナリは動きません。Rustでソースからビルドします。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
sudo apt install -y build-essential cmake pkg-config libssl-dev clang libclang-dev gcc-aarch64-linux-gnu libc6-dev
cargo install shimmy
ビルドには10〜20分かかります。
STEP 3|モデルをダウンロード
mkdir -p ~/models
hf download tencent/Hy-MT2-1.8B-GGUF \
--include "*.gguf" \
--local-dir ~/models/
STEP 4|モデルを認識させてテスト起動
shimmy discover
shimmy list
shimmy serve --bind 127.0.0.1:11435
別ターミナルで確認:
curl -s http://127.0.0.1:11435/v1/models | python3 -m json.tool
STEP 5|systemdサービスとして登録
sudo tee /etc/systemd/system/shimmy.service > /dev/null << 'EOF'
[Unit]
Description=Shimmy AI Inference Server
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu
ExecStart=/home/ubuntu/.cargo/bin/shimmy serve --bind 127.0.0.1:11435
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable shimmy
sudo systemctl start shimmy
STEP 6|nginxでリバースプロキシ設定
sudo tee /etc/nginx/sites-available/shimmy > /dev/null << 'EOF'
server {
listen 80;
server_name corota.pp.ua;
location / {
proxy_pass http://127.0.0.1:11436;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/shimmy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
STEP 7|SSL証明書の取得
ハマりポイント:OCIのiptablesとCloudflareのプロキシ
certbotが失敗する原因が2つありました。
①iptablesのルール順序問題
OCIのデフォルト設定ではREJECTルールが先に来ているため、ポート80/443を追加しても後ろに入ってしまいブロックされます。
# 既存のルールを確認
sudo iptables -L INPUT --line-numbers
# REJECTより前にポート80/443を追加
sudo iptables -I INPUT 5 -m state --state NEW -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT 5 -m state --state NEW -p tcp --dport 443 -j ACCEPT
sudo netfilter-persistent save
②CloudflareのProxyをOFFにする
CloudflareのAレコードがオレンジ☁️(Proxy)になっていると、Let's EncryptがCloudflareのIPにアクセスしてしまい認証に失敗します。certbot実行前にDNS onlyのグレー🌥️に変更してください。
sudo certbot --nginx -d corota.pp.ua
取得後はCloudflareのプロキシを戻してもOKです。
STEP 8|APIキー認証プロキシを追加
shimmyはデフォルトで認証なしのため、FastAPIで簡易プロキシを作ってAPIキー認証を追加しました。ストリーミング・キャンセル対応済みです。
pip3 install fastapi uvicorn httpx --break-system-packages
cat > ~/shimmy-proxy.py << 'EOF'
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, StreamingResponse
import httpx
import asyncio
app = FastAPI()
API_KEY = "your-api-key-here" # ここを変更
async def check_key(request: Request):
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer ") or auth[7:] != API_KEY:
return False
return True
@app.post("/v1/chat/completions")
async def proxy(request: Request):
if not await check_key(request):
return JSONResponse({"error": "Unauthorized"}, status_code=401)
body = await request.json()
is_stream = body.get("stream", False)
if is_stream:
async def generate():
try:
async with httpx.AsyncClient(timeout=300) as client:
async with client.stream("POST", "http://127.0.0.1:11435/v1/chat/completions", json=body) as res:
async for chunk in res.aiter_bytes():
if await request.is_disconnected():
break
yield chunk
except asyncio.CancelledError:
pass
except Exception:
pass
return StreamingResponse(generate(), media_type="text/event-stream")
else:
try:
async with httpx.AsyncClient(timeout=300) as client:
res = await client.post("http://127.0.0.1:11435/v1/chat/completions", json=body)
return JSONResponse(res.json())
except asyncio.CancelledError:
return JSONResponse({"error": "Cancelled"}, status_code=499)
@app.get("/v1/models")
async def models(request: Request):
if not await check_key(request):
return JSONResponse({"error": "Unauthorized"}, status_code=401)
async with httpx.AsyncClient(timeout=30) as client:
res = await client.get("http://127.0.0.1:11435/v1/models")
return JSONResponse(res.json())
EOF
systemdに登録:
sudo tee /etc/systemd/system/shimmy-proxy.service > /dev/null << 'EOF'
[Unit]
Description=Shimmy Proxy
After=network.target shimmy.service
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu
ExecStart=/home/ubuntu/.local/bin/uvicorn shimmy-proxy:app --host 127.0.0.1 --port 11436
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable shimmy-proxy
sudo systemctl start shimmy-proxy
完成した構成
外部クライアント
↓ HTTPS (443)
nginx(SSL終端)
↓ HTTP
shimmy-proxy(FastAPI / ポート11436)
↓ 認証チェック
shimmy(LLMサーバー / ポート11435)
APIの使い方
ベースURL: https://your-domain/v1
APIキー: Bearer your-api-key-here
モデル名: hy-mt2-1.8b-q4-k-m
OpenAI互換なのでOpenAIのSDKやライブラリがそのまま使えます。
ハマりポイントまとめ
- ARMアーキテクチャ → x86バイナリは動かない。Rustでビルドが必要
- clangヘッダー不足 →
libclang-devを追加インストールで解決 - iptablesのルール順序 → REJECTより前にACCEPTルールを入れる必要がある
- CloudflareのProxy → certbot実行前はDNS onlyに切り替える
- shimmyはデフォルトでストリーミング → プロキシで
stream: falseを適切にハンドリングする - Hy-MT2はシステムプロンプト非対応 → モデルのREADMEに明記されている。userメッセージに翻訳指示を入れる
モデルについて
Hy-MT2はTencent製の翻訳特化モデルです。汎用チャットには向いていませんが、33言語対応の翻訳精度は高いです。
プロンプト例:
Translate the following text into Japanese. Note that you should only output the translated result without any additional explanation:
{翻訳したいテキスト}
OCI Always Free + shimmy + Let's Encrypt で完全無料のLLM APIサーバーが作れました。
コメント
コメントを投稿