Files
playchat/docs/deploy-lightsail.md
T
colcaroliandClaude Opus 5 1b88d23f21 Set the OpenAI-compatible endpoint from the settings panel
Pointing OPENAI_BASE_URL at a local llama-server or Ollama sends every gpt-*
call there instead of to OpenAI, and litellm reports that as a bare
"Connection Error" — which reads like a network problem even though the host
can reach api.openai.com fine. Deploying a .env that still carries the local
endpoint reproduces it on the server, with nothing in the message naming the
culprit.

Add OPENAI_BASE_URL and OPENAI_API_BASE to the settings allowlist so the
endpoint can be corrected without editing .env and recreating the container.
Both are listed because litellm reads OPENAI_API_BASE while the OpenAI SDK
reads OPENAI_BASE_URL, so fixing one can leave the other in effect. Neither is
a secret, so the panel shows the current value.

No cache invalidation is needed alongside this: litellm keys its client cache
on the endpoint, so a changed value takes effect on the next call.

Document the trap where it will be read — a commented template in .env.example
and a note in the Lightsail deploy guide about carrying a local .env over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 23:12:34 +09:00

220 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AWS Lightsail へのデプロイ
このアプリを AWS Lightsail の「インスタンス」(Ubuntu VPS)上で、Docker Compose を使って動かす手順。
SQLite・ローカルナレッジベース・Discord Bot・APScheduler がすべて 1 プロセス(バックエンドコンテナ)内で常駐するため、
Lightsail Containers(マネージドコンテナサービス)よりも、状態を持てる通常のインスタンス構成の方が相性が良い。
## 構成
```
Internet
│ :80 / :443
▼
[caddy コンテナ] Let's Encrypt証明書の自動取得・更新、HTTP→HTTPSリダイレクト
│ :80(コンテナ間通信のみ、ホストには公開しない)
▼
[frontend コンテナ] nginx
├─ 静的ファイル (Vue ビルド成果物) を配信
└─ /chat, /api, /memory, /logs, /health を backend にリバースプロキシ
│
▼
[backend コンテナ] FastAPI (uvicorn)
└─ SQLite (playchat.db) / knowledge_base を Docker Volume に永続化
```
`frontend/src/api/client.ts` はすべて相対パスで API を呼ぶ実装になっているため、
本番でも frontend と backend を同一オリジン(nginx 経由)で配信すれば CORS 設定は事実上不要。
リポジトリには以下を追加済み:
- [backend/Dockerfile](../backend/Dockerfile)
- [frontend/Dockerfile](../frontend/Dockerfile) / [frontend/nginx.conf](../frontend/nginx.conf)
- [docker-compose.yml](../docker-compose.yml) / [Caddyfile](../Caddyfile)
## 1. Lightsail インスタンスの作成
1. Lightsail コンソール → 「インスタンスの作成」
2. プラットフォーム: **Linux/Unix**、設計図: **OS のみ → Ubuntu 22.04 LTS**
3. プラン: **2GB RAM / 1vCPU($10/月)以上を推奨**
(フロントエンドの `npm run build` (vue-tsc) はビルド時にメモリを食うため、1GB プランだと OOM で失敗することがある)
4. インスタンス名を付けて作成
作成後、インスタンス詳細ページの「ネットワーキング」タブで:
- **静的 IP をアタッチ**(再起動してもIPが変わらないようにする。ドメインを使う場合は必須)
- ファイアウォールで **80 (HTTP)** と **443 (HTTPS)** を許可(Caddyの証明書取得とHTTPS配信に両方必要。22/SSH はデフォルトで開いている)
## 2. SSH 接続 & Docker のインストール
Lightsail コンソールの「接続」タブからブラウザ SSH で入るか、ダウンロードした鍵で接続する。
```bash
sudo apt update && sudo apt upgrade -y
# Docker 公式インストールスクリプト
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
# Docker Compose Plugin の確認(get.docker.com で入る場合が多い)
docker compose version
```
## 3. リポジトリの取得と設定
リポジトリは自前の Git サーバー(`colc-git.duckdns.org`、Gitea/Forgejo系)でホストしている。
DuckDNS 経由の公開IPで、HTTPS(443)・SSH(22) とも外部から到達可能なので、Lightsail インスタンスからも
VPN 等は不要で直接クローンできる。認証方法は以下のどちらか。
### 方法A: SSH デプロイキー(推奨・pull専用なら安全)
```bash
ssh-keygen -t ed25519 -C "lightsail-playchat" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub
```
表示された公開鍵を colc-git.duckdns.org 側のリポジトリ設定の **Deploy Keys** に **Read only** で登録してから:
```bash
git clone git@colc-git.duckdns.org:colcaroli/playchat.git
cd playchat
```
### 方法B: HTTPS + アクセストークン
colc-git.duckdns.org 側で個人アクセストークン(PAT)を発行し:
```bash
git clone https://colc-git.duckdns.org/colcaroli/playchat.git
cd playchat
# Username / Password 入力を求められたら、Password 欄に発行したトークンを入力
```
トークンをキャッシュしたい場合は `git config credential.helper cache`(メモリに一時保存。
`store` は平文保存になるため非推奨)。
### .env の設定
```bash
cp backend/.env.example backend/.env
nano backend/.env # APIキーやモデル名など本番用の値を設定
```
`backend/.env` で最低限確認する項目:
| 変数 | 本番での指針 |
|---|---|
| `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` | 使うプロバイダーのキーを設定 |
| `DEFAULT_MODEL` | 使いたいモデル名 |
| `CORS_ORIGINS` | `http://<静的IP>` またはドメイン(nginx が同一オリジンで配信するので基本は素通りするが念のため合わせておく) |
| `DATABASE_URL` | **編集不要**(`docker-compose.yml` が `/app/data/playchat.db` に上書きし、Docker Volume に永続化する) |
| `DISCORD_BOT_TOKEN` 等 | Discord Bot / 通知機能を使うなら設定。不要なら空のままでよい(未設定時は該当機能が無効) |
| `SETTINGS_PASSWORD` | GUIの「設定」画面(後述)を使うなら必ず設定する。未設定だと設定画面は403で拒否される |
`backend/.env` はコンテナに `env_file` として渡され、Git 管理対象外(`.gitignore` 済み)なので直接編集してよい。
**ローカルの `.env` をそのまま持ち込むときの注意:** `OPENAI_BASE_URL` / `OPENAI_API_BASE` を
ローカルの llama-server や Ollama に向けていると、サーバー上では存在しない宛先に接続しに行くため、
`gpt-*` のモデルを選んでも litellm が「Connection Error」になる。サーバーの `.env` からは消しておくこと
(設定画面の「OpenAI 互換エンドポイント」からも上書きできる)。
### GUIからのAPIキー管理
初回起動後は、`OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / AWS認証情報などをSSHで`.env`を編集し直さなくても、
アプリ右上の「設定」ボタン(`SETTINGS_PASSWORD` によるBasic認証で保護)から変更できる。GUIで保存した値は
SQLite(`AppSetting`テーブル、`backend_data`ボリュームで永続化)に保存され、`.env`の値より優先される。
**注意:** このパスワード保護は簡易的なものであり、HTTP平文通信の場合はBasic認証の資格情報がネットワーク上に
そのまま流れてしまう。この機能を使うなら[6. HTTPS化](#6-https-化caddy)と合わせて運用すること。
## 4. ビルド & 起動
```bash
docker compose build
docker compose up -d
docker compose logs -f backend # 起動ログ確認(Ctrl+C で抜ける。プロセスは動き続ける)
```
`docker-compose.yml`にはCaddyが含まれており、80/443番ポートはCaddyが受け持つ(`frontend`は直接公開しない)。
Caddyfileにはドメイン名を設定する前提のため、動作確認は静的IPへの直接アクセスではなく
[6. HTTPS化](#6-https-化caddy)の手順でドメイン経由で行うこと。
停止・再起動・更新の運用コマンド:
```bash
docker compose ps # 状態確認
docker compose restart backend # backend だけ再起動
git pull && docker compose up -d --build # コード更新を反映
```
## 5. 永続化されるデータ
`docker-compose.yml` は以下を Named Volume にしている(`docker compose down` してもデータは消えない。`docker compose down -v` は消えるので注意):
- `backend_data` … `playchat.db`(会話履歴・メモリ)
- `backend_knowledge_base` … `knowledge_base/`(Web検索で蓄積したナレッジ含む)
バックアップ例:
```bash
docker compose exec backend sqlite3 /app/data/playchat.db ".backup /app/data/backup.db"
docker cp $(docker compose ps -q backend):/app/data/backup.db ./playchat-backup-$(date +%F).db
```
インスタンスごとのバックアップとしては、Lightsail の「スナップショット」機能(自動スナップショット設定可)も併用すると安心。
## 6. HTTPS 化(Caddy)
[Caddy](https://caddyserver.com/) をリバースプロキシとして`frontend`の手前に置き、証明書の取得・更新を
自動化する(Let's Encrypt、ACME HTTP-01 チャレンジ)。手動でのcertbot操作は不要。
構成:
```
Internet
│ :80 / :443
▼
[caddy コンテナ] ← Let's Encrypt証明書の自動取得・更新、HTTP→HTTPSリダイレクト
│ :80 (コンテナ間通信、ホストには公開しない)
▼
[frontend コンテナ] nginx(静的配信 + backendへのAPIプロキシ)
▼
[backend コンテナ]
```
`frontend`はもう80番ポートをホストに公開せず(`expose`のみ)、代わりに`caddy`が80/443を受けて
`frontend`へ内部転送する。ルートの [Caddyfile](../Caddyfile) にドメインを設定する:
```
your-domain.example.com {
reverse_proxy frontend:80
}
```
前提条件:
- ドメインの A レコード(IPv4)を Lightsail の静的 IP に向けておく(DuckDNS等の無料動的DNSでも可)
- Lightsail ファイアウォールで **443 (HTTPS)** を許可([1. Lightsail インスタンスの作成](#1-lightsail-インスタンスの作成)で追加済みのはず)
起動すると、Caddyがドメイン名を見て自動的にACMEチャレンジ(ポート80経由)で証明書を取得し、
以後は自動更新される。証明書は`caddy_data`ボリュームに永続化されるので、`docker compose down`
(`-v`無し)では消えない。
```bash
docker compose up -d
docker compose logs -f caddy # 証明書取得ログを確認("certificate obtained successfully" 等)
```
`https://your-domain.example.com/` にアクセスして確認する。
## 7. 常駐タスクについて
APScheduler(天気通知など)と Discord Bot は `backend` コンテナ起動時(FastAPI の `lifespan`)に単一プロセス内で動く。
`restart: unless-stopped` を設定しているため、インスタンス再起動やコンテナクラッシュ時も自動的に立ち上がる。
複数インスタンス/複数コンテナへのスケールアウトは想定していない(SQLite・APScheduler・Discord Bot が単一プロセス前提のため)。
## コスト目安
- Lightsail インスタンス 2GB プラン: 約 $10/月(固定IP・80/443 の転送量込み)
- LLM API 利用料(OpenAI / Anthropic / Bedrock)は別途従量課金