03f1ef20f6
Målarkitekturen ur Master Prompt som kod: - Webben containeriserad (flerstegs-Dockerfile → oprivilegierad nginx med SPA-fallback, hashade assets cachas hårt, säkerhetsheaders). - AI-orkestern som egen tjänst (services/ai-orkester): samma routing och AI-regler som edge-funktionen men körbar som pod — egen HS256-JWT-verifiering (fail closed), /halsa, storleksgränser, read-only container som kör som node-användaren. Livetestad: hälsokontroll ok, 401 utan/med ogiltig token, 400-validering bakom giltig JWT. - Kubernetes-manifest (infra/k8s, kustomize): namespace, Deployments med readiness/liveness och åtstramad securityContext, Services, HPA 2–10 pods på 70 % CPU, PodDisruptionBudgets, Ingress med TLS via cert-manager, secret-mall (riktiga värden skapas utanför git). - Klienten väljer AI-väg vid bygget: VITE_AI_ORKESTER_URL → tjänsten i klustret, annars Supabase-edge-funktionen. Samma orkester i båda, låst av paritetstest. - CI-arbetsflöde: tester + produktionsbygge + verifierande containerbyggen av båda bilderna på varje push/PR. - docs/DRIFT.md: arkitekturdiagram, driftsättningssteg, skalning, och vad som återstår mot full självhostning. Verifierat: 27 vitest-tester gröna, produktionsbygge ok, alla K8s-manifest YAML-validerade, orkestertjänsten livetestad, Playwright-röktest grönt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
69 lines
3.4 KiB
Markdown
69 lines
3.4 KiB
Markdown
# Guidad Felsökning – Drift i Kubernetes
|
||
|
||
Målarkitekturen ur [Master Prompt](MASTER-PROMPT.md) som kod: allt körbart i kluster, med secrets för plattformsnycklarna och CI som verifierar både kod och containerbyggen.
|
||
|
||
## Arkitektur
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
T[Tekniker] -->|HTTPS| I[Ingress + TLS]
|
||
I -->|/| W[web\n2–10 pods, HPA]
|
||
I -->|/api/ai| A[ai-orkester\n2–10 pods, HPA]
|
||
A -->|Claude API| C[(Anthropic)]
|
||
W & A -.->|auth + data| S[(Supabase\nPostgres · Auth)]
|
||
K[Secret: felsokning-hemligheter] --> A
|
||
```
|
||
|
||
| Komponent | Vad | Var |
|
||
| --- | --- | --- |
|
||
| `web` | SPA:n bakom oprivilegierad nginx (`Dockerfile`, `docker/nginx.conf`) | Deployment + Service + HPA + PDB |
|
||
| `ai-orkester` | AI-orkestern som egen tjänst (`services/ai-orkester`) — samma routing som edge-funktionen, med egen JWT-verifiering och `/halsa` | Deployment + Service + HPA + PDB |
|
||
| Hemligheter | `ANTHROPIC_API_KEY` + `SUPABASE_JWT_SECRET` | Secret `felsokning-hemligheter` — aldrig i bilder eller manifest |
|
||
| Databas & auth | Postgres (händelselogg, RLS, `hamta_delat_arende`) och inloggning | Supabase — managerad, eller självhostad Supabase i klustret när det steget tas |
|
||
|
||
Klienten väljer AI-väg vid byggtillfället: med `VITE_AI_ORKESTER_URL` satt går anropen till orkestertjänsten i klustret; utan den används Supabase-edge-funktionen. Samma orkester (modeller, systemprompter, scheman) i båda.
|
||
|
||
## Driftsätta
|
||
|
||
```sh
|
||
# 1. Bygg och publicera bilderna (ersätt registry i infra/k8s/*.yaml)
|
||
docker build -t ghcr.io/ORG/guidad-felsokning-web \
|
||
--build-arg VITE_SUPABASE_URL=… \
|
||
--build-arg VITE_SUPABASE_PUBLISHABLE_KEY=… \
|
||
--build-arg VITE_SUPABASE_PROJECT_ID=… \
|
||
--build-arg VITE_AI_ORKESTER_URL=https://app.exempel.se .
|
||
docker build -t ghcr.io/ORG/guidad-felsokning-ai-orkester services/ai-orkester
|
||
docker push ghcr.io/ORG/guidad-felsokning-web
|
||
docker push ghcr.io/ORG/guidad-felsokning-ai-orkester
|
||
|
||
# 2. Skapa secret:en (eller använd External Secrets/Sealed Secrets)
|
||
kubectl create namespace guidad-felsokning
|
||
kubectl -n guidad-felsokning create secret generic felsokning-hemligheter \
|
||
--from-literal=anthropic-api-key='sk-ant-…' \
|
||
--from-literal=supabase-jwt-secret='…'
|
||
|
||
# 3. Applicera manifesten
|
||
kubectl apply -k infra/k8s
|
||
|
||
# 4. Verifiera
|
||
kubectl -n guidad-felsokning get pods
|
||
curl https://app.exempel.se/halsa # → {"status":"ok"}
|
||
```
|
||
|
||
Byt domän och cert-issuer i `infra/k8s/ingress.yaml`.
|
||
|
||
## Skalning och robusthet
|
||
|
||
- **HPA** 2–10 pods per tjänst på 70 % CPU; **PDB** håller minst en pod uppe vid noddränering.
|
||
- Båda containrarna kör **non-root** utan capabilities; orkestern dessutom med read-only rotfilsystem.
|
||
- **Hälsokontroller**: nginx svarar på `/`, orkestern på `/halsa` (readiness + liveness + Docker HEALTHCHECK).
|
||
- Orkestern **failar closed**: utan nyckel/JWT-hemlighet svarar den 503, utan giltig JWT 401.
|
||
|
||
## CI
|
||
|
||
`.github/workflows/ci.yml` kör tester + produktionsbygge och verifierar båda Dockerfilerna på varje push/PR. Publicering till registry och `kubectl apply` läggs i ett separat, behörighetsstyrt deploy-flöde (t.ex. environments med godkännande eller GitOps via Argo CD/Flux).
|
||
|
||
## Kvar mot full självhostning
|
||
|
||
Synk, Live Share-funktionen och inloggningen går fortfarande mot Supabase (managerad Postgres + Auth). Nästa steg för "allt i kluster" är självhostad Supabase eller ett eget API-lager över Postgres i klustret — händelsemodellen är redan API-klar, så det bytet påverkar inte klienten.
|