Skip to main content

Infrastructure

Infrastructure

This page describes the infrastructure the CM Marketplace platform runs on — GCP services, Terraform modules, networking, secrets, CI/CD, and container setup.


1. Cloud Provider and Region​

PropertyValue
Cloud ProviderGoogle Cloud Platform (GCP)
Production Regioneurope-west4 (Eemshaven, Netherlands)
Acceptance Regioneurope-west4
IaC ToolTerraform 1.7.4
CI/CDGitHub Actions
Container RegistryGoogle Artifact Registry (europe-west4-docker.pkg.dev)

2. Terraform Module Structure​

Infrastructure is organized as reusable Terraform modules composed in per-environment configurations:

infra/
modules/
cloudrun/ # Cloud Run service deployment
database/ # Cloud SQL PostgreSQL
pubsub/ # Pub/Sub topics and subscriptions
cloudtasks/ # Cloud Tasks queues
redis/ # Memorystore Redis
gke/ # GKE Autopilot cluster
vpc_network/ # VPC, subnets, NAT, access connectors
secret_manager/ # Secret Manager access
iam/ # Service accounts and IAM roles
alerting/ # Monitoring alert policies
cloudscheduler/ # Cloud Scheduler jobs
workflow/ # Cloud Workflows (data sync templates)
google_apis/ # API enablement
environments/
production/ # Production config (composes all modules)
acceptance/ # Staging/acceptance config
payhub/ # PayHub payment environment

3. GCP Services Used​

Compute​

ServicePurposeConfig
Cloud RunAll API microservices (11 services)1 vCPU, 1-2Gi RAM, autoscale 1-10
GKE AutopilotLong-running background workersFully managed, regional

Data​

ServicePurposeConfig
Cloud SQLPostgreSQL 14 databaseRegional HA, SSD, auto-resize, PITR
MemorystoreRedis cache1GB BASIC, direct peering
Cloud StorageFile storage (product feeds, exports)Standard storage

Messaging​

ServicePurposeConfig
Cloud Pub/SubAsync inter-service events2 topics, 12 subscriptions, 7-day retention
Cloud TasksRate-controlled task dispatch4 queues, 1000 concurrent, 500/sec
Cloud SchedulerCron-based job triggeringHTTP and Pub/Sub targets
Cloud WorkflowsMulti-step data sync orchestration4 workflow templates

Security and Operations​

ServicePurposeConfig
Secret ManagerSecrets storage9 secret categories, per-service
IAMService account managementPer-service accounts, least privilege
Cloud MonitoringAlert policies5xx error alerting, 60s detection
Cloud LoggingStructured loggingSerilog sink, all services

Networking​

ServicePurposeConfig
VPCPrivate networkingRegional, auto-subnets
Cloud NATOutbound static IPManual IP allocation, shared NAT
VPC Access ConnectorCloud Run to VPC bridge2-10 instances, 200-1000 Mbps

4. Networking Architecture​

Internet
|
v
Cloud Run (public endpoints)
|
+-- VPC Access Connector (shared-nat) --+
| |
v v
Cloud SQL (private IP) Redis (direct peering)
|
v
Cloud NAT (static IP) --> External APIs (Salesforce, Dynamics, etc.)

VPC Network​

# infra/modules/vpc_network/network.tf

resource "google_compute_network" "default" {
auto_create_subnetworks = true
routing_mode = "REGIONAL"
}

Cloud NAT with Static IP​

# infra/modules/vpc_network/router.tf

resource "google_compute_router_nat" "shared-nat" {
nat_ip_allocate_option = "MANUAL_ONLY"
nat_ips = [google_compute_address.static-marketplace.id]
min_ports_per_vm = 64
tcp_established_idle_timeout_sec = 1200
tcp_time_wait_timeout_sec = 120
tcp_transitory_idle_timeout_sec = 30
}

All outbound traffic exits through a single static IP, enabling IP allowlisting with external partners (Salesforce, Dynamics, etc.).

VPC Access Connector​

# infra/modules/vpc_network/access_connector.tf

resource "google_vpc_access_connector" "shared-nat" {
machine_type = "f1-micro"
max_instances = 10
min_instances = 2
max_throughput = 1000
min_throughput = 200
}

5. Secrets Management​

Secrets are stored in Google Secret Manager and organized by service:

# infra/environments/production/main.tf

secret_list = [
"common", # Shared across all services
"product_feed", # Product feed specific
"configuration", # Configuration service specific
"crm", # CRM adapter secrets
"livechat", # Livechat adapter secrets
"cms", # CMS adapter secrets
"comms", # Comms adapter secrets
"integrations", # Integration secrets
"cdp" # CDP adapter secrets
]

Secrets are injected as environment variables at Cloud Run startup via Terraform:

