Design Patterns · Principal Track

Designing Istio for the Enterprise: One Architecture at Five Resolutions

A multi-cloud service mesh is not a diagram — it is a stack of decisions, and the expensive ones are made long before anyone writes YAML. This is a working design document you can step through: one architecture held fixed and redrawn five times, from the constraints it has to survive, through the bar it is graded against, down to the replica counts an SRE stands up, with the argument behind every element on screen.

Scope. A service mesh for a large Kubernetes estate — two or more clouds, five or more clusters, workloads that cross trust and regulatory boundaries. Verified against the Istio 1.24 / 1.25 track; ambient mesh and the Kubernetes Gateway API both move between minors, so re-check version-sensitive decisions at each upgrade. Physical values (CIDRs, replica counts, circuit sizing) are illustrative placeholders, not observed values — see the footer.

Service-mesh designs tend to fail the same way. Someone draws a topology, everyone nods, and eighteen months later the estate is wedged behind a choice nobody remembers making. The trust domain is wrong and every identity would have to be re-issued to fix it. The network model assumed a flat, non-overlapping pod address space that was never going to arrive. Config was never scoped, and istiod is now the least reliable component in the fleet.

None of those are implementation mistakes. They are decisions that got made implicitly, by a picture, instead of explicitly, by an argument. A diagram is very good at showing what you built and very bad at showing what you rejected — and in mesh design, the rejected option is usually the whole point.

So this design is drawn five times. Three of those passes — Environment, Logical, Physical — share one topology canvas: the same nine blocks in the same nine positions, gaining resolution as a vague constraint becomes a named component and then a sized, placed instance. Every step you land on carries the decision that put the element there: the options genuinely considered, the recommendation, the reason, and the cost you are accepting. Fifty-one of them, traced back to a 75-decision register.

Two passes deliberately break that constancy, and it is worth saying why. Requirements is a board, not a topology — a bar has no deployment shape. And the Conceptual pass has its own decomposition for a sharper reason. A capability model that inherits the deployment topology is not a conceptual design — it is a logical design with the product names filed off. The test is whether the picture survives a change of technology: if swapping Istio for a mesh that enforces in eBPF at the node would redraw it, then "workload proxy" was never a capability, it was Envoy wearing a generic label. So tab 2 has its own decomposition — capabilities layered by dependency, no locations, no counts — and a mapping table below carries you from there into the named components. The mapping is many-to-many, which is exactly why it cannot be the same picture.

The verdict

  1. One mesh where it buys shared identity; a separate federated mesh only at a hard boundary. Prod versus non-prod, and each strong regulatory zone. Splitting further throws away the reason you adopted a mesh.
  2. Multi-primary control planes — one istiod per cluster. No cluster sits on another cluster's config path, so losing one degrades one.
  3. Multi-network, with an east-west gateway per network. Flat cross-cloud pod routing is a multi-year IPAM programme that is not coming. Bridging costs one hop and is true on day one.
  4. One trust domain per mesh, under the enterprise root, with a per-cluster intermediate. Cross-cluster identity with local issuance. This one is permanent.
  5. Config scope is a day-one requirement, not an optimisation. Discovery selectors, Sidecar and exportTo are cheap at five clusters and near-impossible to retrofit at fifty.
  6. Revisioned canary upgrades, promoted per namespace. Blast radius is one namespace and rollback is a label change — the only version of this that stays safe at fleet scale.

How to read the five resolutions

1 · EnvironmentThe world the design has to survive: who calls in, where it runs, what the network actually is, and the numbers it is measured against. No solution on the canvas.
2 · RequirementsThe bar the design is graded against, stated so each one can be verified — plus the constraints it assumes rather than delivers.
3 · ConceptualA capability model with its own shape — layered by dependency, no locations, no products, no counts. The one tab that is not the topology.
4 · LogicalNamed components, protocols and the flows between them. Real products — but no instance counts, subnets or regions yet.
5 · PhysicalWhat gets stood up: replicas, sizing, load-balancer types, subnets, ports and circuits. The values an IaC module encodes.

