08 — Deployment & Infrastructure
Deployment Diagram (per region)
Placement decisions and why
| Component | Where | Why |
|---|---|---|
| Control-plane services | k8s Deployments, standard node pool | stateless; rolling deploys; HPA on CPU/RPS |
| Signaling | k8s Deployment, HPA on connection count | connection-stateful → graceful drain (see below) |
| SFU | k8s dedicated node pool, hostNetwork: true, one pod per node, guaranteed QoS, taints/tolerations | UDP performance (no kube-proxy NAT in media path), predictable CPU, no noisy neighbors; scaling = adding nodes |
| Coturn | outside k8s — VM/bare-metal fleet with static public IPs | TURN needs stable public IPs + huge port ranges (49152–65535/UDP); k8s adds nothing but friction here |
| PostgreSQL / Kafka / Redis / ClickHouse | k8s operators (CloudNativePG, Strimzi, etc.) or managed cloud equivalents | recommendation: managed services in cloud deployments; operators for sovereign/on-prem |
| Recording | k8s Jobs (LiveKit Egress) | bursty, batch-like; scales to zero |
Kubernetes mechanics
- Rolling deploys (
maxUnavailable: 0, maxSurge: 25%) for all control-plane services; PodDisruptionBudgets everywhere (minAvailable: 2). - Signaling drain:
preStophook flips readiness, sends4503close (clients resume elsewhere within seconds), waits for connection count → 0 or 60 s. Deploys never hard-drop sessions. - SFU drain: node cordoned → LiveKit drain API stops accepting new rooms → existing rooms migrate on natural end or forced ICE-restart migration after grace period (30 min) → node recycled. SFU deploys are blue-green at node-pool level, not rolling.
- Blue-green for gateway and any change with risky traffic implications: parallel Deployment + weighted service shift (via gateway/Argo Rollouts), instant rollback by weight flip.
- Multi-region: this whole diagram stamped per region; see 09 for data-layer topology and global routing.
- Config & secrets: all config via env/ConfigMaps from a single
deploy/source; secrets in cloud secret manager, synced by External Secrets Operator; nothing secret in images or git. - Networking: NetworkPolicies default-deny between namespaces; only declared flows (matrix in 02) allowed. mTLS service-to-service via mesh (Linkerd) or gateway-issued SPIFFE identities.
Repository / Folder Structure
Monorepo (Go for services — one language across control plane; LiveKit and Coturn consumed as upstream images):
communication-baas/
├── docs/ # ← this documentation set
├── contracts/ # cross-service source of truth, CI-enforced
│ ├── openapi/ # REST API specs (one per service)
│ ├── events/ # Kafka event JSON schemas
│ └── ws-protocol/ # signaling message schemas
├── services/
│ ├── gateway/ # thin config/policy repo for Envoy-based gateway
│ ├── auth/
│ │ ├── cmd/ # main()
│ │ ├── internal/
│ │ │ ├── api/ # HTTP handlers (transport)
│ │ │ ├── domain/ # business logic, no framework imports
│ │ │ ├── store/ # PostgreSQL repositories
│ │ │ └── tokens/ # JWT/JWKS
│ │ ├── migrations/
│ │ └── Dockerfile
│ ├── room/ # same internal layout
│ ├── signaling/
│ ├── turn-mgmt/
│ ├── presence/
│ ├── notification/
│ └── analytics/
├── pkg/ # shared Go libraries (log, otel, kafka, redis, authz middleware)
├── sdks/ # client SDKs (thin wrappers over LiveKit SDKs + our REST/WS)
│ ├── web/ ├── android/ ├── ios/ └── desktop/
├── deploy/
│ ├── helm/ # one chart per service + umbrella chart
│ ├── k8s/ # base manifests / kustomize overlays (dev, staging, prod-{region})
│ ├── coturn/ # coturn config templates + fleet provisioning (Terraform + cloud-init)
│ └── terraform/ # cloud infra: VPCs, clusters, LBs, DNS, managed data stores
├── observability/
│ ├── dashboards/ # Grafana JSON
│ ├── alerts/ # Prometheus rules
│ └── otel/ # collector config
├── tools/ # load-test harness (simulated WebRTC clients), chaos scripts
├── Makefile
└── .github/workflows/ # CI: lint, test, contract-check, image build, deploy
Principles: contracts are code (OpenAPI/event schemas versioned and CI-verified against implementations); each service independently buildable/deployable despite the monorepo; internal/domain never imports transport or storage packages (hexagonal layering).
Environments & Promotion
| Env | Purpose | Data | Scale |
|---|---|---|---|
dev | per-developer, docker-compose or kind | ephemeral | minimal (single SFU, single coturn) |
staging | full topology, one region | synthetic + load tests | ~10 % of prod |
prod-{region} | live | real | autoscaled |
Promotion: merge to main → CI builds immutable images (git-SHA tags) → auto-deploy staging → automated smoke (scripted WebRTC join + audio-loopback assertion) + soak → manual gate → progressive prod rollout (region by region, blue-green where applicable).