Skip to content

GCP Overview

One project, one region, one cluster, many namespaces. Tomoda's GCP footprint is intentionally small so that every operator can hold the whole topology in their head.

Single-project setup

There is no separate tomoda-dev and tomoda-prod project. Everything runs in development-485000 in asia-east1 and is isolated by Kubernetes namespace. The project name is a historical artifact — treat it as the production project.

Topology at a glance

flowchart TB
    subgraph GCP["GCP project: development-485000 (asia-east1)"]
        direction TB

        subgraph TF["Terraform state"]
            TFSTATE[(GCS: development-485000-tfstate<br/>prefix: terraform/state<br/>default workspace)]
        end

        subgraph NET["VPC: gke-tomoda-vpc"]
            SUBNET[Subnet 10.0.0.0/24<br/>pods 10.1.0.0/16<br/>services 10.2.0.0/20]
        end

        subgraph GKE["GKE: gke-tomoda (asia-east1-a)"]
            POOL1[spot-nodes pool<br/>e2-medium · count=0]
            POOL2[high-mem-spot pool<br/>e2-standard-2 · count=1]
        end

        subgraph CI["CI / Images"]
            CB[Cloud Build<br/>4 triggers]
            AR_DEV[(Artifact Registry<br/>tomoda-dev-repo)]
            AR_PROD[(Artifact Registry<br/>tomoda-prod-repo)]
        end

        subgraph SEC["Secrets & OAuth"]
            IAP[IAP Brand + OAuth Client]
            SM[(Secret Manager<br/>GOOGLE_OAUTH_CLIENT_SECRET)]
        end

        subgraph STORAGE["GCS Buckets"]
            BK_DB[(tomoda-db-backups-…<br/>CNPG WAL + base)]
            BK_PI[(…-photon-index<br/>public-read, lifecycle)]
        end

        GKE --> NET
        CB --> AR_DEV
        CB --> AR_PROD
        IAP --> SM
        GKE -. Workload Identity .-> BK_DB
        GKE -. Workload Identity .-> BK_PI
        GKE -. Workload Identity .-> AR_DEV
        GKE -. Workload Identity .-> AR_PROD
    end

What lives where

Concern Resource Page
Compute GKE cluster + 2 node pools GKE
Network Custom VPC + secondary ranges VPC
CI Cloud Build triggers + SA Cloud Build
Images 2 Artifact Registry repos Artifact Registry
GitOps Argo CD via Helm Argo CD
Auth IAP brand + OAuth client OAuth & Dex
Geocoder index …-photon-index GCS bucket Photon Indexer
DB backups tomoda-db-backups-… GCS bucket Backup
Identity Workload Identity bindings IAM

Environment isolation

Because there is only one project, "dev" and "prod" are not GCP-level boundaries. They show up as:

  • Kubernetes namespacestomoda-dev, tomoda-prod, data (single namespace, holds both postgres-dev and postgres-prod clusters).
  • Artifact Registry repostomoda-dev-repo for main-branch builds, tomoda-prod-repo for semver-tag builds. Cloud Build pushes to whichever repo the trigger configures.
  • OAuth client IDs — three sets of Google OAuth client IDs (web/iOS/Android) baked into Cloud Build substitutions: one set for dev, one for prod, plus an internal one for middleware tools like Argo CD itself.

If you need stronger isolation later (separate billing, separate IAM blast radius, separate APIs), the migration path is to split the GKE cluster and node pools into a new project; the Terraform code is already parameterised on var.project_id.

Terraform workflow

cd infrastructure/gcp
terraform init                            # connects to gs://development-485000-tfstate
terraform plan  -var-file=terraform.tfvars
terraform apply -var-file=terraform.tfvars

The infrastructure/gcp/README.md mentions workspaces (internal / development / production), but the live environment runs the default workspace and the terraform.tfvars checked in points at development-485000. Do not create new workspaces without first migrating state — there is none for them.

Provider quirks

  • The Google provider sets user_project_override = true and billing_project = var.project_id. This stops API calls from being billed to Google's internal gcloud client project (764086051850) when operators are using user ADC, which would otherwise fail with SERVICE_DISABLED.
  • The kubernetes and helm providers authenticate against the GKE cluster using the operator's current gcloud access token — there is no kubeconfig file involved. terraform apply therefore requires container.clusters.getCredentials on gke-tomoda.