Watch block 5, the connectivity box in the middle of the canvas, across the three topology tabs — “pod CIDRs are not routable between clouds” becomes “bridged, not flat”, becomes “mTLS end to end, SNI-routed at the gateway”, becomes “2×10G Direct Connect, :15443 to the peer gateway and :443 to the peer API server”. Same block, same position, three depths. Use ← → to step.

51 stepped decisions from a 75-decision register Istio 1.24 / 1.25 track ← → to step
Resolution
Flows traffic & connectivity control & config identity & trust telemetry structural, not configured Borders a real container the mesh, or outside it
One enterprise Istio architecture, drawn as a reference topology Consumers, the enterprise PKI and the GitOps controller sit outside the estate. A dashed mesh boundary spans two cloud regions. Each region contains a VPC drawn with its named subnet tiers in the order traffic crosses them: a public subnet holding the load balancer, a gateway subnet holding the ingress gateway and the east-west gateway, and a private subnet holding an availability zone with a pod — an application container beside its Envoy sidecar — plus istiod and the intermediate CA. North-south traffic enters the load balancer, terminates TLS at the ingress gateway, then reaches the pod. A private interconnect joins the two east-west gateways. Below the mesh sit the non-prod mesh, the regulated mesh, the central telemetry store, and the scale and cost envelope. The stepper lights the elements and flows each design decision touches. 1 2 3 outside the mesh — the mesh depends on these, it does not own them 4 6 5 7 8 9 10
structural path (exists regardless of config) control & config data plane connectivity & gateways identity & trust telemetry

The dashed teal envelope is the prod mesh — one of three. It is a single trust domain spanning both estates, which is why a workload in either can prove who it is to the other. Two things make that envelope real rather than decorative: every workload inside it chains to one root CA, and every control plane inside it holds a remote secret for every other, so they share one registry. Networks nest inside a mesh rather than the other way round — crossing a network costs a gateway hop, while crossing a mesh costs trust-bundle federation, which is why there are three of these and not thirty. The other two meshes (7 and 8) sit below it with their own trust domains: the regulated one federates with prod for three exported services, the non-prod one is isolated on purpose. The envelope carries no number because it is not a component — it is the boundary the numbered components sit inside, and trust, config and consumers all arrive from outside it. It is a scope, not a perimeter. Nothing is deployed on that line and nothing is filtered at it; a workload inside the mesh can still reach anything the network permits. What the boundary decides is what comes for free: inside it, one workload can authenticate to another with no per-edge setup, a service name means one service wherever it runs, and a policy naming a principal means the same thing in every cluster. It also decides blast radius — one policy vocabulary is also one place a mistake reaches — which is the actual reason non-prod is a separate mesh rather than a namespace with rules on it. Domains A1, A4, E2.

  1. Consumers & the ingress edge — who arrives from outside, and the one sanctioned way in and out. Domains D, E8.
  2. Root of trust — where workload identity is anchored and how far it reaches. Domain E; one-way doors.
  3. Config governance — how a change to the mesh gets reviewed, promoted, and reverted. Domains F, J, M.
  4. Estate A — a cloud fleet: its control plane, data plane, gateways and local trust anchor. Domains A, B, C.
  5. Cross-location connectivity — what carries service-to-service traffic between estates. Domains A3, D.
  6. Estate B — the second cloud fleet, deliberately drawn identically. Domains A, B, C, K.
  7. Non-prod mesh — its own trust domain, and deliberately not federated to prod. Domains A1, A4.
  8. Regulated mesh — its own trust domain, joined to prod by trust-bundle exchange for three exported services. Domains A1, A4, E9.
  9. Observability — the signals every estate emits and where they land. Domain H.
  10. Scale & cost envelope — the read-out the design is measured against, not a component. Domains I, L.

Capability → component: how the conceptual tab lands

Because tab 2 has its own decomposition, it needs an explicit bridge into the named components. Note how little of this is one-to-one: a single capability lands in several components, and one component — the gateway — carries three different capabilities at once. That many-to-many mapping is the reason a capability model cannot just be the topology with the labels changed.

