私は2025年から複数の LLM API を本番環境で運用しています。最初は公式エンドポイントを直叩きする素朴な構成で動かしていましたが、トラフィックが増えるにつれ「レート制限・キー管理・コスト可視化・監査ログ」の 4 要件が同時に押し寄せてきました。本記事では、HolySheep AI の OpenAI 互換エンドポイントを背後に置き、Nginx + Lua(OpenResty)で軽量 API ゲートウェイを自前構築する手順を解説します。

📊 一目で分かる比較:HolySheep vs 公式API vs 他のリレーサービス

項目 HolySheep AI OpenAI / Anthropic 公式 他の中継サービス
為替レート ¥1 = $1(固定) ¥7.3 = $1 ¥6.8〜¥7.2
GPT-4.1 出力 (/MTok) $8.00 $8.00 $9.50 前後
Claude Sonnet 4.5 出力 $15.00 $15.00 $17.50 前後
Gemini 2.5 Flash 出力 $2.50 $2.50 $3.20 前後
DeepSeek V3.2 出力 $0.42 $0.42 (海外) $0.60 前後
レイテンシ p50 (TTFB) 42ms 147ms 112ms
決済手段 WeChat Pay / Alipay / カード クレジットカードのみ サービスによる
無料クレジット 登録で付与 なし 限定的にあり
OpenAI 互換 API 完全対応 ネイティブ 一部対応
コスト削減率 (vs 公式) 85% 節約 基準 10〜30% 程度

※ 2026年1月時点の実勢価格および東京リージョンから 200 リクエストを 3 回計測した p50 中央値(実測)

🏗️ なぜ自前で API ゲートウェイを立てるのか

公式エンドポイントを直叩きする方式には、以下の限界があります。

私自身、最初の頃は API キー 1 枚で本番運用していた結果、想定の 3 倍の請求が来てしまった苦い経験があります。ゲートウェイを 1 段挟むだけで、これらの課題は 9 割方解決します。

🧩 アーキテクチャ概要

本記事の構成は次のとおりです。

クライアント (Web/モバイル)
        │  HTTPS
        ▼
[ Nginx + Lua ゲートウェイ ]  ── 認証 / レート制限 / ログ
        │  HTTPS (Bearer: YOUR_HOLYSHEEP_API_KEY)
        ▼
   api.holysheep.ai/v1
        │
        ▼
   GPT-4.1 / Claude Sonnet 4.5 / Gemini 2.5 Flash / DeepSeek V3.2 ...

Nginx 1 台で認証・レート制限・ログ転送・プロキシを完結させるので、Kong や Envoy のような重量級ミドルウェアを別途立てる必要はありません。OpenResty のパッケージを入れるだけで、Lua によるフック処理が動きます。

🛠 Step 1: OpenResty のセットアップ

Debian / Ubuntu 系での最小構成です。

# 1. 必要パッケージのインストール
sudo apt update
sudo apt install -y --no-install-recommends wget gnupg ca-certificates

2. OpenResty の公式リポジトリを追加

wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg echo "deb [signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list

3. インストール

sudo apt update sudo apt install -y openresty

4. バージョン確認

openresty -v # -> openresty/1.25.3.1 等

CentOS / RHEL の場合は公式 yum リポジトリ、Amazon Linux 2023 の場合は dnf + openresty リポジトリで同様の手順になります。

🔧 Step 2: Nginx + Lua プロキシ設定

本体となる nginx.conf です。Lua の共有辞書で API キーを管理し、配下に透過プロキシを置きます。

worker_processes auto;
error_log /var/log/openresty/error.log warn;
events { worker_connections 4096; }

http {
    # Lua 共有辞書 (10MB = 推定10万リクエスト/分のレート追跡)
    lua_shared_dict rate_limit 10m;
    lua_shared_dict team_quota 1m;

    # 起動時にチーム別 API キーを登録
    init_by_lua_block {
        local quota = ngx.shared.team_quota
        quota:set("team-a", 1000)  -- 1000 req/min
        quota:set("team-b", 500)
        quota:set("team-c", 200)
    }

    # 共通のリクエストログ (JSON 1行)
    log_format json_log escape=json '{'
        '"ts":"$time_iso8601",'
        '"client":"$remote_addr",'
        '"req_id":"$request_id",'
        '"method":"$request_method",'
        '"path":"$request_uri",'
        '"status":$status,'
        '"rt":$request_time'
    '}';

    access_log /var/log/openresty/access.log json_log;

    server {
        listen 8080 ssl;
        server_name gateway.example.com;

        ssl_certificate     /etc/letsencrypt/live/gateway.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/gateway.example.com/privkey.pem;

        # 共通: ヘルスチェック
        location = /healthz {
            return 200 '{"ok":true}';
        }

        # 本体: 透過プロキシ
        location /v1/ {
            access_by_lua_file /etc/openresty/lua/auth.lua;

            proxy_pass https://api.holysheep.ai/v1/;
            proxy_http_version 1.1;
            proxy_set_header Host api.holysheep.ai;
            proxy_set_header Authorization "Bearer YOUR_HOLYSHEEP_API_KEY";
            proxy_set_header Content-Type "application/json";
            proxy_ssl_server_name on;
            proxy_connect_timeout 3s;
            proxy_read_timeout 60s;

            # 元のクライアント IP を上流に伝える
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

⚖ Step 3: レート制限 Lua スクリプト

/etc/openresty/lua/auth.lua に置きます。チームごとに 1 分あたりのリクエスト上限を適用し、超過時は 429 を返します。

-- 1. クライアントから提示された API キーを取り出す
local client_key = ngx.var.http_x_api_key
if not client_key then
    ngx.status = 401
    ngx.header["Content-Type"] = "application/json"
    ngx.say('{"error":{"message":"X-Api-Key header required","type":"auth_error"}}')
    return ngx.exit(401)
end

-- 2. チーム ID をキーから引く (本来は DB / Redis などから)
local team_id
if client_key:sub(1, 7) == "team-a-" then team_id = "team-a"
elseif client_key:sub(1, 7) == "team-b-" then team_id = "team-b"
elseif client_key:sub(1, 7) == "team-c-" then team_id = "team-c"
else
    ngx.status = 403
    ngx.say('{"error":{"message":"unknown team key","type":"auth_error"}}')
    return ngx.exit(403)
end

-- 3. 共有辞書でレート制限 (Token Bucket 風)
local quota = ngx.shared.team_quota
local limit = quota:get(team_id) or 60  -- デフォルト 60 req/min
local bucket = ngx.shared.rate_limit
local counter_key = team_id .. ":" .. os.time() // 60
local current = bucket:incr(counter_key, 1, 0, 60)

if current > limit then
    ngx.status = 429
    ngx.header["Retry-After"] = 60
    ngx.header["X-RateLimit-Limit"] = limit
    ngx.say(string.format('{"error":{"message":"rate limit exceeded for %s","limit":%d}}', team_id, limit))
    return ngx.exit(429)
end

ngx.header["X-RateLimit-Limit"]  = limit
ngx.header["X-RateLimit-Remaining"] = math.max(0, limit - current)
ngx.req.set_header("X-Team-Id", team_id)

🐍 Step 4: Python クライアントからの呼び出し

OpenAI 公式 SDK がそのまま使えるので、自前のラッパーは不要です。base_url を自前ゲートウェイに向けるだけです。

# pip install openai==1.54.0
from openai import OpenAI

ゲートウェイを指す

client = OpenAI( base_url="https://gateway.example.com/v1", api_key="team-a-prod-xxxxxxxx" # クライアントが持つのはゲートウェイ用キー ) resp = client.chat.completions.create( model="g