feat: Add Rybbit (open-source web analytics) with HA replicas #107
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Overview
Deploy Rybbit — open-source (AGPL-3.0), cookie-less and privacy-friendly alternative to Google Analytics — in the cluster, exposed via Traefik (optionally reachable outside the LAN via Newt). Goal is HA: the stateless app tier runs with 2 replicas each (backend + client), backed by shared Postgres/ClickHouse/Redis.
This issue is for research + planning; implementation happens in follow-up PR(s) once the decisions below are made.
Upstream architecture (from
docker-compose.yml/Caddyfile)backendghcr.io/rybbit-io/rybbit-backendclientghcr.io/rybbit-io/rybbit-clientclickhouseclickhouse/clickhouse-server:26.3.17.4postgrespostgres:17.4redisredis:8.6.4-alpinecaddycaddy:2.10.0(optionalwith-webserverprofile)We replace Caddy with Traefik. Upstream routing to replicate:
/api/*→ backend:3001/.well-known/oauth-*and/.well-known/openid-configuration*→ backend:3001Latest upstream release at time of writing: v2.9.0 — pin image tags instead of
latest.HA assessment
Safe to scale:
client(Next.js) andbackend(Fastify) are stateless HTTP servers — no websockets (no@fastify/websocket/ socket.io inserver/package.json).session:<siteId>:<fingerprint>, 30 min sliding TTL) rather than in-process, so ingestion can be load-balanced across backend pods.Gotchas:
server/src/cluster.ts): telemetry, usage check, unclaimed-site cleanup (weekly reports / lifecycle e-mails are cloud-only, i.e. irrelevant withDEPLOYMENT=self-hosted). With N backend pods these run N times — verify idempotency or serialize (leader election / separate single-replica deployment).pageviewQueue.ts: flush every 1 s / 5000 events). A crash loses up to ~1 s of events — mitigate with graceful shutdown (preStopsleep +terminationGracePeriodSeconds).initializeClickhouse()+initPostgres()). Verify two replicas starting concurrently don't race — likelyCREATE ... IF NOT EXISTS, but should be tested.X-Forwarded-For/X-Real-IP, plusCF-Connecting-IPhandling inresolveClientIp.ts). Verify Traefik forwards XFF correctly so geo data isn't wrong.Proposed deployment
rybbittokubernetes/apps/kustomization.yaml.analytics.kobbo.se(orstats.kobbo.se?) via TraefikIngressRoute, reachable through the Newt tunnel if wanted.replicas: 2for backend/client withpodAntiAffinity+ topology spread andPodDisruptionBudget(minAvailable: 1); works withallowSchedulingOnControlPlanes.local-pathvsnfs-csi— ClickHouse on NFS can be flaky (fsync/latency), so considerlocal-pathpinned to one controlplane node.Open questions / research
CLUSTER_WORKERStuning?nfs-csivslocal-path(durability vs performance; node pinning)pg_dump+ ClickHouseBACKUPto S3/MinIO on a CronJob (upstream hasops/backups; cf. krona backup issue #77)@aws-sdk/client-s3MAPBOX_TOKEN/ maps feature — needed or leave disabled?DISABLE_SIGNUP=true,BASE_URL,DEPLOYMENT=self-hosted,LITE_DASHBOARDenv choicesCLUSTER_WORKERSvs K8s replicas duplication)initContainer/Job, or leave to app startup?Acceptance criteria
DISABLE_SIGNUP=trueafter admin created/api,/.well-known, SPA)docs/rybbit.mdadded and namespace/app listed in kustomizationReferences
docker-compose.yml,Caddyfile,server/src/cluster.ts,server/src/services/tracker/pageviewQueue.ts,server/src/services/sessions/sessionsService.tsStatus: research + beslut klara, implementationen ligger i PR #108 (feature/rybbit → develop). Backuper och Kiln-trackern är kvar som separata uppföljningar.
kiln.kobbo.se-analysen (det du ville börja med)
Hur det exponeras idag:
*.kobbo.seär ett wildcard i DNS → Pangolin-VPS:en (51.38.141.159), som utfärdar Let's Encrypt-cert per hostname och kör newt-tunneln in till in-cluster Traefik (10.8.40.200). Därifrån tarIngressRouteHost(kiln.kobbo.se)→kiln-app:3000.Upptäckt:
kiln.kobbo.sesvarar 200 direkt, utan Pangolin-SSO — medanpi.,hermes.ochn8n.302:ar tillpangolin.kobbo.se/auth/resource/<id>. Det betyder att notisen iznibb/lovable/docs/deploy.mdom att Pangolin blockerar/preview/*(och kräver en Always-allow-regel) inte längre stämmer. Antagligen avsiktligt, men värt att bekräfta — om Kiln ska vara SSO-skyddat är det en egen Pangolin-inställning, inte något Rybbit rör.Vad det betyder för Rybbit + Kiln: att mäta kiln.kobbo.se kräver ingen ändring i Rybbit-deployen. Trackern laddas i besökarens webbläsare och postar cross-origin till
analytics.kobbo.se(Rybbit är cookie-less, så PangolinsSameSite=Lax-cookie är irrelevant). Det som krävs:analytics.kobbo.se. Vill man ändå det (som pi./n8n.) måste Always-allow-regler läggas för/api/script.js,/api/track,/api/site/tracking-config/*,/.well-known/*m.fl. — full lista idocs/rybbit.md.X-Forwarded-*från Pangolin når backend oförändrat, annars blir geodata fel. Traefik rensar idag alla X-forwarded-headers från newt-podden → PR #108 sätterentryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16.znibb/lovable,src/app/layout.tsx) — egen liten PR i app-repot, blockerar inte Rybbit-deployen.data-site-idskapas i Rybbits UI, så det kan ändå inte ligga i GitOps.Behöver beslutet tas i detta steg? Nej. Hostnamn och SSO-val är billiga att ändra senare: DNS-wildcardet finns, klienten använder relativa
/api-URL:er (upstream-imagen bakas med tomNEXT_PUBLIC_BACKEND_URL) och backendensBASE_URLär en runtime-env. Det som behöver bestämmas innan deploy är bara (a) hostnamn + Pangolin-resource och (b) SSO av/på — jag lade PR:en påanalytics.kobbo.seutan SSO som default, säg till om du vill hastats.eller SSO i stället.Övriga beslut (se PR #108 / docs/rybbit.md)
CLUSTER_WORKERS=0+DISABLE_TELEMETRY=true; usage-cron är cloud-only; site-baseline Redis-vald → inga dubbelkörningarmigrate-initContainer med retry (drizzle-kit tar inget advisory lock)nfs-csi(issue #8), NFS-latens accepterad i v1/api-URL:erDISABLE_SIGNUP=true(admin bootstrappas med ett tillfälligtkubectl set env … =false),DISABLE_TELEMETRY=true,CLUSTER_WORKERS=0,BASE_URL=https://analytics.kobbo.seKvar
pg_dump+ ClickHouseBACKUP— förslag: delad pipeline enligt #7/#77.znibb/lovable.analytics.kobbo.se→10.8.40.200:80skapas manuellt (DNS finns redan).bash kubernetes/core/traefik/helmrelease.sh(Helm CLI).Smoke-test i klustret (pre-merge) ✅
Pangolin-resourcen verifierad:
analytics.kobbo.sehar Let's Encrypt-cert och trafiken når in-cluster Traefik.Jag applicerade appen med
scripts/rybbit-apply.sh(ny, samma pre-merge-mönster sompi-devbox-apply.sh). Inget är mergat, så Flux äger den inte än.Status:
https://analytics.kobbo.se/200,/api/health200,/api/script.js200,/api/tracknås (endast POST).ClickHouse query user provisioned.pg_type_typname_nsp_index-krock mellan två pods) och retry-loopen löste det utan krasch — precis vad initContainern är till för.Geo/XFF verifierat med en tillfällig echo-tjänst bakom Traefik:
x-real-ip: 10.244.0.0(newt-podden),x-forwarded-proto: http.entryPoints.web.forwardedHeaders.trustedIPs=10.244.0.0/16+helm upgrade(Traefik rullade utan downtime,maxUnavailable: 0):x-real-ip: <publik klient-IP>,x-forwarded-proto: https,x-forwarded-port: 443.Just nu:
DISABLE_SIGNUP=truei både kluster och git (säkert default). Admin-kontot bootstrappas genom att tillfälligt sättaDISABLE_SIGNUP=false, skapa kontot i UI:t och sätta tillbaka — dokumenterat idocs/rybbit.md.Kvar: merge av PR #108 (develop → main) så Flux tar över, skapa admin-kontot, Kiln-snippet och backuper.
Implementerad av mergad PR #108 (backend/client 2 replikor, PDB:er, smoke-testad i klustret). Backuper spåras redan i #7/#77 och Kiln-trackern är en kvarvarande uppföljning. Stänger.