Home

/

The Production-Ready Playbook

/

Orchestrator-Agnostic Deploy

Orchestrator-Agnostic Deploy

Chapter 9
Part II
7
min read

Orchestrator-Agnostic Deploy

Here is where cloud-agnostic stops being a slogan and becomes a property you can test. Because the unit is an OCI image and the cart, the Order, and the menu live outside it, the same order-service image runs unchanged on Cloud Run, on ECS, on Container Apps, or on Kubernetes. The orchestrator's job is to start your container, route traffic to it, restart it when it dies, and read its health endpoint. Those are standard verbs. Nothing in your code names the platform.

One image, any cloud

The deployment differences live in thin, swappable wrappers: a Cloud Run service YAML, an ECS task definition, a Container Apps manifest, a Kubernetes Deployment. Each says the same things in a different dialect: which image, which port, how much memory, which health path, how many instances. You write the wrapper once per target and the image is the constant.

What it buys you in production: a real exit. When the bill, the region story, or a client's compliance requirement pushes you to another cloud, you move the wrapper, not the application. For a fractional team with no platform group to run a migration project, that optionality is the whole point of the altitude.

Lock-in is rarely a decision. It's the accumulation of small assumptions that each cloud invited you to make. The container boundary is where you refuse them.

Skip-if: you are certain of one cloud forever and the proprietary primitives genuinely buy you something the portable path can't. Be honest that "certain forever" is a strong claim, and that the portable path costs little to hold open.

Infrastructure as Code

A reproducible environment you can recreate from a file is how a small team stays cloud-agnostic without a platform team. Infrastructure as Code (Fowler, InfrastructureAsCode) means the order service, its orders database, its OrderPlaced queue, and its secrets are declared in version-controlled files (Terraform, Pulumi, Bicep), applied by a tool, and reviewed like any other change. The click-ops console is where reproducibility goes to die: nobody remembers which checkbox in which dropdown made staging different from production.

resource "google_cloud_run_v2_service" "order_service" {
  name     = "order-service"
  location = var.region
  template {
    containers {
      image = var.image
      ports { container_port = 8080 }
    }
    scaling { min_instance_count = 1 }
  }
}

What it buys you in production: a new environment in minutes instead of a day of console archaeology, a diff you can review before it touches anything, and a blast radius you can reason about. When the same code provisions staging and production, the gap between them stops being a source of 3am surprises. It is also where orchestrator-agnostic deploy becomes routine, because the per-target wrapper is just another file under review.

Skip-if: a weekend prototype with one manually clicked service that you will throw away. The instant a second environment or a second person appears, ad-hoc console state becomes the most expensive thing you own. Codify before then.

Blue-Green / Canary Deploy

Shipping a new version should not require a maintenance window, and a bad version should not require a panic. Take the change that scares you most: a rewritten pricing engine, the one that computes surge, promo, and loyalty on top of the menu price. Get it wrong and every customer sees a wrong total. Blue-Green Deployment (Fowler, BlueGreenDeployment) keeps two production environments: blue runs the current pricing engine, green holds the new one. You deploy to green, smoke-test it against real menus and real tax rules, then switch traffic in one move. If green misprices, you switch back. The rollback is instant because the old version never went away.

Canary Release (Danilo Sato) is the gradual cousin: route the new pricing engine to 5% of orders, watch the golden-signal metrics from the observability altitude plus your own "totals matched" check, and ramp up only if error rates and latency hold. If they don't, 5% of orders saw the bug, not 100%. Most managed runtimes give you traffic splitting natively, so this is configuration, not a custom router.

# Cloud Run: deploy the new pricing engine without traffic, then send it 5% of orders
gcloud run deploy order-service --image $IMAGE --no-traffic
gcloud run services update-traffic order-service \
  --to-revisions LATEST=5

What it buys you in production: zero-downtime rollout and a rollback measured in seconds, not in how fast someone can rebuild the previous pricing engine while the totals are wrong and support is filling up. The canary turns a deploy from an act of faith into a measured one, watched by the metrics you already wired up at the observability altitude.

