15 KiB
Agnes Multi-Customer Deployment Log
Datum: 2026-04-21
Spec: docs/superpowers/specs/2026-04-21-multi-customer-deployment-spec.md
Plan: docs/superpowers/plans/2026-04-21-multi-customer-deployment.md
Průběžný log všeho, co bylo uděláno, včetně zvolených hodnot, úprav plánu, objevených překážek a jejich řešení. Cílem je, aby další zákazník šel nasadit jedním skriptem.
Přehled
Startup stav: Keboola prod/dev Agnes běžel z osobního forku padak/tmp_oss (branch feature/v2-fastapi-duckdb-docker-cli), git pull při boot, tokeny v plaintextu v VM metadata. Cíl: přejít na self-deploy model — public upstream keboola/agnes-the-ai-analyst + privátní keboola/agnes-infra-keboola s Terraformem, GHCR :stable image, Secret Manager.
Konvence
- Public repo:
keboola/agnes-the-ai-analyst(app + TF modul) - Privátní repo:
keboola/agnes-infra-{customer}(pro Keboolukeboola/agnes-infra-keboola) - GCP projekt:
kids-ai-data-analysis(Keboola) — pozn.: ponechán, ownerpetr@keboola.com - Deploy SA:
agnes-deploy@<project>.iam.gserviceaccount.com - TF state bucket:
gs://agnes-<project>-tfstate/<customer>/ - VM SA:
agnes-<customer>-vm@<project>.iam.gserviceaccount.com(scope: secretmanager.secretAccessor) - Secrets v SM:
keboola-storage-token— sdílený, manuálně vytvořenýagnes-<customer>-jwt-secret— per-customer, auto-generovaný TF
- Image tag:
:stable(floating) — prod default:dev(floating) — dev default:dev-<branch-slug>— per-branch (vyžaduje workflow commit — viz Známá omezení)
Chronologie
2026-04-21 odpoledne — Fáze 0 + 1 (MVP)
- Ověření IAM přes operativu:
gcloud iam service-accounts create test...— funguje i bez přímé role na projektu. Keboola má org-level inherited perms. Owner zůstávápetr@keboola.com. - GHCR image public:
docker manifest inspect ghcr.io/keboola/agnes-the-ai-analyst:stablefunguje bez auth. - Snapshot boot disku:
data-analyst-pre-migration-20260421(safety net před Fází 2). - Per-branch tagging v release.yml: commit
0ade45c— přidává:dev-<slug>tag. Nepushnuto do origin kvůli chybějícímuworkflowscope; uložen jako patch~/.agnes-keys/0ade45c-workflow-per-branch-tag.patch. - bootstrap-gcp.sh: Vytváří SA + role + tfstate bucket + SA key. Spuštěno na
kids-ai-data-analysis. Vytvořenagnes-deploySA, bucketgs://agnes-kids-ai-data-analysis-tfstate, klíč uložen do~/.agnes-keys/agnes-deploy-kids-ai-data-analysis-key.json. - Secret Manager:
keboola-storage-token,jwt-secret-keynahrány (obě s PŘEDCHOZÍMI hodnotami —jwt-secret-keyaby existing JWT tokeny zůstaly validní;keboola-storage-tokenpro kontinuitu syncu). Rotace tokenu odložena do Fáze 2 completion. - fetch-env-from-secrets.sh: VM-side skript, který stahuje secrets a skládá
.env. - Deploy MVP na staré VM
data-analyst:docker compose down→git remote set-url origin https://github.com/keboola/agnes-the-ai-analyst.git→git fetch + reset --hard origin/main→ scp fetch-env.sh →fetch-env.sh→docker compose pull + up -d- Ověřeno:
/api/healthstatus: degraded(stale tables, OK), imageghcr.io/keboola/agnes-the-ai-analyst:stable, loginzdenek.srotyr@keboola.com / 1234funguje.
- Deploy MVP na staré VM
data-analyst-dev: App dir je/opt/data-analyst/pod useremzdeneksrotyr(jiná struktura než prod). Scope VM je omezený —fetch-env.shselhal, ale .env zůstal beze změny (stejné hodnoty), app běží na:stable. - tmp_oss smazán: Starý osobní fork už neexistoval.
2026-04-21 odpoledne — Fáze 2 (TF modul + nové VMs)
- TF modul
infra/modules/customer-instance/: Refactor z monolitníhoinfra/main.tfna reusable modul s:prod_instanceobject +dev_instanceslist (podporuje per-branch image_tag)- Persistent
/datadisk (pd-ssd, default 50 GB prod / 20 GB dev) - Dedikovaný VM SA
agnes-<customer>-vmjen ssecretmanager.secretAccessor - Auto-generovaný JWT secret v SM
- OS Login (
enable-oslogin=TRUE) - Startup script: mount disku, download docker-compose z main branch, fetch secrets,
docker compose up, volitelně watchtower + Caddy profile - Commit:
a2c05a5 infra: refactor Terraform into reusable customer-instance module
- Tag
infra-v1.0.0push do origin. - Privátní repo
keboola/agnes-infra-keboola: Vytvořen v Keboola org. Struktura:terraform/main.tf— module referencegithub.com/keboola/agnes-the-ai-analyst//infra/modules/customer-instance?ref=infra-v1.0.0, backendgcsterraform/variables.tf— default hodnoty pro Keboolu (project, region, prod_instance, dev_instances).github/workflows/plan.yml— PR:terraform plan→ komentář v PR přesgh pr comment(neactions/github-scriptkvůli validátoru).github/workflows/apply.yml— push main: apply-dev (envdev, no protection) → apply-prod (envprod, protected_branches, 5min wait, smoke test)- GitHub secret
GCP_SA_KEYnahrán z~/.agnes-keys/agnes-deploy-*.json - Environmenty
devaprodvytvořeny přesgh api
- Terraform apply Keboola instance: 12 resources vytvořeno:
agnes-prodVM +agnes-prod-datadisk (50 GB) +agnes-prod-ip(34.77.102.61)agnes-devVM +agnes-dev-datadisk (20 GB) +agnes-dev-ip(34.77.94.14)- Firewall
agnes-keboola-allow-web agnes-keboola-vmSA + IAM bindingagnes-keboola-jwt-secret+ version- TF state v
gs://agnes-kids-ai-data-analysis-tfstate/keboola/
- Data migration starý prod → nový prod (~2 min):
docker compose downna starém prod VMtar czf /tmp/agnes-data.tar.gz -C /var/lib/docker/volumes/app_data/_data .(1.8 GB)gsutil cpdogs://agnes-kids-ai-data-analysis-tfstate/migration/agnes-data-20260421-1624.tar.gz- Problém:
agnes-keboola-vmSA nemělstorage.objectViewerna bucketu →gsutil iam ch serviceAccount:...:objectViewer gs://...(dočasné, pro download) docker compose downna novém prod VMgsutil cpz bucketu na nový VM +tar xzf ... -C /datadocker compose up -dna novém prod VM- POZOR: Analytics DB se nezbudovala automaticky po extrakci — viz Známá omezení.
Klíčové hodnoty (kopíruj pro další zákazníky)
GCP_PROJECT_ID = kids-ai-data-analysis
CUSTOMER_NAME = keboola
DEPLOY_SA = agnes-deploy@kids-ai-data-analysis.iam.gserviceaccount.com
TFSTATE_BUCKET = gs://agnes-kids-ai-data-analysis-tfstate
TFSTATE_PREFIX = keboola
VM_SA = agnes-keboola-vm@kids-ai-data-analysis.iam.gserviceaccount.com
JWT_SECRET = agnes-keboola-jwt-secret (TF-managed)
KEBOOLA_TOKEN_SECRET = keboola-storage-token (manuálně vytvořený)
INFRA_MODULE_REF = infra-v1.0.0 (github.com/keboola/agnes-the-ai-analyst)
PROD_IP = 34.77.102.61 (agnes-prod)
DEV_IP = 34.77.94.14 (agnes-dev)
STARÝ PROD IP (legacy) = 35.195.96.98 (data-analyst — po stabilitě smazat)
STARÝ DEV IP (legacy) = 34.62.223.189 (data-analyst-dev — po stabilitě smazat)
Známá omezení / TODO
Workflow commit nepushnutý
Commit 0ade45c (per-branch :dev-<slug> tag v release.yml) vyžaduje workflow scope na GH tokenu, který aktuální token nemá. Uloženo v ~/.agnes-keys/0ade45c-workflow-per-branch-tag.patch.
Akce pro dokončení:
gh auth refresh -h github.com -s workflow
cd <public-repo>
git am ~/.agnes-keys/0ade45c-workflow-per-branch-tag.patch
git push origin feature/multi-customer-deployment
Bez toho fungují jen floating tagy :dev a :stable, ale ne pinned :dev-<branch-slug> v dev_instances.
Analytics DB se po migraci dat nepřebudovala
Po kopii /data přes tar na nový prod VM má system.duckdb všechno (table_registry, users), ale analytics DB je prázdná — SyncOrchestrator nespustil rebuild() automaticky. Endpoint /api/sync/trigger nebo /api/sync/rebuild bude třeba dohledat v app API a zavolat autentizovaně.
Dev VM data-analyst-dev staré scope
Staré data-analyst-dev má omezené compute SA scope bez Secret Manageru. V Fázi 2 se nahrazuje novým agnes-dev (s dedikovaným VM SA), staré zruš po ověření stability.
Starý Keboola token nerotován
Nový token v SM je stále ten stejný, co byl v .env na starém VM. Po ověření stability nového proudu v Keboola UI vygenerovat nový + gcloud secrets versions add keboola-storage-token + restart containerů. Starý pak invalidovat.
Admin heslo 1234 na starém prod
Migrace dat zkopírovala users table, takže heslo je platné i na novém prod. Rotace je uživatelův úkon přes UI. Nové dev VM má jiný state → jiné hesla.
Co zbývá (uživatelské akce)
- Approve prod environment v
apply.ymlrunu (https://github.com/keboola/agnes-infra-keboola/actions/runs/24731681502) — jinak se state neaplikuje na prod - Změnit heslo admin usera z
1234(http://34.77.102.61:8000/login → profil) - Rotovat Keboola Storage token v Keboola UI →
gcloud secrets versions add keboola-storage-token --data-file=- --project=kids-ai-data-analysis→ restart app containerů na obou VMs (cron to zachytí při dalším tiku nebosudo /usr/local/bin/agnes-auto-upgrade.sh)
Aktualizace průběhu (2026-04-21 pozdně)
Fixy po první migraci
-
Docker named volume → bind mount /data: Po první migraci nové VMs používaly
agnes_dataDocker named volume (uložený na boot disku 30GB), nikoli persistent disk mountovaný na/data(50GB). Fix: vdocker-compose.prod.ymloverride volumedatajako bind mount/data. Commit52d6345. Bumplý taginfra-v1.1.0. -
Watchtower → cron:
containrrr/watchtower(v1.7.1 i latest) má nekompatibilní Docker API (posílá 1.25, daemon vyžaduje 1.40+). Nahrazen bash skriptem/usr/local/bin/agnes-auto-upgrade.shspouštěným cronem každých 5 min. Detekuje změnu image digest, pokud ano, pullne +docker compose up -d. Commitcbd85c5v modulu, taginfra-v1.1.0. -
Ověření auto-upgrade: Během finálního verify cyklu cron pullnul novější
:stable-2026.04.33(nejnovější release) a recreate containers na prod. Fungování potvrzené.
Iterace 2 — finalizace (2026-04-21 večer)
- Workflow commit pushnut — po
gh auth refresh -s workflowprotlačen0ade45c+ merge do main. Per-branch tagging:dev-<slug>v GHCR aktivní. - Dev data zmigrovaná —
data-analyst-dev→ lokál →agnes-dev. DuckDB registry obsahuje 99 tabulek + 1 admin usera. - Module bumpnut na v1.2.0 v Keboola infra repu — README plně v EN, CI spustí čistý plan.
- Backup + monitoring → infra-v1.3.0: daily snapshot schedule na
/datadisku (30d retention), per-VM uptime check + alert policy. Template repo bumpnut na v1.3.0. - Renovate config v template + keboola-infra repu — tracks
infra-v*tagy, otevírá PR při nové verzi. - Staré VMs smazané —
data-analyst,data-analyst-dev, jejich static IP, pre-migration snapshot, migration tar z bucketu. - Temporary IAM grants revokovány —
secretmanager.secretAccessorodebrán z default compute SA (na secrets),storage.objectViewerodebrán zagnes-keboola-vmna tfstate bucket. - Onboarding ONBOARDING.md rozšířen o propagation přes
-replace, backup restore, monitoring setup, race condition fix. - Auth v2 → v3 action bump v obou workflow repech (silences Node 20 deprecation warning).
- Prod apply-dev úspěšně proběhl po manuálním triggeru (initial apply měl race s timing secret creation). apply-prod čeká na reviewera.
Finální stav (po iteraci 2)
| Resource | Value |
|---|---|
| Prod VM | agnes-prod @ 34.77.102.61 (e2-small, 50GB /data PD, daily snapshot) |
| Dev VM | agnes-dev @ 34.77.94.14 (e2-small, 20GB /data PD, daily snapshot) |
| Staré VMs | 🗑️ smazané |
| Image tagy | prod :stable, dev :dev, feature branches :dev-<slug> |
| Auto-upgrade | Cron */5 * * * * — bash skript, detekce digest change → restart |
| Prod health | degraded (stale tables), 103 tables, 9.3M rows, 2 users |
| Dev DB | 99 tables v registry, 1 user (admin@keboola.com) |
| Backups | Daily snapshot @ 02:00, 30-day retention (oba disky) |
| Monitoring | uptime check 60s/10s per VM, alert po 5 min failure (notification channels nenapojené — user musí dodat) |
| Login prod | zdenek.srotyr@keboola.com / 1234 (pending user: rotate) |
| TF state | gs://agnes-kids-ai-data-analysis-tfstate/keboola/ (versioned, GCS backend) |
| Deploy SA | agnes-deploy@kids-ai-data-analysis.iam.gserviceaccount.com |
| VM SA | agnes-keboola-vm@kids-ai-data-analysis.iam.gserviceaccount.com (scope: secretmanager.secretAccessor) |
| Secrets | keboola-storage-token, jwt-secret-key, agnes-keboola-jwt-secret |
| Public upstream repo | https://github.com/keboola/agnes-the-ai-analyst |
| Template repo | https://github.com/keboola/agnes-infra-template (is_template=true, ref infra-v1.3.0) |
| Keboola infra repo | https://github.com/keboola/agnes-infra-keboola (EN README, Renovate, ref infra-v1.2.0) |
| Module tagy | infra-v1.0.0 (initial), v1.1.0 (volume + cron), v1.2.0 (CI fix), v1.3.0 (backups + monitoring) |
Onboarding druhého zákazníka — kompletní flow
Podle docs/ONBOARDING.md — cíl: < 1 hodina. Klíčové kroky:
bootstrap-gcp.sh <PROJECT_ID>— SA + bucket + klíčgcloud secrets create keboola-storage-token ...(pokud source = keboola)gh repo create <org>/agnes-infra-<cust> --template keboola/agnes-infra-template --private- Upload GCP_SA_KEY do GH secret
- Editovat
terraform/main.tf(backend bucket/prefix) +terraform.tfvars - Vytvořit
dev+prodenvironments přesgh api git push→ CI applyPOST /auth/bootstrapadmin user- Otestovat
/api/health+ login
Předpokládám, že nový zákazník (např. GRPN) projde všech 9 kroků za ~30–45 min včetně čekání na TF apply.
Budoucí one-click deploy
Cíl: pro nového zákazníka {customer} (např. grpn) by mělo stačit:
# 1. Vytvořit GCP projekt (má billing)
gcloud projects create agnes-{customer}
# 2. Bootstrap GCP (SA + bucket + role + klíč)
./scripts/bootstrap-gcp.sh agnes-{customer}
# 3. Vytvořit Keboola Storage secret v zákaznickém SM (manuálně, token dodá zákazník)
echo -n "<KEBOOLA_TOKEN>" | gcloud secrets create keboola-storage-token \
--data-file=- --replication-policy=automatic --project=agnes-{customer}
# 4. Klonovat template repo (template repo musí existovat — Fáze 6)
gh repo create {org}/agnes-infra-{customer} --template keboola/agnes-infra-template --private
# 5. Upload SA key do GH secretu
cd agnes-infra-{customer}
gh secret set GCP_SA_KEY < ~/.agnes-keys/agnes-deploy-agnes-{customer}-key.json
# 6. Vyplnit terraform/terraform.tfvars (customer_name, project, IP preferences)
# 7. První apply — spustí CI/CD a nahodí VMs
git add . && git commit -m "initial" && git push
Co tomu ještě chybí:
- Template repo (Fáze 6)
- Onboarding skript, který provede kroky 1–7 interaktivně
- Dokumentace: jak nastavit DNS, TLS, admin account bootstrap