env_vars = merge(
local.run_env.common.redis,
local.run_env.common.gcloud,
nonsensitive(module.secret_manager.run_instance_secrets.crm),
nonsensitive(module.secret_manager.run_instance_secrets.common)
)

6. IAM and Service Accounts​

Each service runs under a dedicated service account with least-privilege roles:

# infra/environments/production/main.tf

sa_roles = {
crm = ["roles/cloudsql.client", "roles/cloudtasks.enqueuer",
"roles/pubsub.publisher", "roles/redis.dbConnectionUser",
"roles/cloudscheduler.admin"]
livechat = ["roles/cloudscheduler.admin", "roles/cloudtasks.enqueuer",
"roles/redis.dbConnectionUser"]
configuration = ["roles/cloudsql.client", "roles/cloudtasks.enqueuer",
"roles/pubsub.publisher", "roles/redis.dbConnectionUser",
"roles/cloudtasks.viewer", "roles/cloudtasks.taskDeleter"]
cdp = ["roles/redis.dbConnectionUser", "roles/storage.objectUser",
"roles/cloudscheduler.admin"]
cms = ["roles/cloudscheduler.admin", "roles/cloudtasks.enqueuer",
"roles/redis.dbConnectionUser", "roles/storage.objectUser",
"roles/secretmanager.secretAccessor"]
product-feed = ["roles/cloudtasks.enqueuer", "roles/storage.objectUser",
"roles/redis.dbConnectionUser",
"roles/iam.serviceAccountUser"]
comms = ["roles/cloudtasks.enqueuer",
"roles/redis.dbConnectionUser"]
jsltservice = ["roles/logging.logWriter"]
integrations = ["roles/logging.logWriter"]
frontend = ["roles/logging.logWriter"]
}

7. Monitoring and Alerting​

5xx Error Alert​

# infra/modules/alerting/main.tf

resource "google_monitoring_alert_policy" "cloudrun_5xx_alert" {
display_name = "Cloud Run - Error Alert"
combiner = "OR"
enabled = true

conditions {
display_name = "Cloud Run service has 5xx errors"
condition_threshold {
filter = "resource.type = \"cloud_run_revision\" AND
metric.type = \"run.googleapis.com/request_count\" AND
metric.labels.response_code_class = \"5xx\""
duration = "60s"
comparison = "COMPARISON_GT"
threshold_value = 2
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_DELTA"
group_by_fields = ["resource.labels.service_name",
"metric.labels.response_code"]
}
}
}

alert_strategy {
auto_close = "1800s"
}
}

Triggers when any service returns more than 2 5xx errors in 60 seconds. Auto-closes after 30 minutes.


8. Container Setup​

All services use a standard Dockerfile running as non-root:

# Example: src/app-crm/.../Dockerfile

FROM proget2.sharedservices.cmgroep.local/dockerfeed/trusted/dotnet/aspnet:10.0

RUN groupadd --gid 1001 appuser \
&& useradd --uid 1001 --gid 1001 -m appuser

USER appuser
WORKDIR /app
COPY ${BUILD_DIR} ./
EXPOSE 80
EXPOSE 443
ENTRYPOINT ["dotnet", "CM.Marketplace.Adapters.CRM.Web.API.dll"]

9. CI/CD Pipeline​

Pipeline Flow​

Developer pushes to main
|
v
GitHub Actions: Build + Test + Coverage + SonarQube
|
v
Docker Build --> Push to Artifact Registry (europe-west4)
|
v
Auto-deploy to Acceptance (Terraform apply)
|
v
Database migration (HTTP API call)
|
v
Manual trigger / repository_dispatch
|
v
Deploy to Production (Terraform plan + apply)

Production Deployment​

# .github/workflows/deploy-prod.yaml

name: Deploy to Production
on:
workflow_dispatch:
repository_dispatch:
types: [artifact_committed_prod]

jobs:
deploy:
runs-on: engage-hosted
environment: Production
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with: { terraform_version: 1.7.4 }
- uses: google-github-actions/auth@v2
with: { credentials_json: "${{ secrets.GCP_CREDENTIALS }}" }
- run: terraform init
- run: terraform plan -var-file="production.tfvars.json" -out=plan
- run: terraform apply --auto-approve "plan"

Enabled Google APIs​

# infra/modules/google_apis/locals.tf

apis = {
compute = "compute"
cloudrun = "run"
serverlessvpc = "vpcaccess"
secretmanager = "secretmanager"
cloudfunction = "cloudfunctions"
}

10. Environment Comparison​

AspectProductionAcceptance
DB Tierdb-custom-1-3840db-f1-micro
DB AvailabilityREGIONALZONAL
Cloud Run Memory1-2Gi512Mi
Cloud Run Max Scale101-5
Cloud Run Min Scale1-20-1
Deploy TriggerManual / repository_dispatchAuto on main push
Environment ProtectionRequired approvalNone

Last updated: May 2026