Part 5 of 946 min read · 22 diagramsAI-assisted

Managed Kubernetes: EKS, AKS, GKE

Table of Contents#

  1. Why Managed Kubernetes Exists
  2. The Shared Responsibility Model, Applied to Kubernetes
  3. What "Managed" Actually Covers, and What It Doesn't
  4. Amazon EKS — Architecture
  5. EKS Compute Options: EC2 Node Groups vs Fargate
  6. EKS Identity: IRSA
  7. EKS Networking: The VPC CNI
  8. Azure AKS — Architecture
  9. AKS Compute Options: VM Scale Sets vs Virtual Nodes
  10. AKS Identity: Azure AD Workload Identity
  11. AKS Networking: Kubenet vs Azure CNI
  12. Google GKE — Architecture
  13. GKE's Big Differentiator: Autopilot vs Standard
  14. GKE Release Channels
  15. GKE Networking: VPC-Native Clusters
  16. GKE Identity: Workload Identity
  17. Cluster Access Control: Mapping Cloud IAM to Kubernetes RBAC
  18. Observability, Compared Across Providers
  19. The Three, Side by Side
  20. The Three Serverless Options, Compared In Depth
  21. Managed Add-on Ecosystems, Compared
  22. A Real Cost Comparison
  23. Node Upgrades — A Genuinely Important Operational Reality
  24. Multi-Cluster and Multi-Cloud Kubernetes
  25. A Full Worked Example: The Same Application, Three Providers
  26. Choosing Between Them
  27. Migrating Between Providers — A Real Checklist
  28. Part 5 CLI Cheat Sheet
  29. Common Mistakes
  30. Worked Practice Problems
  31. Summary and What's Next

Why Managed Kubernetes Exists#

Parts 1-4 of this series covered Kubernetes's architecture in full — the control plane, etcd, the scheduler, networking, storage. Running all of that yourself, correctly, at high availability, is genuinely hard, ongoing operational work: keeping etcd healthy and backed up (Part 4), patching and upgrading the API server and control plane components, scaling the control plane itself under load. Managed Kubernetes services exist specifically to take that operational burden off your team — directly connecting to the toil discussion from the SRE Fundamentals series: running your own control plane is exactly the kind of repeatable, undifferentiated operational work most organizations would rather not own.

Every mechanism this Part covers — control-plane management, compute options, identity, networking, cost, upgrades, multi-cluster patterns — maps onto a real, concrete decision a platform team makes when standing up production Kubernetes, and each decision recurs, in some shape, across EKS, AKS, and GKE alike, which is exactly the organizing structure this Part follows provider by provider.

Diagram

The Shared Responsibility Model, Applied to Kubernetes#

This directly reuses the shared responsibility model from the DevSecOps series, now made concrete for Kubernetes specifically — genuinely one of the most commonly tested "do you actually understand managed Kubernetes" interview questions.

The exact same shared-responsibility SHAPE — provider owns infrastructure-level concerns, you own configuration and workload-level concerns — appears throughout this entire course (the AWS Cloud Architecture series applies it to EC2/RDS, the DevSecOps series applies it to container security); this Part is simply the Kubernetes-specific instance of a pattern worth recognizing everywhere it recurs.

Diagram

The single most important, frequently-tested nuance worth stating explicitly: "managed" almost always means the CONTROL PLANE, not the WORKER NODES, and definitely not your workloads. Depending on the specific compute option chosen (covered per-provider below), you may still be fully responsible for patching, securing, and scaling the actual machines your pods run on — a genuinely common point of confusion for candidates who assume "managed Kubernetes" means "nothing to operate."

A quick, worth-memorizing framing for interviews: "control plane" almost always means fully managed across all three providers; "worker nodes" range from fully-yours (standard node groups/pools) to fully-managed (Fargate, Virtual Nodes, Autopilot) depending on the specific compute option chosen — the compute option, not the provider itself, is what actually determines how much operational burden remains on your team.


What "Managed" Actually Covers, and What It Doesn't#

Diagram

Amazon EKS — Architecture#

EKS (Elastic Kubernetes Service) runs the control plane across multiple AWS Availability Zones automatically, with AWS managing etcd and the API server's high availability entirely behind the scenes.

Diagram
# Create an EKS cluster (using eksctl, the most common CLI tool)
eksctl create cluster --name my-cluster --region us-east-1 --nodes 3

# Point kubectl at it
aws eks update-kubeconfig --name my-cluster --region us-east-1

# Now standard kubectl works exactly as covered in Parts 1-4
kubectl get nodes

Why eksctl is worth knowing by name specifically: it's the de facto standard CLI for creating and managing EKS clusters (originally a community tool, now closely associated with AWS itself), abstracting away a genuinely large amount of underlying CloudFormation/VPC/IAM setup that would otherwise need to be configured by hand.

A real, common alternative worth naming: Terraform (via the official AWS/terraform-aws-eks provider modules) is at least as common as eksctl for production cluster provisioning, specifically when the cluster needs to be managed as part of a broader Infrastructure-as-Code codebase alongside the rest of the AWS account's resources — eksctl remains the faster, more approachable choice for standalone clusters or quick iteration.

Worth stating precisely what "AWS managing etcd" actually means, extending Part 1's etcd depth to the managed context: you never see, back up, or directly interact with EKS's etcd at all — no etcdctl access, no visibility into the underlying Raft cluster topology. AWS's own internal backup and recovery procedures cover it entirely, which is exactly the "control plane fully managed" claim from earlier in this Part made completely concrete for the specific component Part 1 spent the most depth on.


EKS Compute Options: EC2 Node Groups vs Fargate#

A genuinely important, concrete architectural decision specific to EKS.

Diagram

Why this choice matters, worth stating explicitly, and directly connecting back to the resource-management discussion in Part 2: Fargate genuinely eliminates the "worker node OS patching" responsibility entirely — but it comes with real architectural constraints (DaemonSets, for instance, from Part 2, fundamentally don't fit Fargate's per-pod-isolated model) and a real cost premium. A strong interview answer weighs this explicitly, not just picks one as "obviously better."

Worth flagging explicitly: this compute decision gets much deeper, EKS-specific treatment in Part 7 — Karpenter as the modern alternative to fixed node groups, EKS Auto Mode as a newer, even-more-managed option, and the exact Fargate limits (vCPU/memory ceilings, no hostNetwork) worth knowing precisely before committing to it.


EKS Identity: IRSA#

Already referenced in the DevSecOps series (Part 4) as the standard AWS workload-identity pattern — here's its full context specifically within EKS.

Diagram

