sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# Stack trace for `docker-compose.prod.yml` deployment
|
||||
|
||||
Use this doc to see **how the frontend, backend, Postgres, and MinIO connect**, in what **order Compose starts services**, and how to **verify** each tier after deploy. For Postgres volume errors, HTTP vs HTTPS on Vite, and `.env` dotfiles, see [deploy-production-docker.md](./deploy-production-docker.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. High-level dependency graph
|
||||
|
||||
Compose service names (`postgres`, `minio`, etc.) resolve on the **`profyt-net`** bridge (`10.5.0.0/16`). Static IPs are defined in `docker-compose.prod.yml` for readability; traffic still uses DNS names **`be0`**, **`minio`**, **`postgres`**.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph browser [Browser on the Internet]
|
||||
U[User]
|
||||
end
|
||||
|
||||
subgraph host [Docker host ports]
|
||||
FEPORT["HOST:FE_PORT ~ Vite HTTP"]
|
||||
MINAPI["HOST:MINIO_API_PORT"]
|
||||
MINUI["HOST:MINIO_CONSOLE_PORT"]
|
||||
end
|
||||
|
||||
subgraph net [Compose network profyt-net]
|
||||
fe0["fe0 :8080<br/>nginx (prod)"]
|
||||
be0["be0 :4402<br/>FastAPI"]
|
||||
pg[("postgres :5432")]
|
||||
s3["minio :9000 API<br/>:9001 console"]
|
||||
end
|
||||
|
||||
U -->|"http://PUBLIC_HOST:FE_PORT (not https)"| FEPORT --> fe0
|
||||
U -->|"presigned GET bytes"| MINAPI --> s3
|
||||
|
||||
fe0 -->|"nginx proxy /api, /submitted-initiatives…"| be0
|
||||
be0 -->|"INITIATIVE_DATABASE_URL"| pg
|
||||
be0 -->|"S3_ENDPOINT_URL presign + server ops"| s3
|
||||
|
||||
subgraph oneoff [runs after MinIO healthy]
|
||||
cors["minio-cors:<br/>ensure buckets"]
|
||||
end
|
||||
|
||||
cors --> s3
|
||||
|
||||
subgraph localhost_only [Reachable only on the host VM]
|
||||
dbmap["127.0.0.1:15432 → postgres"]
|
||||
bemap["127.0.0.1:4402 → be0"]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Responsibility matrix
|
||||
|
||||
| Piece | Compose service | Listen (container) | Published to Internet? | Env / config that ties it together |
|
||||
|--------|----------------|----------------------|-------------------------|-----------------------------------|
|
||||
| **Frontend** | `fe0` | `0.0.0.0:8080` | **`http://${PUBLIC_HOST}:${FE_PORT}`** maps host → container 8080 | **Production:** `Dockerfile.prod` → nginx static + proxy `/api` → `be0`. **Dev:** Vite (`Dockerfile` + `npm run dev`) |
|
||||
| **Backend API** | `be0` | `0.0.0.0:4402` | **No** — `127.0.0.1:4402` on host only; browsers use **`fe0` proxy** `/api`, etc. | `INITIATIVE_DATABASE_URL=postgresql+asyncpg://…@postgres:5432/…`, `S3_ENDPOINT_URL=http://minio:9000`, **`S3_PUBLIC_ENDPOINT_URL`** defaults to **`http://${PUBLIC_HOST}:${MINIO_API_PORT}`** — set **`https://…`** when MinIO sits behind HTTPS for presigned URLs ([minio-behind-https.md](./minio-behind-https.md)) |
|
||||
| **Database** | `postgres` | `:5432` | **No** — `127.0.0.1:15432` on host for admin tools only | `POSTGRES_*` first init only (`initiative_pg_data` volume) |
|
||||
| **Object storage** | `minio` | API `:9000`, console `:9001` | **`http://${PUBLIC_HOST}:${MINIO_API_PORT}`** (often proxied via HTTPS in production — see linked doc above) **`MINIO_CONSOLE_PORT`** | **`MINIO_SERVER_URL`** / **`MINIO_BROWSER_REDIRECT_URL`** compile from **`PUBLIC_HOST`** by default or use `.env` HTTPS overrides together with **`S3_PUBLIC_ENDPOINT_URL`** |
|
||||
|
||||
**`minio-cors`** is a one-shot job: waits for healthy MinIO and creates **`initiative-attachments`**, **`initiative-exports`**, and **`initiative-quarantine`**. **Community MinIO** does not implement S3 per-bucket CORS (`mc cors set`); browsers rely on **`MINIO_API_CORS_ALLOW_ORIGIN`** on the **`minio`** service (defaults to `*` in Compose) for presigned GETs.
|
||||
|
||||
---
|
||||
|
||||
## 3. Request paths (mental trace)
|
||||
|
||||
1. **SPA + API calls**
|
||||
User opens **`http://PUBLIC_HOST:FE_PORT`**. The browser loads Vite-served assets from **`fe0`**. Calls to **`/api/...`** (and similar proxied paths) go to **`fe0`**, which forwards to **`http://be0:4402`** inside the network.
|
||||
|
||||
2. **Presigned S3 / MinIO from the browser**
|
||||
**`be0`** builds URLs using **`S3_PUBLIC_ENDPOINT_URL`** (must be reachable from the user’s browser, usually **`http://PUBLIC_HOST:MINIO_API_PORT`**). The browser downloads objects **directly from MinIO** on the host-published port—not through **`be0`**.
|
||||
|
||||
3. **Backend → Postgres**
|
||||
Only **`be0`** uses **`INITIATIVE_DATABASE_URL`**; host `127.0.0.1:15432` is optional for **`psql`** / dumps from the VPS shell.
|
||||
|
||||
4. **Backend → MinIO (server-side)**
|
||||
**`be0`** uses **`S3_ENDPOINT_URL=http://minio:9000`** for signing and internal API traffic; **`minio`** is the Compose DNS name, not **`PUBLIC_HOST`**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Startup order Compose enforces
|
||||
|
||||
| Order | Service | Blocking condition |
|
||||
|------|---------|--------------------|
|
||||
| 1 | `postgres`, `minio` | (none in compose—they start in parallel.) |
|
||||
| 2 | `minio-cors` | `minio` **healthy** |
|
||||
| 3 | `be0` | `postgres` **healthy** AND `minio` **healthy** |
|
||||
| 4 | `fe0` | `be0` **started** |
|
||||
|
||||
If Postgres never becomes healthy (**bad `POSTGRES_*` vs existing volume**, etc.), **`be0` never attaches** cleanly and **`fe0`** may misbehave or appear “up” while API calls fail.
|
||||
|
||||
---
|
||||
|
||||
## 5. Deploy checklist (recommended)
|
||||
|
||||
From the **repository root on the VPS** (same folder as `docker-compose.prod.yml`):
|
||||
|
||||
1. **`.env`** present (`ls -a`), values filled from `.env.example`.
|
||||
2. **`PUBLIC_HOST`** = the hostname or IP users type in the browser (must match how you open the UI and how MinIO URLs are generated).
|
||||
3. **`./scripts/verify-prod-env.sh`** exits `0`.
|
||||
4. Start the stack (pick one):
|
||||
- **Script (pull, build, detached):** `./scripts/deploy-prod.sh`
|
||||
- **Manual compose:** see **subsection 5.1** below.
|
||||
5. Open app with **`http://`**, not **`https://`**, unless you put a reverse proxy in front.
|
||||
|
||||
### 5.1 Manual `docker compose -f docker-compose.prod.yml up`
|
||||
|
||||
This is valid as long as you stay in the **repo root** and a **`.env`** file exists there.
|
||||
|
||||
- **Variable substitution:** Compose automatically reads a file named **`.env`** in the **project directory** (normally your current working directory) and uses it to expand `${PUBLIC_HOST}`, `${FE_PORT}`, etc. in `docker-compose.prod.yml`. You do **not** have to pass `--env-file .env` for that to work, but being explicit avoids surprises:
|
||||
```bash
|
||||
docker compose --env-file .env -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
- **Foreground vs daemon:** plain `up` streams logs in the terminal and exits with Ctrl+C (containers stop unless you use `--abort-on-container-exit` behavior—default stops on interrupt). For a long-running server, prefer **`up -d`** (detached).
|
||||
- **Rebuild after Dockerfile or dependency changes:** add **`--build`** (the deploy script always builds). Without it, Compose may reuse old images.
|
||||
- **No pre-checks:** the script runs `verify-prod-env.sh` and `compose config` for you; if you use only `up`, run **`./scripts/verify-prod-env.sh`** yourself first so bad `POSTGRES_USER` / empty ports fail fast.
|
||||
|
||||
Example minimal manual flow:
|
||||
|
||||
```bash
|
||||
cd /path/to/remix-of-my-perspective-lifestyle-32
|
||||
./scripts/verify-prod-env.sh
|
||||
docker compose --env-file .env -f docker-compose.prod.yml up -d --build
|
||||
docker compose --env-file .env -f docker-compose.prod.yml ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Quick verification commands
|
||||
|
||||
Run on the host with the same `--env-file` you use for deploy:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env -f docker-compose.prod.yml ps
|
||||
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=80 postgres
|
||||
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=80 be0
|
||||
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=40 fe0
|
||||
docker compose --env-file .env -f docker-compose.prod.yml logs --tail=40 minio
|
||||
```
|
||||
|
||||
Smoke checks:
|
||||
|
||||
- **Postgres**: `docker compose ... exec postgres pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"` (substitute real values from `.env` when using shell snippets).
|
||||
- **Backend** (from host): `curl -sS http://127.0.0.1:4402/docs` — expect FastAPI Swagger HTML (or `/openapi.json`).
|
||||
- **MinIO** (from host or laptop if firewall allows): `curl -sS -o /dev/null -w "%{http_code}" http://${PUBLIC_HOST}:${MINIO_API_PORT}/minio/health/live`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Firewall hint
|
||||
|
||||
Typically you must allow **inbound TCP**: **`FE_PORT`**, **`MINIO_API_PORT`**, **`MINIO_CONSOLE_PORT`** (and **`22`** for SSH). Postgres and **`be0`** intentionally stay on **localhost-only** binds in this compose file.
|
||||
Reference in New Issue
Block a user