1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自宅GPUをスマホから叩く — Home Nerve × Cloudflare Tunnel(1033/530切り分け実録)

1
Posted at

これが俺のとっておき — 3層の「自宅要塞」

クラウド推論は便利だが、自分の LoRA・ログ・GPU は自宅に置きたい。一方でスマホや外出先から触りたい。Render だけでは足りない。

俺のとっておきは Home Nerve という FastAPI ゲートウェイだ。

役割 典型ポート
Ollama 推論本体 :11434
Home Nerve 認証・レート制限・WebSocket 中継 :11436
cloudflared 自宅 → 公開 hostname へ Tunnel

Ollama を 0.0.0.0 で晒さない。Nerve が API キーを要求し、必要なら IP 許可リストとレート制限をかける。Tunnel は 11436 だけ を外に出す。自宅回線の IP 変動は Tunnel 側が吸収するので、DynDNS より運用が楽なことが多い。

# cloudflared config.yml(抜粋)
tunnel: xeno-home-nerve
ingress:
  - hostname: nerve.yourdomain.com
    service: http://127.0.0.1:11436
  - service: http_status:404

症状 — HTTP は 200 なのに WebSocket だけ死ぬ

Mobile IDE やストリームクライアントから wss://nerve.yourdomain.com/ws/xeno-stream に繋ぐと、UI は開くのにリアルタイムだけ止まる。

WebSocket connection failed
Error 1033: Cloudflare Tunnel error
HTTP 530 Origin connection refused

ブラウザ Console では 530、Cloudflare 画面では 1033 が出ることが多い。フロントの JS バグではなく、Tunnel 層 + オリジン層 の複合障害が大半だ。

切り分け表 — 1033 / 530 / 401

コード 意味 最初に見る場所
1033 Tunnel 未起動 / 設定ミス / 認証切れ cloudflared プロセス、config.yml
530 Origin ダウン / ポート未リッスン uvicorn / Home Nerve
401 / 1008 API キー不一致 クライアントとサーバーの環境変数

切り分けの順序は固定する。1033 → 530 → 401。この順を崩すと、キーを直しても Tunnel が死んでいるので永遠に繋がらない。

Home Nerve の最小設計

xeno_home_nerve_server.py は Ollama への 認証付きプロキシMobile IDE WebSocket ハブ だ。

@app.middleware("http")
async def _guard(request: Request, call_next):
    _check_ip(request)
    _rate_limit(request)
    key = _extract_key(request, request.headers.get("X-Xeno-Home-Key"), request.headers.get("Authorization"))
    if request.url.path not in {"/health", "/mobile-ide"}:
        _verify_key(key)
    return await call_next(request)

WebSocket は Query か Header でキーを渡す。

@app.websocket("/ws/xeno-stream")
async def mobile_ws(websocket: WebSocket):
    key = websocket.query_params.get("api_key") or websocket.headers.get("X-Xeno-Home-Key")
    if not _verify_ws_key(key):
        await websocket.close(code=1008)
        return
    await websocket.accept()
    # code_edit / ping / job_done を非同期で処理

スマホから code_edit を送ると、自宅 PC 上の IDE + Ollama が動く。HTTP 同期でやるとタイムアウトするので WebSocket + asyncio.create_task が必須。

起動手順(自宅側)

# 1. API キー生成(32byte hex など)
export XENO_HOME_NERVE_API_KEY="$(openssl rand -hex 32)"
export OLLAMA_BASE_URL=http://127.0.0.1:11434

# 2. Nerve 起動
./start_home_nerve.sh
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11436/health
# 期待: 200

# 3. Tunnel 起動
./tools/start_nerve_tunnel.sh
# または systemd: systemctl --user restart cloudflared-su-desk

Render 側(クラウド Worker)から叩くときは環境変数を揃える。

XENO_HOME_NERVE_URL=https://nerve.yourdomain.com
XENO_HOME_NERVE_API_KEY=<自宅と同一>
XENO_OLLAMA_TRANSPORT=remote

検証手順

  1. ローカル HTTPcurl http://127.0.0.1:11436/health が 200、ollama_ok: true
  2. Tunnel 経由 HTTPcurl https://nerve.yourdomain.com/health が 200(1033 なら cloudflared を見る)
  3. WebSocket Upgradecurl -i -N -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' 'https://nerve.yourdomain.com/ws/xeno-stream?api_key=YOUR_KEY'101 Switching Protocols
  4. 意図的障害cloudflared を止めて 1033 を再現 → 再起動で解消
  5. キー不一致 — わざと wrong key → 1008 / 401 を確認してから本番キーに戻す

ログに MOBILE_IDE_WS_CONNECTED / MOBILE_IDE_JOB_OK が出れば、Tunnel + Nerve + IDE の3点が通っている。

よくある穴

  • Ollama だけ 0.0.0.0 公開 — Nerve をバイパスすると API キーが効かない。必ず 11436 経由
  • Tunnel だけ再起動 — Origin(uvicorn)が古い PID のまま 530 が残る。Nerve → Tunnel の順で再起動
  • LAN 用 ws://192.168.x.x を本番に混在 — PWA が LAN URL をキャッシュすると外出先で必ず死ぬ。本番は wss:// 固定
  • API キーを Query と Header で二重定義し片方だけ更新 — クライアント実装ごとに渡し方が違う。ドキュメント化しないと必ずズレる
  • WebSocket を同期処理code_edit を await したまま次の ping を待つと切断。asyncio.create_task でジョブを逃がす
  • in-memory レート制限だけ — 複数 Worker プロセスだと穴が開く。単一プロセス前提なら OK、増やすなら Redis 等

Render × 自宅のハイブリッド運用

Web サービス(Shop / API)は Render。推論は XENO_OLLAMA_TRANSPORT=remote で Home Nerve に逃がす。Tunnel が落ちるとクラoud 側は 502 になるが、自宅 PC が再起動すれば復旧する。完全 SaaS ではないが、GPU とデータ主権を手元に残せる。

Worker の wakeup loop(note / Zenn / Qiita 定時投稿)と Nerve は別プロセスだが、同じ .env 思想 — 秘密は環境変数、状態は JSON、投稿系統は分離 — で運用している。

まとめ

  • Home Nerve = 自宅 Ollama の門番 + Mobile WebSocket ハブ
  • 1033 は Tunnel、530 は Origin、401/1008 はキー — この順で潰す
  • wss:// + API キー + 非同期ジョブが、スマホから自宅 GPU を触る最小セット
  • 商品リンクではなく インフラ実録。Tunnel が通った瞬間、自宅マシンが「自分専用の推論リージョン」になる
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?