IRSA (IAM Roles for Service Accounts) is exactly the workload-identity mechanism referenced in the DevSecOps series' secrets management tutorial — worth restating here as the concrete EKS-specific implementation: it lets a pod assume a real, scoped AWS IAM role, automatically, with zero long-lived AWS credentials ever stored in the cluster — directly eliminating the exact class of leak risk covered in that earlier tutorial.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: checkout-service
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/checkout-s3-access

EKS Networking: The VPC CNI#

A genuinely distinctive, EKS-specific architectural detail worth knowing, directly extending the CNI discussion from Part 3.

Diagram

Why this is worth knowing specifically, as a genuinely important practical implication: because pods get real VPC IPs, they're directly visible to (and limited by) your VPC's own IP address capacity — a genuinely common, real operational issue at scale is running out of available VPC IP addresses for pods, something teams using an overlay-network CNI (like Calico in overlay mode) wouldn't hit in the same way. This is a real, EKS-specific capacity planning consideration (directly connecting to the Capacity Planning & Performance series) worth being aware of. Part 7 covers the two standard EKS-specific mitigations — custom networking and IP prefix delegation — in full depth.


Azure AKS — Architecture#

AKS (Azure Kubernetes Service) follows the same fundamental managed-control-plane model, with a few Azure-specific distinctions worth knowing.

# Create an AKS cluster
az aks create --resource-group my-rg --name my-cluster --node-count 3 --generate-ssh-keys

# Point kubectl at it
az aks get-credentials --resource-group my-rg --name my-cluster

kubectl get nodes

As with EKS, Terraform's official azurerm_kubernetes_cluster resource is a common production alternative to the az aks CLI for teams managing AKS as part of a broader IaC codebase.

Diagram

Why AKS's historical control-plane pricing is worth knowing as a concrete, citable fact: it's a genuinely real, practical cost differentiator often raised in "which managed Kubernetes should we use" discussions — always worth verifying current pricing directly, since cloud pricing models change, but knowing this distinction exists as a real consideration (not just "they're all basically the same") is a strong, specific interview signal.

Worth stating precisely, mirroring the same claim already made for EKS's etcd: AKS manages the entire control plane, including etcd, with zero direct access or visibility for the cluster operator — the exact same "control plane fully abstracted away" property, just under Azure's own internal operational tooling instead of AWS's.


AKS Compute Options: VM Scale Sets vs Virtual Nodes#

The AKS-specific equivalent of the EKS EC2-vs-Fargate decision, worth understanding at the same level of depth.

Diagram
# Add a VM Scale Set-based node pool
az aks nodepool add --resource-group my-rg --cluster-name my-cluster \
  --name userpool --node-count 3 --node-vm-size Standard_D4s_v5

# Enable Virtual Nodes for serverless burst capacity (requires ACI + VNet integration)
az aks enable-addons --resource-group my-rg --name my-cluster --addons virtual-node --subnet-name aci-subnet

Why Virtual Nodes are worth knowing as a distinct, real AKS capability, not just "Azure's version of Fargate": a genuinely common real use case is burst scaling — a normal VM Scale Set node pool handles steady-state load, while Virtual Nodes absorb sudden traffic spikes far faster than provisioning new VMs could (pods launch in seconds via ACI, without waiting for a new VM to boot) — directly connecting to the burst-capacity patterns in the Capacity Planning & Performance series, applied as a specific, named AKS feature rather than a generic autoscaling concept.


AKS Identity: Azure AD Workload Identity#

The Azure-specific equivalent of EKS's IRSA — the same underlying pattern (OIDC federation, no long-lived credentials), different provider-specific name and implementation.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: checkout-service
  annotations:
    azure.workload.identity/client-id: <managed-identity-client-id>

A strong, senior-level interview line, tying EKS and AKS identity together: "IRSA on EKS and Azure AD Workload Identity on AKS solve the exact same problem, the exact same way — OIDC federation between the Kubernetes ServiceAccount and the cloud provider's own IAM system, eliminating long-lived credentials entirely. The mechanism generalizes; only the provider-specific configuration differs."

A quick naming note worth remembering: Azure has, over time, referred to this same underlying feature as both "AAD Pod Identity" (an older, now-deprecated approach) and "Azure AD Workload Identity" (the current, OIDC-federation-based standard) — always confirm which generation of the feature a given piece of documentation or an existing cluster is actually using before assuming compatibility.

Worth noting Part 7's Pod Identity discussion has an AKS/GKE-relevant echo: just as EKS Pod Identity emerged as a simpler successor to IRSA's OIDC-provider-per-cluster model, watch for equivalent simplifications from Azure and Google over time — the underlying OIDC-federation mechanism is stable, but the exact provisioning ergonomics around it (how many manual setup steps a platform team needs) continues to evolve across all three providers, not just AWS.


AKS Networking: Kubenet vs Azure CNI#

AKS's networking choice deserves the same depth as EKS's VPC CNI discussion — it's a genuinely important, real architectural decision made at cluster-creation time.

Diagram

Why this is genuinely the same architectural tradeoff as EKS's VPC CNI section above, worth stating explicitly: Azure CNI gives pods real, routable VNet IPs (like EKS's default VPC CNI), which is essential when other Azure services need to reach pods directly, but consumes real VNet address space per pod — the exact same capacity-planning consideration already covered for EKS. Kubenet trades that direct reachability away in exchange for conserving VNet IP space, functioning more like an overlay-network CNI choice (Calico in overlay mode) than EKS's default model. As of current AKS guidance, Azure CNI (specifically Azure CNI Overlay, a newer hybrid mode combining overlay-style pod IP conservation with Azure CNI's networking features) is increasingly the recommended default — worth verifying current guidance rather than assuming either is universally preferred.


Google GKE — Architecture#

