Migrating production workloads from Amazon EKS to Google Kubernetes Engine (GKE) used to be a high-risk lift-and-shift project. But for teams that have already standardized on Istio as their service mesh, there is a smarter path: use Istio’s multi-cluster setup to shift live traffic gradually between clusters, eliminating downtime and giving you a real rollback plan. This article walks through a practical, zero-downtime approach to moving workloads from EKS to GKE using Istio’s traffic management capabilities — a method that treats migration as an incremental, observable operation rather than a nail-biting cutover event.
Why EKS-to-GKE Migration Needs a Different Playbook
Cloud providers have matured, and the reasons for moving between managed Kubernetes offerings are rarely about raw feature checklists. Cost models differ, AI/ML integrations tilt toward Google Cloud’s TPUs and Vertex AI, and billing commitments often drive consolidation. But the operational reality is that your EKS cluster is not just a collection of Deployments and Services. It is a living system with autoscalers, network policies, monitoring dashboards, and—most importantly—stateful dependencies.
A traditional migration might involve re-creating Kubernetes manifests, tweaking node pools, and then flipping DNS at the end. That approach works for greenfield applications, but for anything with sticky sessions, long-running connections, or background workers, the final cutover can be brutal. Istio solves this by giving you a layer of abstraction at the network level. You can keep both clusters running, join them into a single service mesh, and then decide request-by-request which cluster serves traffic.
Understanding Istio’s Multi-Cluster Architecture
Before you start moving workloads, you need to understand the two primary multi-cluster models Istio supports. For this migration pattern, the primary-remote setup is usually the better fit. One cluster (your current EKS cluster) acts as the primary control plane, while the GKE cluster connects as a remote. This lets you manage both from a single Istio configuration source, and it simplifies the traffic shifting rules you’ll apply later.
The alternative—two separate primary clusters—gives you more isolation but requires more care when you need to route traffic across cluster boundaries. If you are planning a permanent move, primary-remote is a pragmatic first step because it keeps the EKS side as the source of truth until you decide to flip the primary role.
Network Requirements and Security Considerations
Istio multi-cluster traffic shifting depends on stable, secure network connectivity between the two clusters. For EKS and GKE, that means setting up a VPN tunnel or using Google Cloud’s Interconnect and AWS’s Direct Connect to establish private routing. You will also need to configure cross-cluster Service discovery. Istio uses its own DNS proxy plus the mesh’s service registry to ensure that a Service in GKE can be resolved by workloads still running in EKS—and vice versa.
Security should not be an afterthought. Use the same root certificate authority across clusters and enable mTLS globally. This ensures that traffic between the two clusters is encrypted and authenticated, so you are not accidentally introducing a trust boundary that did not exist before.
Preparing Workloads for Multi-Cluster Traffic Shifting
Not every workload is a candidate for gradual migration right away. Istio can shift traffic at the virtual-service level, but the underlying Pods must be manageable in both clusters. Start with stateless workloads—web frontends, API gateways, and microservices that do not hold local state. These are the safest to move using a percentage-based weight split.
For stateful workloads, think carefully about persistent volumes and database connectivity. If a legacy service reads and writes to a database that is still hosted in AWS, then moving the Pod to GKE could add latency or, worse, split your write path into two regions. In that scenario, you might need to use an external database proxy or migrate the data layer first.
Creating a Migration Namespace and Labeling Strategy
To keep traffic shifting manageable, avoid moving entire namespaces at once. Instead, use fine-grained labels on your Istio VirtualServices and DestinationRules. For example, label all workload versions with migration-step: 1 on both clusters. Then you can reference those subsets in your traffic split rules without editing dozens of individual Service entries.
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: checkout-service
spec:
host: checkout.default.svc.cluster.local
subsets:
- name: eks
labels:
cluster: eks
- name: gke
labels:
cluster: gke
Corresponding VirtualService rules can then route 90% to the EKS subset and 10% to the GKE subset. That is your first step, and it is already zero-downtime because every request still succeeds—you are simply changing which backend responds.
Shifting Traffic Gradually with Istio VirtualServices
The core of a zero-downtime migration is the weighted routing rule. Once your GKE cluster is running the same workload image and configuration, you can start introducing traffic in small increments. A typical sequence looks like this:
- Start with 0% to GKE to confirm the mesh is healthy and endpoints are discovered.
- Route 5% of traffic to GKE for a few hours while monitoring latency, error rates, and Pod logs.
- Increase to 20%, then 50%, then 80%, giving each level enough time to catch issues like cold starts or missing config maps.
- Finally, set 100% to GKE, keep EKS running but idle, and observe for a full burn-in period.
This is not just a traffic-management exercise. Every shift should trigger the same observability checks: request error rate, p99 latency, CPU utilization, and any custom application metrics. Use Prometheus and Grafana (or Google Cloud Managed Service for Prometheus) to compare the two subsets in real time.
Handling Stateful Concerns During the Cutover
When you move a workload that depends on a database, do not rely on traffic shifting alone. You need a plan for states like session affinity. Istio can honor sticky sessions by using consistentHash on cookies or headers, but if the backend Pod moves to a different cluster, those sessions can break. For that reason, consider using a distributed session store (like Redis) before the migration, or accept a brief “session reset” during the final 100% shift.
For background jobs and message consumers, traffic shifting does not help. You need to drain the old cluster’s queue consumers and start new ones in GKE. That can be done by scaling down the Deployment in EKS while scaling up the one in GKE, or by using a feature flag that disables the worker from processing new tasks.
Observability: The Safety Net That Makes Gradual Shifts Possible
Zero-downtime claims are only credible when backed by real-time observability. Istio generates a rich set of telemetry—metrics, logs, and distributed traces—for every request that crosses the mesh. Before you start shifting traffic, enable Envoy access logging and configure tracing to a backend that works across both clusters, such as Jaeger or Google Cloud Trace.
Pay special attention to the error budget. If you are allowed 0.5% errors and you shift 10% of traffic, a single misconfigured VirtualService can quickly burn through that budget. Set alerts on the GKE subset specifically, not just on the overall service. It is easy to mask problems in the new cluster when the EKS side is still absorbing most of the load.
Rollback Without Panic
The greatest benefit of using Istio for migration is the ability to roll back instantly. If something goes wrong at 50% traffic, you just update the VirtualService to send 0% to GKE. Because the EKS cluster was never scaled down, your old environment is still fully capable. This safety net is what makes continuous delivery-style migration viable for production.
To make rollback even safer, avoid deleting any EKS resources during the migration window. Keep the cluster alive, keep the deployment replicas at a reasonable minimum, and only decommission after you have spent several days at 100% on GKE with no incidents.
Final Cleanup and Decommissioning EKS
Once the workload has run smoothly in GKE for the required burn-in period, you can begin decommissioning the EKS side. First, remove the EKS subset from your Istio DestinationRules. Then scale down the old Deployment to zero replicas. Keep the Service and the EKS cluster itself running for another week to allow logs and metrics to expire naturally.
When you are confident no hidden batch job or cron process still points to EKS, you can tear down the cluster and remove the VPN/interconnect links. Do not forget to update your DNS records and any external service accounts that referenced the old cluster’s Service IPs.
Common Pitfalls When Migrating Between Clusters
Even with Istio, there are traps that can cause subtle outages in the middle of your migration. Watch out for these:
- Mismatched Service names: The GKE and EKS Services must resolve to the same namespace and name in the mesh, otherwise traffic shifting rules will not apply.
- Missing Istio gateway annotations: If you use ingress gateways, update the VirtualService attached to the gateway, not just the internal Service’s VirtualService.
- Kubernetes version drift: If GKE runs a newer API version or changes behavior for certain Ingress resources, your application might still serve traffic but with unexpected side effects.
- Pod readiness delays: Set
initialDelaySecondson the GKE Deployments to account for slower image pulls or different storage classes.
Test the entire sequence in a sandbox environment before touching production. The goal is not just to avoid downtime, but to make the migration repeatable—so if you need to move another workload next quarter, you already have a proven playbook.
Conclusion
Migrating workloads from EKS to GKE does not have to be an all-or-nothing gamble. By using Istio’s multi-cluster setup to shift traffic gradually, you gain fine-grained control over how and when requests move between clusters, an instant rollback mechanism, and the confidence that comes from validating new infrastructure under live traffic. That combination turns a scary infrastructure migration into a routine, reversible release—exactly how modern cloud-native operations should handle change.