Capability (tab 2)Resolves to (tab 3)Stood up as (tab 4)
Workload identitySPIFFE IDs, signed locally by each cluster's istiod24h certificates, rotated at half-life without a restart
Trust anchoring & federationenterprise root → per-cluster intermediate; trust-bundle exchange across meshesoffline HSM root, Vault-issued 365d intermediates delivered as cacerts
Authenticated transportmTLS, PeerAuthentication STRICTpermissive → strict per namespace, port exceptions catalogued
AuthorizationAuthorizationPolicy, default-denyaudit mode first, baseline allows generated from observed callers
Traffic controlrouting + destination rules, locality LB, outlier detectionplatform defaults per workload class; canary weights
Policy distribution & convergenceistiod serving xDSistiod ×3, sized from the watch set; push-latency SLO
Reachability & discoveryremote secrets, network labels, service entriescross-cluster endpoint discovery over the interconnect
North-south ingressone ingress gateway per exposure tier (public / internal / partner), authored through the Kubernetes Gateway APIa Deployment per tier, ×3 across AZs, each behind its own L4 NLB; TLS 1.3 terminates at the gateway, not at the LB
Boundary mediationingress and east-west gateways, plus a declared egress posture — one capability, two deployments and a firewallingress tiers behind L4 NLBs, internet-facing for public and partner and internal for corp; east-west Envoy ×3 behind an internal NLB on :15443; outbound declared as ServiceEntry under REGISTRY_ONLY and enforced by a VPC egress firewall onto one NAT address per AZ — no Istio egress gateway
Segmentation & isolationa separate federated mesh per risk tierPCI mesh on its own clusters, own root, own Argo project
Config governanceGitOps repo + admission policy + the three scoping levelsArgo CD app-of-apps, OPA/Kyverno gates
Tenancy & ownershipnamespace as tenant, RBAC split, ownership labelsone Argo project per mesh; CODEOWNERS on mesh paths
Lifecyclerevisions and revision tagscanary promotion per namespace; rollback is a label change
Observability & assurancemetrics, traces, access logs; derived topology; control-plane SLOsmetrics agent + OTel collector; 15s scrape, 1% tail sampling, 30d hot / 13mo
Capacity & cost envelopethe three scale controls~0.7 vCPU + 1Gi per 10 meshed pods, transfer attributed per tenant

The spine: one concern, five resolutions

Progressive resolution only works if you can follow a single concern all the way down. Cross-cluster connectivity is the clearest thread — note that it is not a block at all in two of the five resolutions, and at the conceptual layer it becomes two capabilities, which is the mapping above doing its job:

ResolutionWhat block 5 saysThe decision captured
EnvironmentWorkloads span two clouds and on-prem; VPCs are separate and CIDRs may overlap. Cross-cloud L3 is a network-team dependency with its own latency and egress bill.Assumption: a non-flat network is a given, not a choice. Flat cross-cloud pod routing is treated as unavailable.
RequirementsNot a block at all — a stated bar: a workload must be able to call a peer in another location, within the per-hop latency budget, with cross-location transfer attributed to whoever caused it.What the bar is, and how it is checked → verified by a cross-location failover test and a per-hop latency budget, not by assertion.
ConceptualTwo capabilities, not one block: reachability & discovery (a workload can reach a peer wherever it runs) and boundary mediation (anything crossing passes one control point). Neither mentions a location.Flat address space vs separate spaces bridged → separate, bridged at a mediated boundary. ⚠ one-way
LogicalEast-west gateway per network doing SNI passthrough, plus cross-cluster endpoint discovery and topology.istio.io/network labels.How traffic crosses a boundary → per-network gateway, mTLS never terminated at the hop; endpoint discovery via remote secrets.
Physicalistio-eastwestgateway ×3 across zones behind an internal L4 load balancer on 10.20.0.0/24, reaching the peer over 2×10G Direct Connect; only :15443 to the peer gateway and :443 to the peer cluster's API server are open.Sizing, placement and the port contract → three replicas, private subnet, narrow firewall contract, transfer metered per tenant.

Every major concern in the design should thread like that. If one only appears in a single tab, either it is genuinely scoped to that depth or a resolution is incomplete.

⚠ The five one-way doors