GKE (Google Kubernetes Engine) deserves particular attention for one specific reason worth stating explicitly: Google originally created Kubernetes itself (open-sourcing it in 2014, based on internal experience running Borg, Google's own internal cluster scheduler) — GKE is often considered the most mature, most "native" managed Kubernetes offering as a direct result.

Consistent with the pattern established for EKS and AKS: GKE also fully manages etcd and the entire control plane, with zero direct operator access — the same underlying claim, the same underlying component from Part 1, just Google's own internal tooling behind it instead of AWS's or Azure's.

# Create a GKE cluster
gcloud container clusters create my-cluster --num-nodes 3

# Point kubectl at it
gcloud container clusters get-credentials my-cluster

kubectl get nodes

Rounding out the pattern: Terraform's google_container_cluster resource is the standard GKE IaC alternative to raw gcloud commands, completing the same eksctl/az-aks/gcloud-vs-Terraform choice across all three providers.


GKE's Big Differentiator: Autopilot vs Standard#

The single most distinctive, most commonly-tested GKE-specific architectural choice — genuinely worth understanding deeply.

Diagram

Why Autopilot is such a strong, specific thing to know about, worth stating explicitly: it directly extends the "managed vs. self-managed" spectrum from the very start of this Part one step further than EKS/AKS's standard node-group model — Autopilot removes node-level operational responsibility (patching, right-sizing, capacity planning at the node level) almost entirely, billing you for actual pod resource consumption instead of provisioned node capacity. The real tradeoff, worth naming: Autopilot enforces certain security/configuration best practices automatically (which can be a genuine feature) but also removes some low-level customization flexibility that Standard mode (or EKS/AKS) still allows.

A concrete, worth-knowing enforced-baseline example: Autopilot automatically applies stricter default Pod Security Standards and resource-limit requirements than a Standard-mode cluster would by default — a genuinely real instance of the "removes some flexibility, but the removed flexibility is specifically the kind that's easy to misconfigure insecurely" tradeoff, directly connecting to the Pod Security Admission material from Part 1/DevSecOps series.


GKE Release Channels#

A genuinely practical, GKE-specific feature worth knowing — directly connects to the version-upgrade discussion later in this Part.

Diagram

Why this is worth knowing as a concrete, GKE-specific concept: it directly formalizes the "how aggressively should we adopt new Kubernetes versions" decision as an explicit, named choice — rather than every team having to independently decide and manage their own upgrade cadence, GKE offers pre-defined, curated tracks matching different real risk tolerances.

Worth stating the honest comparison explicitly: EKS and AKS don't offer a directly named equivalent to Release Channels — version-adoption cadence on those two is a manual planning decision the platform team owns entirely, whereas GKE bakes a curated, three-tier risk-tolerance framework directly into the product. This is a genuine, citable GKE differentiator, not a minor cosmetic difference.


GKE Networking: VPC-Native Clusters#

Closing out the third and final networking model, completing the same depth given to EKS's VPC CNI and AKS's Kubenet/Azure CNI choice.

Diagram

Why GKE's specific implementation (secondary IP ranges, called "alias IP ranges") is worth knowing as a distinct, real mechanic: rather than pods sharing the same primary subnet range as nodes (as with EKS's default VPC CNI), a VPC-Native GKE cluster provisions dedicated secondary ranges specifically for pod IPs and another for Service ClusterIPs — a deliberate design that makes capacity planning for pod IP exhaustion (the same real concern raised for EKS above) an explicit, visible sizing decision made at cluster creation, rather than something that silently shares capacity with the node subnet.

Across all three providers, worth stating as the closing, unifying observation for this section: EKS's VPC CNI, Azure CNI, and GKE VPC-Native clusters all converge on the same fundamental design goal — giving pods real, VPC-routable IPs for direct integration with other cloud-native services — while each implements the actual IP address bookkeeping differently (shared subnet capacity for EKS, a dedicated overlay/CNI choice for AKS, dedicated secondary ranges for GKE). The underlying capacity-planning concern (pods consume real, finite address space) is identical across all three, even though the specific mechanics and terminology differ by provider.


GKE Identity: Workload Identity#

Rounding out the identity comparison with the same depth already given to IRSA and Azure AD Workload Identity — GKE's own OIDC-federation implementation.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: checkout-service
  annotations:
    iam.gke.io/gcp-service-account: checkout-sa@my-project.iam.gserviceaccount.com
# Bind the Kubernetes ServiceAccount to the Google Cloud service account
gcloud iam service-accounts add-iam-policy-binding checkout-sa@my-project.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:my-project.svc.id.goog[default/checkout-service]"

Worth stating the same unifying line one final time, now with all three concretely shown: GKE Workload Identity, exactly like IRSA and Azure AD Workload Identity, is OIDC federation between the Kubernetes ServiceAccount and the cloud provider's own IAM — a pod annotated this way gets short-lived, automatically-rotating Google Cloud credentials, with zero long-lived service account keys ever stored in the cluster. The only genuinely provider-specific piece across all three is the exact annotation key and the underlying IAM binding syntax — the security model and the risk it eliminates (the DevSecOps series' secrets management tutorial) are identical.

Three providers, three annotation keys, one underlying mechanism — the single cleanest illustration this entire Part offers of "consistent core, different edges."

Keep this exact example in mind for the Choosing Between Them section further down — it's the concrete evidence behind that section's closing interview line.

Cost estimation, worth a quick pointer: all three providers offer official pricing calculators (AWS Pricing Calculator, Azure Pricing Calculator, Google Cloud Pricing Calculator) — always model a real workload's expected node count and instance mix through the actual current calculator before quoting a cost comparison number, since published list prices change over time.

That closes out the identity comparison — the next section turns to access control at the human/CI layer, a genuinely separate concern from the workload identity just covered.


Observability, Compared Across Providers#

Each provider offers a native, first-party observability integration — worth a direct comparison, since "just use the built-in monitoring" means something genuinely different depending on which cloud a cluster runs on.

Diagram

Why the naming collision between AWS's "Container Insights" and Azure's own "Container Insights" (a specific feature within Azure Monitor) is genuinely worth flagging explicitly: they are completely separate, unrelated products from different vendors that happen to share an identical marketing name — a real, concrete source of confusion when reading documentation or discussing observability strategy across a multi-cloud team, worth calling out explicitly rather than assuming shared terminology implies a shared underlying technology.