Skip-if: an internal restaurant-onboarding tool with a handful of staff users where a few seconds of restart costs nothing. A rolling restart is simpler and it's fine. The pattern earns its place when downtime has a real cost or a rollback has to be instant, which a customer-facing pricing change always is.

Sidebar: Secrets Management

Secrets are the one piece of externalised state you must not treat like ordinary config. Deep security was book #1's job; the hosting-altitude rule is short. The orders-database password, the payment-gateway API key, the token-signing key: none of them belongs in the image, in the repo, or in a plaintext environment variable baked into a manifest. Put each in a managed secret store (GCP Secret Manager; AWS Secrets Manager, Azure Key Vault) and let the runtime inject it at start, so the order-service image stays portable and the secret stays out of source control.

// Orders-DB connection pulled from the platform's secret store at startup, never committed
var connString = builder.Configuration["Secrets:OrdersDb"];

Two habits cover most of the risk: rotate secrets on a schedule so a leak has a short half-life, and grant each service read access only to the secrets it needs. The courier service has no business reading the payment key. That keeps secrets cloud-agnostic in the same way everything else here is: the application asks for a value by name and never knows which vault answered.

Honorable Mentions

Four patterns that just missed the cut, for the reader going further. Feature Flags (Fowler, feature toggles) decouple deploy from release: ship the new pricing engine dark, turn it on for one city, and kill it without a rollout. They pair naturally with canary and are the cheapest way to de-risk a change once you have more than a trickle of orders. Gateway / Backend-for-Frontend (Sam Newman) puts a tailored API edge in front of your services, one per client type. The customer app and the courier app want very different things from the same Order: the customer wants the menu, the running total, and a live map; the courier wants the pickup address, the route, and a single "picked up" button. A shared contract that serves both ends up bloated for each. A customer BFF and a courier BFF each shape the order data to one screen, and neither leaks the other's concerns into the core service.

In the front-end. This is where the client stack meets the hosting altitude. The customer app (React / TypeScript) talks only to its BFF and never to the order, menu, and courier services directly; the courier app talks only to its own. Each BFF is the seam where one client's view of the Order is assembled, so a change to the courier screen never risks the customer total.

Secrets Management sits here too as a full pattern, expanded just above into the sidebar it deserves at this altitude. Service Discovery lets the order, menu, and courier services find each other by name rather than by hardcoded address. On the managed runtimes in this book it's usually handled for you (Cloud Run URLs, internal DNS), which is exactly why it's a mention and not a slot. Reach for the explicit pattern on Kubernetes or a service mesh, and not before three services have turned into thirty.

Seven altitudes, one working vocabulary. Now watch it compose into one real service.

the-pareto-stack-cloud-design-patterns-for-small-teams
the-ladder-of-altitudes
how-to-read-this
object-level-the-patterns-that-earn-their-keep
decorator
state
component-level-structuring-one-service
ports-and-adapters-hexagonal
mediator-the-commandquery-split
data-persistence
optimistic-concurrency
messaging-scale
outbox
resilience-staying-up-when-dependencies-dont
rate-limiting-throttling
timeout-fallback
the-composed-pipeline
observability-diagnostics-seeing-inside-production
metrics-the-four-golden-signals
externalised-configuration
hosting-cloud-agnostic-by-default
sidecar-ambassador
orchestrator-agnostic-deploy
a-reference-service
the-relay-outbox-to-queue
the-payment-saga-charge-pay-out-compensate
the-over-engineering-tax
conclusion-production-ready-deliberately
the-pattern-quick-reference-card
altitude-3-data-persistence
altitude-5-resilience
the-skip-list
full-event-sourcing-for-crud
robert-c-martin-uncle-bob-the-house-authority-for-structure
altitude-2-component
altitude-4-messaging-scale
altitude-6-observability-diagnostics

Download the full PDF for free?

Free download — no account required

Get the PDF
Get the PDF
Related Chapters
Free Download
Get the full PDF
All pages, including all code examples, diagrams, and the appendix reference card.
No spam. Unsubscribe at any time.
Your email won't be shared.
Oops! There's a problem with your request. We're working on fixing it. Please try again later.