10 · Project — Multi-Tier Microservices Platform¶
This project combines every Level 3 module into one working platform: an order-processing system running on OpenShift, connected across a multi-region network, secured and observed, governed by cost policy, and fronted by a managed API gateway.
Architecture¶
┌────────────────────────┐
partner apps ─────────▶ │ API Connect gateway │
└───────────┬─────────────┘
│ Route (TLS)
┌───────────▼─────────────┐
│ ROKS: orders-frontend │
└───────────┬─────────────┘
│ publishes
┌───────────▼─────────────┐
│ Event Streams: │
│ orders.created topic │
└──┬───────────┬───────────┘
consumes │ │ consumes
┌───────────────────▼┐ ┌──▼──────────────────┐
│ ROKS: inventory-svc │ │ ROKS: billing-svc │
└──────────┬──────────┘ └──────────┬───────────┘
│ │
┌──────────▼──────────┐ ┌───────────▼──────────┐
│ Databases for │ │ Databases for │
│ PostgreSQL (orders) │ │ PostgreSQL (billing) │
│ + us-east replica │ │ + us-east replica │
└───────────────────────┘ └────────────────────────┘
All VPC traffic routed through Transit Gateway; Direct Link to on-prem
warehouse system for inventory sync. Sysdig monitoring + Log Analysis
attached to the ROKS cluster. Key Protect root keys wrap the databases
and COS buckets. SCC's CIS profile evaluates the whole account.
Step 1 — Network foundation¶
ibmcloud is vpc-create platform-vpc --address-prefix-management manual
ibmcloud is vpc-address-prefix-create pfx-z1 platform-vpc us-south-1 10.30.0.0/20
ibmcloud is subnet-create private-z1 platform-vpc --zone us-south-1 --ipv4-address-count 128
ibmcloud tg gateway-create --name platform-tgw --location us-south
ibmcloud tg connection-add platform-tgw --network-type vpc \
--network-id $(ibmcloud is vpc platform-vpc --output json | jq -r .crn)
Step 2 — OpenShift cluster and namespaces per service¶
ibmcloud ks cluster create vpc-gen2 --name platform-roks \
--vpc-id $(ibmcloud is vpc platform-vpc --output json | jq -r .id) \
--subnet-id $(ibmcloud is subnet private-z1 --output json | jq -r .id) \
--zone us-south-1 --flavor bx2.4x16 --workers 3 --version 4.14_openshift
oc new-project orders-frontend
oc new-project inventory-svc
oc new-project billing-svc
Step 3 — Event Streams backbone¶
ibmcloud resource service-instance-create platform-events messagehub standard us-south
ibmcloud es topic-create orders.created --instance platform-events --partitions 3 --replication-factor 3
orders-frontend publishes to orders.created; inventory-svc and
billing-svc each consume it in their own consumer group, per Module 04
— neither downstream service blocks the other, and neither is a direct
dependency of the frontend.
Step 4 — Databases with cross-region replicas¶
ibmcloud cdb deployment-create orders-db --datacenter us-south --plan standard --version 15
ibmcloud cdb deployment-create orders-db-dr --datacenter us-east \
--replica-of $(ibmcloud cdb deployment orders-db --output json | jq -r .id)
ibmcloud cdb deployment-create billing-db --datacenter us-south --plan standard --version 15
ibmcloud cdb deployment-create billing-db-dr --datacenter us-east \
--replica-of $(ibmcloud cdb deployment billing-db --output json | jq -r .id)
Step 5 — Security: Key Protect + SCC¶
ibmcloud resource service-instance-create platform-kp kms tiered-pricing us-south
ibmcloud kp key create root-key-platform --instance-id <kp-guid> --standard-key false
ibmcloud scc profile-attach --profile "CIS IBM Cloud Foundations Benchmark" \
--scope-id $(ibmcloud account show --output json | jq -r .account_id) \
--instance-id <scc-guid>
Reference root-key-platform's CRN when creating both databases'
encryption configuration and the COS bucket used for order-archive
exports, so one key rotation event covers every regulated data store in
the platform.
Step 6 — Observability¶
ibmcloud resource service-instance-create platform-monitoring sysdig-monitor graduated-tier us-south
ibmcloud ob monitoring config create --instance platform-monitoring --cluster platform-roks
ibmcloud ob monitoring alert-create --instance platform-monitoring \
--name orders-frontend-error-rate \
--condition 'avg(sysdig_http_error_rate{kube_namespace_name="orders-frontend"}) > 0.05' \
--duration 300 --severity high
Step 7 — Governance and cost tags¶
ibmcloud resource tag-attach \
--resource-id $(ibmcloud is vpc platform-vpc --output json | jq -r .crn) \
--tag-names env:prod,team:platform,cost-center:eng-42
ibmcloud billing budget-create --name platform-monthly-budget \
--amount 3000 --resource-group-name mastery-path --alert-percentages 50,80,100
Step 8 — Front it with API Connect¶
ibmcloud resource service-instance-create platform-apic api-connect professional us-south
ibmcloud apic draft-apis:create --server platform-apic orders-api.yaml
ibmcloud apic products:create --server platform-apic --title "Orders API" \
--apis orders-api:1.0.0 --plan default --rate-limit "1000/1hour"
ibmcloud apic products:publish --server platform-apic orders-api-product:1.0.0 \
--catalog production-catalog --space default
Verification checklist¶
- [ ]
oc get pods -Ashowsorders-frontend,inventory-svc, andbilling-svcallRunning, each with its Route reachable. - [ ] A test order posted through the API Connect gateway produces an
orders.createdevent that both consumers process independently (confirm via consumer group offsets, not just app logs). - [ ]
ibmcloud cdb deployment-connections orders-db-drshows an active replica relationship toorders-db. - [ ]
ibmcloud scc resultsshows at least one completed scan cycle against the account. - [ ] The Sysdig dashboard shows live metrics for all three namespaces,
and the error-rate alert policy exists (
ibmcloud ob monitoring alert-list). - [ ]
ibmcloud billing resource-group-usageshows tagged spend forteam:platform.
Terraform composition (top level)¶
module "network" {
source = "./modules/ha-vpc"
region = "us-south"
}
module "cluster" {
source = "./modules/roks-cluster"
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
}
module "events" {
source = "./modules/event-streams"
topics = ["orders.created"]
}
module "databases" {
source = "./modules/postgres-with-dr"
primary_region = "us-south"
dr_region = "us-east"
names = ["orders-db", "billing-db"]
}
How It Actually Works¶
- The decoupling this platform relies on holds because
inventory-svcandbilling-svcare two independent consumer groups reading the same Kafka partition log, not two subscribers on a fan-out bus. Each group maintains its own committed offset intoorders.created; a slow or crashedbilling-svcdoesn't blockinventory-svc's consumption because Kafka retains the log and serves each group from wherever its own offset sits — addingnotifications-svcin the stretch goal is purely "start a third consumer group at whatever offset it chooses," which is why it requires zero change to the producer side. - One root key covering both databases and the COS bucket means one Key Protect rotation event re-wraps every data-encryption key those services hold a reference to — it doesn't touch or re-encrypt the underlying data itself, per the envelope-encryption mechanism from Module 03. That's the operational payoff of standardizing on a single platform-wide root key: a security incident response that needs to revoke and rotate a key does it once, for everything the platform encrypts, instead of hunting down every service's own key.
- The Sysdig alert and the API Connect rate limit protect two different
layers of the same request path and can both be true simultaneously.
A caller hammering
/ordersfirst hits API Connect's per-plan counter (Module 09) — if under the limit, the request proceeds to the ROKS Route and only then shows up in Sysdig's per-namespace error-rate metric (Module 05) if the backend itself starts failing. A spike in gateway 429s and a spike in backend 5xx alerts are diagnosing different problems even though both fire from the same burst of traffic. - Cross-region DR readiness here is only as real as the last drill —
the replica relationship shown by
deployment-connectionsproves replication is live, not that promotion and failback actually work end-to-end, which is exactly why Module 06 treats a timed promotion drill as the real verification, and why this project's checklist checks for an active replica but the stretch goal insists on actually running the failover.
Stretch goals¶
- Add a fourth consumer service (
notifications-svc) toorders.createdwithout modifyingorders-frontend, proving the decoupling actually holds. - Wire SCC findings into Event Notifications so a failed control posts to a chat channel automatically.
- Run a full failover drill: promote
orders-db-dr, repointorders-svcconfig, and measure the real RTO end-to-end. - Add a second API Connect product version (
v2) with a breaking change to the/ordersschema, and keepv1callers unaffected. - Turn the manual verification checklist into a script that checks each item via CLI and exits non-zero on any failure.