Decision register — every step on this canvas

StepResolutionDecisionRecommendationSource

Rollout — the order that keeps the doors open

The sequence matters as much as the design. The one-way doors are decided first, before anything is installed, because every later phase assumes them.

0
Foundationsweeks 0–4
  • Decide the one-way doors: mesh split, trust domains, CA topology, network model.
  • Stand up CA integration, the GitOps repo structure and the supported-platform matrix.
  • Build a staging mesh that mirrors the target topology.
1
Single-cluster pilotweeks 4–8
  • Install via Helm and GitOps with revisions in one non-prod cluster; mTLS permissive.
  • Enforce config scoping and the injection strategy from the first namespace.
  • Onboard one or two friendly teams; stand up observability.
2
Hardenweeks 8–14
  • Flip pilot namespaces to strict mTLS; roll default-deny authorization in audit, then enforce.
  • Add admission guardrails, the RBAC split, egress posture and the ingress pattern.
  • Define control-plane SLOs; rehearse rollback.
3
Multi-clusterweeks 14–22
  • Go multi-primary and multi-network with east-west gateways; wire remote secrets.
  • Enable cross-cluster discovery and locality failover; test cluster loss deliberately.
  • Roll to more production clusters in waves.
4
Multi-cloudweeks 22–30
  • Extend to the second cloud over the private interconnect; federate trust if the mesh is split.
  • Admit the on-prem / OpenShift estate as another network, on the same gateway-and-remote-secret pattern.
  • Validate cross-cloud mTLS, DNS, failover and data-transfer cost.
  • Stand up the regulated-zone mesh, with a FIPS build if in scope.
5
Scale & optimiseongoing
  • Pilot ambient for high-density namespaces; tune convergence against the SLOs.
  • Migrate north-south to Gateway API where ready; mature progressive delivery.
  • Continuous N-2 upgrades; quarterly DR and chaos game-days.

Who owns what

The platform team runs the paved road; app teams drive on it. The split below is what makes the guardrails enforceable rather than advisory — R/A is responsible and accountable, C is consulted.

ActivityMesh platformApp teamsSecurityNetwork
Control plane run & upgradeR/AICI
Gateways & the shared ingress edgeR/ACCC
Namespaced routing configCR/AII
Namespaced authorization policyCR/ACI
Mesh-wide policy & defaultsR/AICI
CA / PKI & key custodyCIR/AI
Trust domain & federationRIAI
Cross-cloud interconnectCICR/A
Admission guardrailsR/AICI
Observability platformR/ACII

What is most likely to go wrong

RiskImpactMitigation
Unscoped config overwhelms the control plane at scaleHighEnforce all three scoping levels through admission policy from day one; watch push latency as an SLO.
Wrong trust domain or CA topology chosenHighTreat as one-way doors decided in phase 0 with security in the room; back up the root.
Strict mTLS rollout breaks non-meshed callersMedStaged permissive → strict per namespace, with port exceptions catalogued before the flip.
Default-deny authorization causes a mass outageHighRoll out in audit mode first; generate the baseline allow list from observed caller identity.
CA key lossCriticalHSM-backed root, tested restore, dual-trust rotation runbook.
Multi-primary remote-secret fan-out at fleet scale — every control plane watches every peer, N×(N−1)HighCap clusters per mesh and shard by tier or region rather than growing one mesh; use a shared control location for small edge clusters; alert on watch count and push latency per control plane.
Observability cost explosion from cardinalityMedTelemetry pruning, sampling and retention tiers designed in, not added after the first bill.

References

  1. Istio — Deployment models (single vs multi-cluster, network models, control-plane models, tenancy)
  2. Istio — Install multicluster (multi-primary, primary-remote)
  3. Istio — Security (mTLS, PeerAuthentication, AuthorizationPolicy)
  4. Istio — Plug in CA certificates (per-cluster intermediate)
  5. Istio — Performance and scalability (config scoping, sizing)
  6. Istio — Canary upgrades with revisions
  7. Istio — Ambient mesh
  8. SPIFFE — trust domains and federation
  9. cert-manager — istio-csr
  10. Kubernetes Gateway API