Enable auth-service/api-gateway on production, build their images in CI

This commit is contained in:
2026-08-18 14:11:00 +07:00
parent e5afedfa2f
commit b68005be1c
70 changed files with 6781 additions and 263 deletions
+62 -67
View File
@@ -12,25 +12,30 @@ The former numbered `0029` material and historical plans are retained in
there until reviewed. See the canonical
[documentation policy](docs/documentation-policy.md) for source precedence.
> **Status** (2026-08-11): **live in production at
> [realvuxbaro.me](https://realvuxbaro.me)** — a real RAG chatbot over the
> whole formulary, not a scaffold. What exists and what does not:
> **Status** (2026-08-18): **live in production at
> [realvuxbaro.me](https://realvuxbaro.me)**, running on **k3s + ArgoCD**
> since the 2026-08-17 cutover — a real RAG chatbot over the whole
> formulary, not a scaffold. What exists and what does not:
>
> | Part | State |
> |---|---|
> | `ingestion/` | Done — 15,100 chunks embedded and loaded into Qdrant `duocthu_v1` |
> | `apps/ai-service/` | Done — live grounded RAG (retrieval, generation, grounding, abstention, citations, traces) |
> | `apps/web/` | Done — chat UI with citation/evidence panel |
> | `apps/api-gateway`, `auth-service`, `user-service`, `chat-service` | **Not built** — `README.md` + `package.json` only |
> | `apps/web/` | Done — chat UI with citation/evidence panel, optional login |
> | `apps/auth-service`, `apps/api-gateway` | **Built** (real NestJS: register/login/JWT, `/auth/*` proxy) but **disabled by default** in the Helm chart — not yet live on production. Seed accounts (`admin`/`demo`) use placeholder passwords not safe to expose publicly as-is |
> | `apps/user-service`, `apps/chat-service` | **Not built** — `README.md` + `package.json` only |
> | `apps/mobile/` | **Not built** — reserved |
> | `infra/docker/` | Done — production runs Compose, including the Prometheus/Grafana/Tempo observability overlay |
> | `infra/helm/medical-chatbot/` | Built and validated as an offline migration kit; **not deployed** to Docker Desktop, k3s or ArgoCD |
> | `infra/k8s`, `terraform`, `argocd` | **Not built yet** — still the target (ADR 0002), not abandoned: the plan is the team's self-hosted Gitea + ArgoCD; the current EC2/Compose setup is an interim stopgap |
> | `infra/docker/` | Compose is **stopped** (was production through 2026-08-17); still used for local dev (Postgres/Qdrant/observability), not for deploying anywhere |
> | `infra/helm/medical-chatbot/` | **This is production.** Two ArgoCD Applications, `medical-chatbot-app` and `medical-chatbot-data`, both tracked in `infra/argocd/applications/` |
> | Team's Gitea + ArgoCD (ADR 0002) | **Still not started**, not abandoned the personal k3s/ArgoCD instance above is a separate, owner-operated cluster, not the team's shared infrastructure |
>
> Because the gateway and auth services do not exist, `apps/web` talks
> **directly** to `apps/ai-service`; there is no authentication layer. See
> `apps/web`'s chat/history/feedback/sections routes still call
> `apps/ai-service` **directly** — auth-service/api-gateway exist but chat
> traffic doesn't route through the gateway yet. Login is optional and only
> gates `/admin`; anonymous chat is unaffected. See
> [canonical architecture document](docs/architecture.md) for the implemented
> topology and the explicit status of current, scaffolded and target components.
> topology (not yet updated for this — verify against code, not that doc,
> until it is).
## Directory map
@@ -56,8 +61,9 @@ docs-legacy/ raw notes, historical plans and ADRs pending review
## Prerequisites
- Node.js + pnpm (JS workspace: `apps/web`, `packages/*`; the NestJS service
directories are unbuilt placeholders)
- Node.js + pnpm (JS workspace: `apps/web`, `apps/auth-service`,
`apps/api-gateway`, `packages/*`; `apps/user-service`/`apps/chat-service`
are still unbuilt placeholders)
- Python 3.11+ (`apps/ai-service`, `ingestion`)
- Docker (local Postgres + Qdrant via `infra/docker/docker-compose.yml`)
- AWS credentials with Bedrock invoke permission, for anything that generates
@@ -143,62 +149,51 @@ Local endpoints:
| Tempo | `http://localhost:3200` | Trace backend; normally queried through Grafana |
| ai-service metrics | `http://localhost:8079/metrics` | Raw OpenMetrics output when ai-service runs on port 8079 |
For the existing EC2 Compose deployment, the optional overlay is
`infra/docker/docker-compose.observability.yml`. It leaves
`docker-compose.prod.yml` unchanged. A deployment, when explicitly approved,
uses both files:
```powershell
docker compose `
-f infra/docker/docker-compose.prod.yml `
-f infra/docker/docker-compose.observability.yml `
up -d
```
Grafana is available directly through Caddy and the existing production TLS
certificate at `https://realvuxbaro.me/grafana/`. Anonymous access is disabled;
sign in with the Grafana admin account. A dedicated
`grafana.realvuxbaro.me` hostname can replace this path after its Namecheap A
record exists.
Grafana and Prometheus are also bound to EC2 loopback only. This keeps both SSH
fallbacks available without exposing their native ports to the Internet:
```powershell
ssh `
-L 3002:127.0.0.1:3002 `
-L 9090:127.0.0.1:9090 `
<ssh-user>@52.0.158.61
```
Keep that session open and use `http://localhost:3002/grafana/` for Grafana or
`http://localhost:9090` for the raw Prometheus UI. The same Grafana account is
used through both the public HTTPS path and the SSH tunnel. The production
password lives in the GitHub Actions secret `GRAFANA_ADMIN_PASSWORD`; do not use
the Compose fallback password in production.
Prometheus intentionally has no public URL. Normally use
**Grafana -> Explore -> Prometheus**; use its SSH tunnel only for low-level
target or PromQL diagnostics. To investigate a slow request, open the
request-latency panel, follow its exemplar/trace link, or paste the returned
`X-Trace-ID` into **Explore -> Tempo**.
Grafana is served through the same k3s ingress as the app, at
`https://realvuxbaro.me/grafana/`. Anonymous access is on but demoted to
**Viewer** (dashboards load with no login; write actions need the admin
account). Prometheus has no public URL — use **Grafana -> Explore ->
Prometheus** for PromQL, or **Explore -> Tempo** with a returned
`X-Trace-ID` to investigate a slow request.
## Production
Live at [realvuxbaro.me](https://realvuxbaro.me): a single EC2 `t3.large`
running `infra/docker/docker-compose.prod.yml` (postgres, qdrant, ai-service,
web, Caddy for automatic Let's Encrypt TLS). Bedrock is reached through an IAM
instance role — there are no long-lived AWS keys on the box or in any env file.
Live at [realvuxbaro.me](https://realvuxbaro.me), running on **k3s +
ArgoCD** (a personal, owner-operated cluster — not the team's shared
infrastructure) since the 2026-08-17 cutover. Two ArgoCD Applications:
`medical-chatbot-app` (ai-service, web, observability) and
`medical-chatbot-data` (PostgreSQL, Qdrant — a separate release so an app
redeploy or prune can never touch persistent data). Both are tracked in
`infra/argocd/applications/`; `infra/helm/medical-chatbot/` is the chart both
render from. Bedrock is reached through an IAM instance role — no long-lived
AWS keys on the box or in any manifest.
Pushing to `master` deploys: `.github/workflows/deploy.yml` SSHes in, resets to
the pushed commit, rebuilds `ai-service`/`web`, reconciles the observability
containers, reloads Caddy, runs migrations and verifies health, metrics and an
exact request trace. Postgres and Qdrant data survive deploys because they live
in named volumes rather than the containers.
Two things deploy independently:
This is **interim infrastructure**, not the end state. The intended target is
still the team's self-hosted **Gitea** (company domain) plus their **ArgoCD**
instance, per `docs-legacy/adr/0002-argocd-gitops.md` — that work is *not started*,
not cancelled. Until it is deliberately started, the project stays on private
GitHub, and the team's existing `git.vinmec.tech/ai-team/gitops` repository is
reference-only: never push this project into it.
- **App code** (`apps/ai-service/**`, `apps/web/**`, `packages/**`) — a push
to `master` triggers `.github/workflows/build-practice-images.yml`, which
builds and pushes GHCR images tagged by commit SHA, then repoints
`medical-chatbot-app` at the new tag. `ci.yml` runs in parallel and does
**not** gate this — a red test suite does not block a deploy.
- **Chart/config** (`infra/helm/**`) — `helm-chart.yml` lints and asserts
render invariants on the PR; once merged, ArgoCD's own `selfHeal` picks up
the change automatically. No CI step applies it directly.
Rollback is `.github/workflows/rollback-k3s.yml` (`workflow_dispatch`,
`target_sha`) — see `docs/operations.md` for the full runbook, including its
current gaps (no automated rollback for a config-only change, and the
Grafana admin password still lives inline on the ArgoCD Application rather
than in a real Kubernetes Secret).
The former EC2 Docker Compose deployment (`i-039fc8f6102467a54`) is
**stopped**, not deleted — see `docs/operations.md` if it's ever needed as a
manual DNS fallback again, though its corpus/schema will drift further out
of date the longer it stays off.
The team's self-hosted **Gitea** + **ArgoCD** (per
`docs-legacy/adr/0002-argocd-gitops.md`) remains the longer-term target for
this project and is **still not started** — not abandoned, just a separate
decision from the personal-cluster cutover above. Until it is deliberately
started, the project stays on private GitHub, and the team's existing
`git.vinmec.tech/ai-team/gitops` repository is reference-only: never push
this project into it.