Deployment¶
A backend or frontend change reaches production through a tag → CI → ArgoCD → GKE pipeline. There is no kubectl apply step run by a human; everything is GitOps.
End-to-end flow¶
flowchart TD
Dev[Developer] -->|scripts/release.sh| Tag[git tag v1.2.3]
Tag -->|push| GitHub[GitHub: tomoda repo]
GitHub -->|tag trigger| CB1[Cloud Build: backend]
GitHub -->|tag trigger| CB2[Cloud Build: frontend]
CB1 --> AR[(Artifact Registry)]
CB2 --> AR
AR -.image updater.-> DevopsRepo[GitHub: devops repo<br/>K8s manifests + Kustomize]
DevopsRepo --> ArgoCD[ArgoCD]
ArgoCD -->|reconcile| GKE[GKE cluster]
Step by step:
- Tag the release. Run
scripts/release.shfrom themainbranch. It bumps theVERSIONfiles, commits, tagsvX.Y.Z, and pushes both the commit and the tag. - Cloud Build builds images. The tag push triggers two Cloud Build jobs (one backend, one frontend). Each produces an image and pushes tags to Artifact Registry (
tomoda-prod-repofor tags,tomoda-dev-repofor main-branch builds with manual approval). See Cloud Build and DevOps → Cloud Build. - Argo CD Image Updater detects the new image SHA in Artifact Registry and commits a Kustomize image override back to the
devopsrepo. - Argo CD reconciles. Argo CD watches the
devops/repo and applies the new manifests to the target K8s namespace using Kustomize. See DevOps → Argo CD. - Rolling update. Kubernetes performs a rolling update of the Deployment, gated by the readiness probe. Prod has a
PodDisruptionBudgetwithminAvailable: 1.
Where the manifests live¶
The shape of the application in GKE:
| File | Purpose |
|---|---|
k8s/apps/tomoda/base/backend-deployment.yaml (in devops repo) |
Backend Deployment + ClusterIP Service |
k8s/apps/tomoda/base/frontend-deployment.yaml |
Frontend (Nginx) Deployment + Service |
k8s/apps/tomoda/base/ingress.yaml |
Traefik Ingress (path-routed: /api, /ws → backend; rest → frontend) |
k8s/apps/tomoda/base/network-policy.yaml |
NetworkPolicy restricting pod-to-pod traffic |
k8s/apps/tomoda/base/kustomization.yaml |
Kustomize base |
k8s/apps/tomoda/overlays/dev/ |
Dev-environment overrides (namespace tomoda, dev image repo) |
k8s/apps/tomoda/overlays/prod/ |
Prod-environment overrides (namespace prod, prod image repo, PDB) |
See DevOps → Tomoda K8s app for the full breakdown.
The image reference in base/backend-deployment.yaml currently points at asia-east1-docker.pkg.dev/development-485000/tomoda-dev-repo/tomoda-backend:latest with imagePullPolicy: Always. Overlays may pin to a specific SHA.
Source of truth
Always read the manifests in devops/ for current resource limits, replicas, env vars, and host names. Anything quoted here is illustrative.
Health & readiness¶
The backend Deployment defines both a liveness and a readiness probe against GET /health on :8080:
livenessProbe:
httpGet: { path: /health, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet: { path: /health, port: 8080 }
initialDelaySeconds: 10
periodSeconds: 10
/health is implemented in the backend (see backend/internal/handlers/health.go). A rolling update will not shift traffic to a new pod until the readiness probe passes.
Resources¶
The backend currently runs at modest limits (verify in devops/k8s/apps/tomoda/base/backend-deployment.yaml):
requests: 64Mi memory, 10m CPUlimits: 512Mi memory, 500m CPUreplicas: 1(single-process WebSocket Hub — see Overview)
Rollback¶
Rolling back is a Git operation on the devops/ repo, not the application repo. See DevOps → Rollback for the full procedure (image rollback via Argo CD UI, git revert, or — last resort — kubectl rollout undo).
Frontend deploy¶
The frontend Pod (Nginx serving the static Expo web export) is deployed via the same flow. Because the EXPO_PUBLIC_* env vars are baked in at build time, any change to public API URLs / OAuth client IDs requires a rebuild, not a config change — see Cloud Build.
Mobile is different¶
The iOS and Android binaries are not deployed through this pipeline. They're built and submitted via EAS — see Native Release.