A consolidated, practical recommendation: for a single-cloud deployment, the native integration is almost always the fastest path to a working observability baseline (directly connecting to the Observability series' own build-vs-buy framing) — the vendor-neutral OpenTelemetry path (ADOT on EKS, or the OpenTelemetry Operator generically on any of the three) becomes the right choice specifically when multi-cloud portability or avoiding vendor lock-in for observability tooling is a genuine, stated requirement, not a default best practice to reach for regardless of actual need.


Cluster Access Control: Mapping Cloud IAM to Kubernetes RBAC#

A genuinely distinct concern from workload identity (IRSA/Azure AD Workload Identity/GKE Workload Identity, covered above) — this is about how a human (or a CI/CD pipeline) authenticates kubectl itself against the cluster, worth its own precise treatment.

Diagram
# EKS — grant a human IAM role kubectl access, mapped to a Kubernetes RBAC group
aws eks create-access-entry --cluster-name my-cluster \
  --principal-arn arn:aws:iam::123456789012:role/platform-team \
  --kubernetes-groups platform-admins

# AKS — Azure AD-integrated RBAC role assignment
az role assignment create --role "Azure Kubernetes Service RBAC Admin" \
  --assignee <azure-ad-group-id> --scope <cluster-resource-id>

# GKE — Google Cloud IAM role binding for cluster access
gcloud projects add-iam-policy-binding my-project \
  --member="group:platform-team@example.com" \
  --role="roles/container.admin"

A genuinely important, real security principle worth stating explicitly across all three: cloud IAM authentication and Kubernetes RBAC authorization are two SEPARATE layers, and a misconfiguration in either can grant unintended access. A human with broad cloud-account-level IAM permissions but no explicit Kubernetes RBAC binding still can't act inside the cluster on EKS/AKS's more Kubernetes-RBAC-centric models — but GKE's roles/container.admin is a real, worth-knowing exception: it's broad enough to grant significant in-cluster access directly through Google Cloud IAM, without a separate Kubernetes RBAC binding being strictly required, making careful IAM role scoping (least privilege, from the DevSecOps series) just as load-bearing as Kubernetes RBAC itself on GKE specifically.

The EKS Access Entries migration, worth knowing as a real, relatively recent (2023) change: EKS originally required editing a raw aws-auth ConfigMap in kube-system (a genuinely error-prone, unversioned, single-point-of-failure mechanism — a malformed edit could lock out cluster access entirely) to map IAM principals to Kubernetes RBAC. Access Entries replace this with a proper, versioned EKS API, closing a real, historically common operational footgun — worth knowing which mechanism a given EKS cluster still uses, since older clusters may not have migrated yet.


The Three, Side by Side#

Diagram
EKSAKSGKE
Created byAWSMicrosoftGoogle (original K8s creator)
Fully serverless optionFargate— (more limited)Autopilot
Workload identity mechanismIRSAAzure AD Workload IdentityWorkload Identity
Standard CLI/toolingeksctl, aws eksaz aksgcloud container
Real IPs from cloud VPC by default?Yes (VPC CNI)Varies by network plugin choiceVaries by mode
Named version-adoption tracksNo (manual upgrade planning)No (manual upgrade planning)Yes (Release Channels)
Fully-serverless optionFargateVirtual Nodes (ACI-based)Autopilot
Default networking modelVPC CNI (real VPC IPs)Kubenet or Azure CNI (configurable)VPC-Native (real VPC IPs, dedicated ranges)
Managed add-on ecosystemEKS-managed add-ons (Part 7)AKS add-ons/extensionsGKE add-ons
Multi-cluster fleet management toolingAmazon EKS Connector, third-party (ArgoCD, Rancher)Azure Arc-enabled KubernetesAnthos / Google Kubernetes Engine Fleet
Cluster access control mechanismEKS Access Entries (modern) or aws-auth ConfigMap (legacy)Native Azure AD integration + Azure RBAC or Kubernetes RBACGoogle Cloud IAM (can grant access directly) + Kubernetes RBAC
Native observability productContainer Insights (CloudWatch) or ADOTContainer Insights (Azure Monitor — same name, different product)Cloud Operations Suite (formerly Stackdriver), on by default
Node upgrade capacity controlBlue/Green node groups (Part 7)Node image upgrade channels--max-surge-upgrade/--max-unavailable-upgrade
Modern node autoscalerKarpenter (Part 7) or Cluster AutoscalerCluster Autoscaler (AKS-managed)Cluster Autoscaler (GKE-managed)
Newest fully-managed compute optionEKS Auto Mode (Part 7)Virtual NodesAutopilot

The Three Serverless Options, Compared In Depth#

Fargate, Virtual Nodes, and Autopilot were each introduced individually — worth a direct, side-by-side comparison now that all three have been covered, since "fully serverless Kubernetes" means genuinely different things across providers.

EKS FargateAKS Virtual NodesGKE Autopilot
ScopePer-pod (opt-in via Fargate profiles)Per-pod burst capacity (opt-in via node selector/taint)Whole-cluster mode (chosen at cluster creation)
DaemonSet supportNoNoNo (Autopilot manages system DaemonSets itself)
Billing modelPer-pod vCPU/memoryPer-pod, ACI-based pricingPer-pod resource usage
Node-level customizationNoneNoneNone — enforces GKE's own security/config baseline
Best-fit use caseMixed cluster: stateless workloads on Fargate, DaemonSet-needing infra on EC2 node groupsBurst capacity ABOVE a normal VM Scale Set node poolEntire cluster, when zero node-layer operations is the explicit goal

The single most important distinction worth stating precisely: Fargate and Virtual Nodes are opt-in, PER-WORKLOAD choices within an otherwise normal cluster, while Autopilot is a WHOLE-CLUSTER mode chosen at creation time. This is a genuinely real architectural difference, not just a terminology quirk — an EKS or AKS cluster can freely mix serverless and node-group-based compute for different workloads simultaneously (Part 7 covers this mixed pattern for EKS specifically), while GKE Autopilot commits an entire cluster to Google's fully-managed node model, with GKE Standard as the separate, opt-in-to-node-group-control alternative instead.

A worth-naming, subtle consequence of this difference: a team wanting BOTH "mostly serverless" AND "a few node-level-customized workloads" on GKE genuinely cannot mix Autopilot and Standard within one cluster the way EKS/AKS mix Fargate/Virtual-Nodes with regular node groups — they'd need two separate GKE clusters (one Autopilot, one Standard) to achieve the same mixed-compute pattern EKS/AKS support natively within a single cluster, a real, concrete architectural tradeoff worth surfacing when GKE is being compared against the other two specifically for this kind of mixed-workload requirement.


Managed Add-on Ecosystems, Compared#

Each provider offers its own curated set of managed cluster add-ons — worth a direct comparison, extending Part 7's EKS-specific add-on treatment to all three.

Diagram
# EKS
aws eks list-addons --cluster-name my-cluster

# AKS
az aks addon list-available --output table

# GKE
gcloud container clusters describe my-cluster --format="value(addonsConfig)"

Why this matters practically, worth stating as a genuine, provider-specific due-diligence step: each provider's add-on catalog reflects that provider's own broader ecosystem — AKS's Key Vault Secrets Provider integrates with Azure Key Vault, GKE's Config Connector lets Kubernetes manage actual Google Cloud resources declaratively, EKS's Pod Identity Agent (Part 7) is AWS-IAM-specific. None of these are portable across providers — a genuinely important, concrete example of where the "consistent core API, different edges" thesis of this entire Part becomes real: migrating a workload between providers means re-evaluating every add-on-dependent integration point, not just re-pointing kubectl at a new cluster.


A Real Cost Comparison#

Beyond the historical control-plane pricing difference already noted for AKS, a fuller, worked cost comparison is worth walking through explicitly — always verify exact current pricing, since it changes, but the shape of this comparison is durable.

Diagram

A strong, senior-level answer worth internalizing precisely: "The control-plane pricing difference between EKS/AKS/GKE is a real, easily-citable fact, but it's rarely the dominant cost driver at any meaningful scale — worker node compute (instance selection, Spot/Reserved usage, right-sizing, and for EKS specifically, Karpenter-style consolidation from Part 7) overwhelmingly dominates real Kubernetes spend. I'd evaluate control-plane pricing as a genuine but secondary factor, and focus cost-optimization effort on the compute layer first, regardless of which provider is chosen." This mirrors the exact same "control plane fee is real but not usually the main line item" framing worth applying to any of the three providers, not just AKS's historically-free tier.

Fully-serverless options (Fargate, Autopilot, and AKS's Virtual Nodes) deserve their own specific cost callout, directly extending Part 7's EKS-specific cost-optimization section: they charge a real, per-workload premium over equivalent right-sized EC2/VM capacity, in exchange for eliminating node-level operational burden entirely — the same "spiky/bursty workloads benefit, steady-state high-density workloads pay a real premium" shape covered for Fargate in Part 7 applies essentially identically to GKE Autopilot and AKS Virtual Nodes.


Node Upgrades — A Genuinely Important Operational Reality#

Regardless of provider, worker node upgrades remain a real, non-trivial operational concern — directly connecting to the rolling-update and PodDisruptionBudget concepts from Part 2.

Diagram

Why this matters, worth stating explicitly, and directly tying back to Part 2's Deployment discussion: this "cordon and drain" node upgrade pattern relies on the exact same graceful-shutdown and readiness-probe mechanics from Parts 1-2 — a pod that doesn't handle SIGTERM properly (Linux & Networking Fundamentals series, Part 1) can be abruptly disrupted during what's supposed to be a graceful node upgrade, regardless of how automated the managed provider's upgrade tooling is. A managed control plane doesn't remove the need for your workloads to be genuinely resilient to disruption — it just automates the mechanical process around it.

Per-provider node upgrade mechanics, worth knowing the actual named commands, extending Part 7's EKS-specific upgrade-strategy depth to all three:

# EKS — trigger a managed node group upgrade (Part 7 covers this in full depth,
# including the in-place vs Blue/Green distinction)
aws eks update-nodegroup-version --cluster-name my-cluster --nodegroup-name workers

# AKS — node image upgrade, with a configurable upgrade channel
az aks nodepool update --resource-group my-rg --cluster-name my-cluster \
  --name userpool --node-image-only

# GKE — surge upgrade, controlling how many EXTRA nodes are provisioned
# during the upgrade to maintain capacity (directly analogous to EKS's
# Blue/Green pattern, but built into the standard upgrade command itself)
gcloud container clusters upgrade my-cluster --node-pool default-pool \
  --max-surge-upgrade 1 --max-unavailable-upgrade 0

GKE's --max-surge-upgrade/--max-unavailable-upgrade, worth knowing as directly analogous to the Deployment maxSurge/maxUnavailable fields from Part 2, applied at the NODE level instead of the pod level: --max-surge-upgrade 1 --max-unavailable-upgrade 0 means GKE provisions one extra node before removing any old one, guaranteeing capacity never drops during the upgrade — the exact same "extra capacity vs. accepted temporary reduction" tradeoff already covered for rolling pod updates, now applied to the underlying node fleet itself.


Multi-Cluster and Multi-Cloud Kubernetes#

A genuinely real, common enterprise pattern worth understanding: many organizations run more than one cluster, sometimes across more than one provider, deliberately — worth distinguishing the real reasons from the real costs.

Diagram

Why "just use namespaces instead of multiple clusters" is NOT always the right answer, worth stating precisely as a real tradeoff: namespace-based multi-tenancy (Part 7's EKS-specific treatment covers this in depth) shares a single control plane and node capacity across every tenant — genuinely appropriate for trusted internal teams, but insufficient when the isolation requirement is a genuine regulatory boundary, a genuinely untrusted tenant, or blast-radius protection against a control-plane-level incident itself (which namespace isolation can't help with at all, since every namespace shares the same control plane).

Multi-cloud specifically, worth a balanced, honest answer rather than an unconditionally positive one: running the same workload across EKS, AKS, and GKE simultaneously for redundancy is real, and reasonably practical precisely because the core Kubernetes API is consistent across all three (this Part's central thesis) — but it multiplies real operational cost: separate identity mechanisms to manage (IRSA vs Azure AD Workload Identity vs GKE Workload Identity), separate networking models to reason about, separate CLI tooling, and genuinely harder cross-cluster observability and deployment tooling. A strong, honest interview answer: "Multi-cloud Kubernetes redundancy is achievable specifically because the workload API is portable, but it's a real, ongoing operational tax — worth paying only when the actual business requirement (regulatory, genuine catastrophic-outage tolerance) justifies it, not adopted by default 'to avoid lock-in' without a concrete driving need."

This exact "portability is achievable but never free" framing recurs throughout this Part — the same tension underlies the migration checklist below and the observability-standardization recommendation earlier in this Part.

Cluster API (full depth in Part 6) and GitOps tooling (ArgoCD ApplicationSets, Flux) are the standard, practical tools for actually operating multiple clusters consistently — declaratively managing cluster lifecycle and keeping application deployments in sync across a fleet, rather than manually replicating changes cluster by cluster.

Cross-referencing forward, worth naming explicitly: the service-mesh-based version of this exact pattern — extending identity and traffic management, not just cluster provisioning, across cluster boundaries — gets its own full treatment in Part 4's Multi-Cluster Mesh Federation section. This Part's coverage is the cluster-provisioning and workload-portability layer; Part 4 covers the traffic/identity layer built on top of it.


A Full Worked Example: The Same Application, Three Providers#

Tying the entire Part together — a concrete walkthrough of deploying the identical stateless application (an image with a Deployment, Service, and workload-identity-backed S3/Blob/GCS access) across all three providers, making the "consistent core, different edges" thesis fully concrete.

# The Deployment and Service manifests are IDENTICAL across all three
# providers — this is the "consistent core Kubernetes API" claim,
# made completely literal
apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
spec:
  replicas: 3
  selector:
    matchLabels: {app: checkout}
  template:
    metadata:
      labels: {app: checkout}
    spec:
      serviceAccountName: checkout-service
      containers:
        - name: app
          image: checkout:1.2.3
          ports: [{containerPort: 8080}]
---
apiVersion: v1
kind: Service
metadata:
  name: checkout-svc
spec:
  selector: {app: checkout}
  ports: [{port: 80, targetPort: 8080}]

What genuinely differs, provider by provider — exactly the three areas this entire Part has walked through in depth:

StepEKSAKSGKE
Cluster creationeksctl create clusteraz aks creategcloud container clusters create
ServiceAccount annotationeks.amazonaws.com/role-arnazure.workload.identity/client-idiam.gke.io/gcp-service-account
Storage access targetAn S3 bucket, via an IAM roleA Blob container, via a Managed IdentityA GCS bucket, via a Google Cloud service account
CLI to check rolloutkubectl (identical)kubectl (identical)kubectl (identical)

The single strongest, most concrete closing observation this worked example makes real, worth stating explicitly in an interview: the Deployment and Service manifests above — the actual application-facing Kubernetes objects — are byte-for-byte identical across all three clouds. Everything that differs is confined to exactly two places: cluster/node provisioning (the eksctl/az aks/gcloud container commands, run once, by platform engineers) and workload identity annotation (one line in the ServiceAccount, mapping to each cloud's own IAM system). This is precisely why "the core Kubernetes API and workload behavior is consistent across all three providers" isn't a vague claim — it's demonstrable, directly, in the exact manifests a development team actually writes and deploys day to day.


Choosing Between Them#

Diagram

A strong, senior-level closing interview line: "In practice, the core Kubernetes API and workload behavior — everything from Parts 1-4 of this series — is genuinely consistent across all three providers, since they all run real, upstream Kubernetes. The meaningful differences are at the edges: identity/IAM integration, compute options, networking model, and pricing — I'd choose based on existing cloud investment and the SPECIFIC operational tradeoffs (like Autopilot's node-abstraction) that matter most for the workload in question, not because one is broadly 'better' at running Kubernetes itself."

This decision framework is deliberately the SAME shape used throughout this entire course whenever comparing competing managed services with a shared underlying open standard — weigh the provider-specific edges against existing investment and concrete operational needs, not abstract popularity.


Migrating Between Providers — A Real Checklist#

Given how often the "how portable is this really" question comes up, a concrete, actionable checklist is worth having — directly assembled from every provider-specific edge identified throughout this Part.

Diagram

A genuinely important, non-obvious sequencing recommendation worth stating explicitly: if a multi-cloud or provider-migration future is even plausible, adopting OpenTelemetry-based observability (ADOT or the generic OpenTelemetry Operator) BEFORE the first migration is meaningfully cheaper than migrating observability tooling twice — once to the vendor-neutral standard, and again during the actual provider switch. This is a real, concrete example of the "design for the portability you'll actually need" principle: not every workload needs multi-cloud portability, but for the ones that plausibly will, front-loading the vendor-neutral choices (OpenTelemetry over native, standard Kubernetes resources over provider-specific CRDs where a reasonable equivalent exists) pays off specifically at migration time, not before.


Part 5 CLI Cheat Sheet#

# EKS
eksctl create cluster --name my-cluster --region us-east-1
aws eks update-kubeconfig --name my-cluster --region us-east-1
aws eks describe-cluster --name my-cluster --query 'cluster.status'
eksctl get iamidentitymapping --cluster my-cluster

# AKS
az aks create --resource-group my-rg --name my-cluster --node-count 3
az aks get-credentials --resource-group my-rg --name my-cluster
az aks show --resource-group my-rg --name my-cluster --query 'provisioningState'
az aks nodepool list --resource-group my-rg --cluster-name my-cluster

# GKE
gcloud container clusters create my-cluster --num-nodes 3 --enable-ip-alias
gcloud container clusters get-credentials my-cluster
gcloud container clusters describe my-cluster --format='value(status)'
gcloud container node-pools list --cluster my-cluster

# Cross-provider — once kubectl is configured, everything below is IDENTICAL
kubectl get nodes -o wide
kubectl config get-contexts
kubectl config use-context <context-name>   # switching between multiple clusters/providers

# Cluster access control (Part 5)
aws eks list-access-entries --cluster-name my-cluster
az role assignment list --scope <cluster-resource-id>
gcloud projects get-iam-policy my-project --flatten="bindings[].members" --filter="bindings.role:roles/container.admin"

Common Mistakes#

MistakeWhy It's WrongFix
Assuming "managed Kubernetes" means zero operational responsibilityOnly the control plane is managed by default — worker node patching, RBAC, and workload security remain your responsibility unless using a specifically serverless option (Fargate, Autopilot)Understand exactly which layer each specific compute option actually manages
Choosing EKS Fargate or GKE Autopilot without checking workload compatibilityDaemonSets and certain node-level customizations don't fit these fully-serverless modelsVerify workload requirements against the specific constraints of a serverless compute option before committing
Storing long-lived cloud credentials in a pod instead of using IRSA/Workload IdentityReintroduces exactly the credential-leak risk the DevSecOps series' secrets management tutorial coversUse the provider's native workload identity mechanism (IRSA, Azure AD Workload Identity, GKE Workload Identity)
Assuming a managed control plane means node upgrades are risk-free for your workloadsThe cordon-and-drain process still depends on YOUR pods handling graceful shutdown properlyEnsure proper SIGTERM handling and readiness probes (Parts 1-2) regardless of how automated the provider's upgrade tooling is
Picking a managed Kubernetes provider purely on "which is most popular"Ignores real, concrete differences in identity integration, compute options, and cost model that matter for the specific workloadEvaluate based on existing cloud investment and the specific operational tradeoffs that matter most
Focusing cost-optimization effort on control-plane pricing differencesWorker node compute overwhelmingly dominates real Kubernetes spend at any meaningful scale — control-plane fees are a secondary factorPrioritize instance selection, Spot/Reserved usage, and right-sizing/consolidation as the primary cost levers
Splitting into multiple clusters "for isolation" without a concrete driving requirementMultiplies operational overhead (multiple control planes, identity mechanisms, tooling) without a corresponding real isolation need for trusted internal use casesDefault to namespace-based isolation for trusted teams; reserve multi-cluster/multi-cloud for a genuine regulatory, blast-radius, or catastrophic-outage requirement
Confusing AWS's "Container Insights" with Azure Monitor's own, unrelated "Container Insights" featureIdentical marketing names across two completely separate vendor products causes real, avoidable documentation-reading confusionAlways confirm which provider's documentation is being referenced when the term "Container Insights" comes up
Assuming an add-on-dependent integration (Config Connector, Key Vault Secrets Provider, Pod Identity Agent) migrates for free between providersEach provider's managed add-on catalog is genuinely provider-specific — none of it is portableRe-evaluate every add-on-dependent integration point explicitly as part of any cross-provider migration plan, not just re-point kubectl
Assuming GKE's node surge-upgrade settings are optional tuning rather than a real capacity decision--max-unavailable-upgrade 0 with no corresponding surge capacity can silently reduce available capacity mid-upgradeSet --max-surge-upgrade deliberately, mirroring the same maxSurge/maxUnavailable tradeoff already made for pod-level rolling updates
Granting a broad Google Cloud IAM role like roles/container.admin without realizing it can bypass Kubernetes RBAC scopingUnlike EKS/AKS's more RBAC-centric access model, this specific GKE IAM role alone can grant significant in-cluster accessScope Google Cloud IAM roles for cluster access as carefully as Kubernetes RBAC itself — least privilege applies to both layers
Editing EKS's legacy aws-auth ConfigMap directly on a cluster that has migrated to Access EntriesCan create conflicting or unexpected access mappings between the two mechanismsVerify which access-control mechanism a given EKS cluster actually uses before making changes, and prefer Access Entries for new clusters
Expecting GKE Autopilot and Standard node pools to coexist in one clusterAutopilot is a whole-cluster mode, not a per-workload compute choice like Fargate/Virtual NodesProvision two separate GKE clusters if genuinely mixed Autopilot/Standard workloads are needed

Worked Practice Problems#

Problem 1: A team chooses EKS Fargate specifically to eliminate node management, then discovers their monitoring stack requires a DaemonSet (a log-shipping agent that must run on every node). What went wrong, and what would you recommend?

Answer: Fargate's fully-serverless, per-pod-isolated model fundamentally doesn't support DaemonSets — there's no persistent, shared "node" in the traditional sense for a DaemonSet to run one copy per. This is exactly the kind of workload-compatibility check that should happen before committing to a serverless compute option, not after. Recommendation: either use EC2 managed node groups instead (accepting the node-management responsibility Fargate was meant to eliminate) for workloads that genuinely need DaemonSets, or restructure the log-shipping approach to use a sidecar container per pod instead of a cluster-wide DaemonSet (a real, common alternative pattern specifically for Fargate-based architectures), or run a hybrid cluster with both Fargate profiles for stateless app workloads and a small EC2 node group specifically for DaemonSet-requiring infrastructure.

Problem 2: An engineer stores a long-lived AWS access key as a Kubernetes Secret so their application can call S3, on an EKS cluster. What's the concrete, better alternative, and why?

Answer: Use IRSA instead — annotate the pod's ServiceAccount with the target IAM role's ARN, and EKS's built-in OIDC provider handles the credential exchange automatically, giving the pod short-lived, automatically-rotating AWS credentials with nothing long-lived ever stored in the cluster at all. This directly eliminates the exact class of risk covered in the DevSecOps series' secrets management tutorial — a leaked Kubernetes Secret containing a static AWS key remains valid indefinitely until someone notices and manually rotates it, while IRSA-issued credentials are short-lived by design and never need to be stored anywhere for an attacker to steal in the first place.

Problem 3: A company wants to minimize both cloud vendor lock-in and node-level operational burden, but is deciding between a single-cloud fully-serverless option (like GKE Autopilot) and standard node-group-based Kubernetes across multiple clouds. What tradeoff would you highlight?

Answer: Fully-serverless options like GKE Autopilot minimize node-level operational burden very effectively, but they're also the LEAST portable choice — Autopilot's specific automation and constraints are Google-specific, and moving that exact operational model to another cloud isn't a like-for-like migration. Standard node-group-based Kubernetes (EC2 node groups on EKS, VM Scale Sets on AKS, standard node pools on GKE) keeps more operational burden on the team, but since the actual workload-facing Kubernetes API surface is genuinely consistent across all three providers' standard modes, workloads themselves remain highly portable — the real tradeoff is operational-burden reduction versus multi-cloud portability, and a team can't fully maximize both simultaneously with today's provider offerings.

Problem 4: A finance team asks why the company's Kubernetes cloud bill barely changed after switching from AKS (historically free control plane) to EKS (per-cluster control-plane fee), despite EKS's fee being a real, nonzero line item. What's the most likely explanation?

Answer: At any real operational scale, worker node compute overwhelmingly dominates total Kubernetes spend — the control-plane fee difference between providers, while real and worth knowing, is typically a small fraction of total cost compared to the actual EC2/VM instances running the workloads. A cluster running dozens or hundreds of worker nodes will see its bill driven almost entirely by instance selection, utilization, and Spot/Reserved coverage — the control-plane fee, whether zero (AKS historically) or a modest per-hour charge (EKS), is essentially rounding error by comparison at that scale. This is exactly why cost-optimization effort should focus on the compute layer (Part 7's Karpenter/Spot discussion, applicable in shape to any provider) rather than the comparatively minor control-plane pricing difference.

Problem 5: A platform team wants to split their single EKS cluster into three separate clusters — one per major internal team — believing this improves security isolation. What questions would you ask before agreeing this is the right call?

Answer: I'd ask whether the teams are genuinely mutually untrusted (a real security boundary) or just organizationally separate-but-trusted internal teams — for the latter, namespace-based isolation (RBAC, NetworkPolicy, ResourceQuota, and Pod Identity per namespace, all covered in Part 7) provides strong, real isolation at a fraction of the operational cost of three separate control planes. I'd also ask whether there's a genuine blast-radius concern specifically about a control-plane-level incident (which namespace isolation genuinely cannot protect against, since all namespaces share one control plane) — if that's the real driving concern, multiple clusters is the right call regardless of trust level. Absent a concrete regulatory, trust, or blast-radius requirement, three clusters means three control planes to upgrade and monitor, tripling a genuinely real operational burden without a correspondingly large increase in actual isolation for trusted internal teams.

Problem 6: A team migrating a workload from GKE to EKS reports that everything in their Deployment and Service manifests "just worked" unchanged, but their application's cloud-storage-access code broke and required real changes. Why did one part transfer cleanly and the other didn't?

Answer: This is precisely the "consistent core, different edges" distinction this Part has built toward throughout. The Deployment and Service manifests are pure Kubernetes API objects — pods, replica counts, label selectors, port mappings — none of which reference anything cloud-specific, so they're genuinely portable byte-for-byte across any conformant Kubernetes cluster, GKE or EKS alike. The storage-access code, however, was relying on GKE Workload Identity's OIDC federation into Google Cloud IAM to reach a GCS bucket — EKS has no knowledge of Google Cloud IAM at all; the equivalent mechanism on EKS is IRSA, federating into AWS IAM to reach an S3 bucket instead. The ServiceAccount annotation, the underlying IAM role/policy setup, and quite possibly the actual storage API calls (GCS vs S3 client libraries) all needed genuine, deliberate rework — exactly the "provider-specific edge" the core API's portability doesn't extend to.

Problem 7: A platform team wants to migrate their AKS observability stack to GKE and looks for "the same Container Insights feature" in GKE's product list, without success. What's the actual explanation?

Answer: "Container Insights" is Azure Monitor's own specific feature name — it has no equivalent name on GKE because it's not a shared, cross-vendor term; it's simply what Azure branded its own container observability integration. The actual GKE equivalent is the Google Cloud Operations Suite (formerly Stackdriver), which is enabled by default on most GKE clusters and provides comparable metrics/logs/traces capability, just under an entirely different product name and with different underlying implementation details. This is exactly the kind of naming-collision confusion worth being alert to when working across providers — assuming a literal name match exists across clouds, rather than looking for the functionally-equivalent native offering, is a real, avoidable research mistake.

Problem 8: A security review on a GKE cluster finds a service account with roles/container.admin at the project level, but no corresponding Kubernetes RBAC RoleBinding anywhere in the cluster. The team assumes this means the service account has no actual in-cluster access. Are they correct?

Answer: No — this is precisely the GKE-specific exception worth knowing explicitly: roles/container.admin, granted at the Google Cloud IAM layer, is broad enough to grant substantial in-cluster access directly, without requiring a separate Kubernetes RBAC binding at all. This differs from EKS and AKS's more Kubernetes-RBAC-centric access models, where cloud IAM primarily handles authentication (proving who you are) while Kubernetes RBAC handles authorization (what you're allowed to do once authenticated) as a clearly separate step. On GKE specifically, a sufficiently broad Cloud IAM role can collapse that separation — meaning a security review of GKE cluster access must audit Google Cloud IAM role assignments with the same rigor as Kubernetes RBAC, not treat the absence of RBAC bindings as proof of no access.

Problem 9: An organization standardized on GKE Autopilot for its simplicity, then later needs a small subset of workloads requiring a custom node AMI for a compliance requirement Autopilot's managed nodes can't satisfy. What's the actual, concrete path forward?

Answer: Since GKE Autopilot is a whole-cluster mode, not a per-workload compute choice, the compliance-requiring workload cannot simply be scheduled with a special selector onto customized nodes within the existing Autopilot cluster the way an EKS or AKS team could mix in a custom-AMI node group alongside Fargate/Virtual Nodes. The concrete path: provision a second, GKE Standard cluster specifically for the compliance-requiring workload (where custom node configuration is possible), while keeping the bulk of the fleet on the existing Autopilot cluster — accepting the added operational overhead of a second cluster (Part 5's multi-cluster tradeoff) as the necessary cost of this specific, genuine requirement Autopilot's model can't accommodate within one cluster.


Summary and What's Next#

  • Managed Kubernetes exists to remove the genuine operational toil of running your own control plane (etcd, API server HA, upgrades) — directly connecting to the toil-elimination principle from the SRE Fundamentals series.
  • The shared responsibility model applies directly: the control plane is (almost) always managed, but worker nodes, RBAC, workload security, and application reliability remain your responsibility, to varying degrees depending on the specific compute option chosen.
  • EKS offers EC2 node groups (more control) or Fargate (fully serverless, but with real workload constraints like no DaemonSets) and uses IRSA for workload identity, with real VPC IPs assigned to pods by default.
  • AKS follows the same fundamental model, with Azure AD Workload Identity as its equivalent identity mechanism, and has historically differentiated on control-plane pricing.
  • GKE, built by Kubernetes's original creator, offers Autopilot — the most fully "serverless" of the three providers' default offerings, billing per-pod rather than per-node — plus named Release Channels for explicit version-adoption risk tolerance.
  • All three providers' identity mechanisms (IRSA, Azure AD Workload Identity, GKE Workload Identity) solve the exact same problem the same way: OIDC federation eliminating long-lived cloud credentials entirely, directly closing the exact risk covered in the DevSecOps series.
  • Node upgrades still depend on your workloads handling graceful disruption properly (SIGTERM, readiness probes) — a managed control plane automates the mechanical upgrade process, but doesn't remove your responsibility for genuinely resilient pods.
  • The right provider choice depends on existing cloud investment and specific operational tradeoffs (like node-abstraction level), not a universal "best" — the core Kubernetes behavior from Parts 1-4 is consistent across all three.
  • AKS's Virtual Nodes and GKE's VPC-Native clusters round out the compute and networking comparison — Virtual Nodes mirror Fargate for serverless burst capacity, and VPC-Native clusters mirror the VPC CNI/Azure CNI's real-routable-pod-IP model via dedicated secondary IP ranges.
  • Worker node compute, not control-plane pricing, dominates real Kubernetes spend at any meaningful scale — control-plane fee differences are real but secondary; cost-optimization effort belongs on instance selection, Spot/Reserved usage, and consolidation.
  • Multi-cluster and multi-cloud Kubernetes are real, achievable patterns precisely because the core workload API is portable across all three providers — but each adds genuine, ongoing operational tax (separate identity mechanisms, tooling, observability) that should be paid only for a concrete driving requirement, not adopted by default.
  • GKE Workload Identity completes the three-way identity comparison — OIDC federation into Google Cloud IAM, mechanically identical to IRSA and Azure AD Workload Identity, differing only in annotation syntax and IAM binding commands.
  • Each provider's native observability integration and managed add-on catalog are genuinely provider-specific, including confusingly-identical product names (AWS's and Azure's separate "Container Insights") across vendors — none of this transfers for free in a cross-provider migration.
  • A full worked deployment makes the portability claim concrete: Deployment/Service manifests are byte-for-byte identical across all three providers; only cluster provisioning commands and workload-identity annotations genuinely differ.
  • A real migration checklist separates what transfers freely (manifests, usually NetworkPolicy/Ingress) from what must be rebuilt (identity/IAM, cloud-storage SDK calls, add-on-dependent integrations) — and adopting vendor-neutral choices (OpenTelemetry) before a migration is plausible is meaningfully cheaper than re-platforming twice.
  • Cluster access control (human/CI kubectl access) is a distinct layer from workload identity — EKS's Access Entries replaced the error-prone aws-auth ConfigMap, AKS integrates Azure AD directly into RBAC, and GKE's roles/container.admin is a real, worth-knowing exception where cloud IAM alone can grant in-cluster access without a separate RBAC binding.
  • Fargate and Virtual Nodes are opt-in, per-workload choices mixable within one cluster; Autopilot is a whole-cluster mode chosen at creation — a real architectural difference, not just naming, with genuine consequences for mixed-compute requirements.
  • A compliance or custom-node requirement that Autopilot can't satisfy means provisioning a second, GKE Standard cluster — not a per-workload override — a concrete, worth-remembering consequence of Autopilot's whole-cluster scoping.

Continue to Part 6 (06-onprem-and-cluster-provisioning.md) to cover the other side of this spectrum — running Kubernetes yourself, on-premise or self-managed in the cloud, and the modern tools that make that genuinely practical.