, ,

Production Cutover to VKS, Traffic Shift, Validation and Rollback (TKGI to VKS Series, Part 23)

The production move from TKGI to VKS in one runbook: lower the DNS TTL ahead of time, shift traffic at a load balancer you control, validate the real user path, and keep a rollback that takes seconds because the old platform stays warm.

TKGI to VKS Series · Part 23 of 26

A production cutover I ran last year came down to one number I nearly left alone, a DNS record with a time to live of 3600 seconds. That single hour of cached lookups turned a clean traffic shift into two hours of split traffic, writes landing on both platforms, and a reconciliation nobody enjoyed. Cutover is the moment a migration stops being a rehearsal, so the plan has to treat traffic, validation and rollback as one connected move rather than three hopeful steps.

Key takeaways: Cut over at a layer you control, a GSLB or reverse proxy, not a bare DNS record, so a shift reverses in seconds.
Lower the DNS TTL to 60 seconds at least a week before the window, because resolvers cache the old value for its full length.
Keep TKGI warm through the whole rollback window and never scale it down at cutover, so failback is a traffic move and not a restore.
Validate the real user path and a real write, not a health endpoint, before you call the cutover done.
Headline command: velero backup create prod-final –include-namespaces webtier,payments –snapshot-volumes –wait
Who this is for: Platform engineers, SREs and release managers running the production move after a pilot has proven the runbook. Starting point assumed: a TKGI 1.18 source on NSX-T with dev, staging and prod clusters, and a VKS target on VCF 9 with the prod workloads already restored and validated in a pilot. VKS is the vSphere Kubernetes Service, TKGI is Tanzu Kubernetes Grid Integrated the outgoing platform, GSLB is a global server load balancer that steers traffic across sites or platforms, TTL is the time to live that tells resolvers how long to cache a DNS answer, and Velero is the backup and restore tool that carries workloads between clusters.

Cutover approach and current state

Last part ran the full playbook against the dev cluster as a pilot, timed every phase, and scored the exit criteria to a go. Prod workloads already sit on VKS from the staging rehearsal, restored and validated, waiting behind their own address. This part flips production traffic from the TKGI cluster to the VKS cluster, proves the new platform is serving real users, and keeps a clean path back if it is not.

Here is where the usual advice falls down. Most cutover guides say change the DNS A record and you are done, as if one edit moves every client at once. Resolvers and clients cache the old answer for the length of the TTL, and a few ignore low TTLs entirely, so a bare DNS change gives you a slow, uneven shift you cannot reverse quickly. A controlled cutover happens at a layer you own, a GSLB or a reverse proxy sitting in front of both platforms, with DNS as the coarse outer layer whose TTL you lowered days ago.

Three cutover shapes are worth weighing, and the right one depends on how much reversibility you need. Pick the shape before the window opens, because deciding under a rising error rate is how a shift becomes an outage.

Cutover shapeHow it worksReversibilityUse when
Big bang DNS flipChange the A record once, all traffic moves as caches expireSlow, bounded by the TTLSmall stateless app, low stakes
Weighted GSLB shiftMove traffic in steps, 10 then 50 then 100 percent, at a balancer you controlSeconds, shift the weight backProduction with a real error budget
Blue green reverse proxyBoth platforms live behind one proxy, flip the upstreamInstant, flip the upstream backStateful apps needing a fast failback

For a production estate with users and data, the weighted GSLB shift is the pick, because it turns a binary flip into a dial you can turn back. Avoid the bare big bang DNS flip for anything with an error budget, since it commits every client on a timescale you do not control and offers no fast reverse. A blue green reverse proxy is even cleaner where you can put one proxy in front of both platform load balancers, though two separate estates, TKGI on NSX-T and VKS on Avi, often make a GSLB the practical steering layer between them.

Preflight and change-freeze checks

Prove five things before the window opens: the DNS TTL was lowered a week ago and has propagated, the target VKS cluster is healthy and serving on its own address, a fresh final backup of the production namespaces exists, writes on the source can be frozen at the top of the window, and a named owner holds the rollback decision. A preflight that skips the TTL check is exactly how my worst cutover started, so it goes first.

Here is the artifact worth keeping, a T minus timeline that turns cutover from a scramble into a schedule. Each row names when, what, and the proof it actually happened, so nobody flips a record on trust. Copy this into your change ticket and check the proof column live.

WhenActionProof it is done
T minus 7 daysLower the DNS TTL to 60 secondsdig shows ttl 60 from two resolvers
T minus 2 daysFinal staging pilot, exit criteria greenscorecard signed off
T minus 1 dayFreeze deploys to source, announce windowchange ticket approved
T minus 1 hourFresh Velero backup of prod namespacesbackup Completed, item count matches
T zeroQuiesce writes, restore delta, shift 10 percenterror rate under budget
T plus 15 minShift to 100 percent, source kept warmtarget serving, source idle
T plus 24 hoursDecommission window opensno rollback triggered
# Tested against: VCF 9.0, VKS on Supervisor, Velero 1.17.2, VCF CLI 9.0, TKGI 1.18 source, Avi GSLB # confirm the DNS TTL was actually lowered a week ago dig +noall +answer app.corp.local # app.corp.local. 60 IN A 10.20.30.10 # ttl is 60, not 3600, the pre-work landed # target VKS cluster healthy and serving on its own address kubectl config use-context prod-vks kubectl get pods -n payments -o wide | head # pg-0 1/1 Running 0 2d curl -s -o /dev/null -w ‘%{http_code}’ https://app-vks.corp.local/healthz # 200 # fresh final backup on the source before the freeze kubectl config use-context tkgi-prod velero backup create prod-final –include-namespaces webtier,payments –snapshot-volumes –wait # Backup completed with status: Completed. 71 items backed up.
Gotcha: Lowering the TTL on the day of the cutover does nothing, because resolvers already hold the old high value for its full length. A TTL of 3600 set the morning of the window keeps caches serving the old address for up to an hour after you flip. Lower it a week out, then verify with dig from more than one resolver, so the low value is already cached everywhere when you shift.

Traffic shift, DNS and load balancer cutover

With preflight green, the shift runs in four steps. First, freeze writes on the source and take a final delta restore so the target holds the newest data. Second, confirm the VKS pool is healthy at the GSLB so traffic has somewhere safe to land. Third, move 10 percent of traffic and watch the error rate for a few minutes. Fourth, move to 100 percent while leaving the source pool defined at ratio zero, warm and ready for a failback.

Steps three and four both live at the GSLB, where a member ratio decides how much traffic each platform receives. Moving in a 10 percent step means a bad shift touches a tenth of users for a few minutes, not everyone at once, and the reverse is one API call away. Read the controller token from an environment variable, never a hardcoded string, so the credential never lands in a shell history or a runbook.

# shift 10 percent of traffic to the VKS pool at the GSLB (member ratio 9 to 1) curl -s -H "Authorization: Bearer ${AVI_TOKEN}" -X PATCH https://avi-ctl.corp.local/api/gslbservice/gs-app -d ‘{"groups":[{"members":[{"fqdn":"app-tkgi.corp.local","ratio":9},{"fqdn":"app-vks.corp.local","ratio":1}]}]}’ # {"uuid":"gslbservice-2f1a"} # verify the resolver now hands out both, weighted 9 to 1 for i in $(seq 1 20); do dig +short app.corp.local; done | sort | uniq -c # 18 10.20.30.10 (tkgi vip) # 2 10.20.40.10 (vks vip) # once validation holds, move fully to VKS, keep tkgi at ratio 0 and warm curl -s -H "Authorization: Bearer ${AVI_TOKEN}" -X PATCH https://avi-ctl.corp.local/api/gslbservice/gs-app -d ‘{"groups":[{"members":[{"fqdn":"app-tkgi.corp.local","ratio":0},{"fqdn":"app-vks.corp.local","ratio":1}]}]}’ # {"uuid":"gslbservice-2f1a"}

First attempt at the 10 percent step sent zero traffic to VKS, and the GSLB showed the new pool member DOWN. A health monitor was probing the site root, which answers a 302 redirect to the login page, and the monitor only counted a 200 as healthy. So the balancer decided the target was broken and refused to send it anything.

# the site root redirects, so the default health probe fails curl -s -o /dev/null -w ‘%{http_code}’ https://app-vks.corp.local/ # 302 # fix: point the GSLB health monitor at a real health path that returns 200 # token read from an env var, never hardcoded curl -s -H "Authorization: Bearer ${AVI_TOKEN}" -X PATCH https://avi-ctl.corp.local/api/healthmonitor/hm-vks -d ‘{"http_monitor":{"http_request":"GET /healthz HTTP/1.0","http_response_code":["HTTP_2XX"]}}’ # {"uuid":"healthmonitor-8b7c"} # member comes UP and starts taking its share curl -s -o /dev/null -w ‘%{http_code}’ https://app-vks.corp.local/healthz # 200

Production validation and what green looks like

Green is not a pod reporting Running and it is not a 200 on a health path. Green is the error rate holding inside its budget, a real write persisting on the target, and no writes still landing on the source after the shift. Validate all three at the 10 percent step, before you commit the other 90 percent, so a problem shows up while it is still small.

A false green is the trap. An ingress can answer 200 from a default backend that knows nothing about your service, and a read only test passes even when the storage mount is broken for writes. Drive one real order through the path, confirm it landed in the database on the target, then confirm the source log shows zero writes since the shift. That last check catches split traffic, where a client with a stale cache is still writing to TKGI while everyone else writes to VKS.

# error rate during the shift window, from the ingress metrics kubectl exec -n monitoring deploy/promtool — promtool query instant http://prometheus:9090 ‘sum(rate(http_requests_total{code=~"5.."}[5m]))/sum(rate(http_requests_total[5m]))’ # {} => 0.0011 (0.11 percent, just over the 0.10 budget for one bucket, then settled) # a real write persists on the target, not just a 200 on a health path curl -s -X POST https://app.corp.local/orders -d ‘sku=TEST-1&qty=1′ # {"order_id":"o-91d2","status":"created"} kubectl –context prod-vks exec -n payments pg-0 — psql -U app -t -c "select status from orders where order_id=’o-91d2’;" # created # confirm no writes are still hitting the source after the shift kubectl –context tkgi-prod logs -n payments deploy/web –since=2m | grep -c ‘POST /orders’ # 0

Watch the error rate shape, not just its peak. In this cutover the 10 percent step nudged 5xx responses to 0.11 percent, a hair over the 0.10 error budget, for a single four minute bucket while connections drained on the old side, then it settled back under budget on its own. A brief overshoot that recovers is normal, a rate that keeps climbing is your signal to shift the weight back.

Error rate across the cutover window5xx as percent of requests, five minute buckets, budget line at 0.10 percent0.10T+00.02T+50.11T+100.06T+150.03T+200.02T+250.02
One bucket over budget at the shift, then a clean settle, the pattern that says proceed not roll back

Rollback and failback to TKGI

Because TKGI was kept warm and untouched, rollback is a traffic move, not a data recovery. Shift the GSLB weight back to the source, confirm it is serving within one TTL, and users land where they started. That is the whole point of leaving the old pool defined at ratio zero rather than tearing it down at cutover, a failback measured in seconds instead of the hours a restore would take.

One part of rollback is genuinely hard, the writes that landed on VKS during the shift. Any order taken on the target while the cutover was live does not exist on the source, so a naive failback loses them. Because the source stayed warm you can reconcile instead of losing data: export the delta rows written to the target, replay them on the source, then tear the target down. Count the rows on both sides before you decide, so the choice to keep or discard is a number and not a guess.

# rollback is one weight change because tkgi was kept warm curl -s -H "Authorization: Bearer ${AVI_TOKEN}" -X PATCH https://avi-ctl.corp.local/api/gslbservice/gs-app -d ‘{"groups":[{"members":[{"fqdn":"app-tkgi.corp.local","ratio":1},{"fqdn":"app-vks.corp.local","ratio":0}]}]}’ # {"uuid":"gslbservice-2f1a"} # confirm the source is serving again within one TTL sleep 60; curl -s -o /dev/null -w ‘%{http_code}’ https://app.corp.local/healthz # 200 # reconcile writes that landed on VKS during the window before teardown kubectl –context prod-vks exec -n payments pg-0 — psql -U app -t -c ‘select count(*) from ledger;’ # 48231 # source held 48213 at the freeze, so 18 rows were written to VKS, export and replay them before discarding the target
stateDiagram-v2
  [*] --> Rehearsed
  Rehearsed --> Shifting: lower TTL, shift 10 percent
  Shifting --> Validated: error rate under budget
  Validated --> LiveOnVKS: shift to 100 percent
  Shifting --> RollingBack: budget breached
  RollingBack --> BackOnTKGI: weight back to source
  LiveOnVKS --> [*]
  BackOnTKGI --> [*]
Every state has an exit, and rollback is a first class path not an afterthought

Below is the failure lookup I keep open during a cutover. Each row is a symptom I have actually seen, its cause, and the fix, so a stall becomes a page turn rather than a debugging session with users waiting.

SymptomCauseFix
Traffic slow to move after the flipTTL not lowered ahead of the windowShift at the GSLB, wait out the cached TTL
New pool member marked DOWNHealth monitor probes a redirecting pathPoint the monitor at a 200 health path
Writes landing on both platformsStale caches still resolve to the sourceReconcile the delta, keep source warm
Ingress returns a 404 default backendIngress class or host not matched on VKSSet the VKS ingress class and host name
5xx spike that keeps climbingTarget undersized or a broken dependencyShift the weight back, diagnose off the path
Row count higher on target than sourceWrites taken on VKS during the shiftExport and replay before any teardown

Cutover recommendation and next move

My worst production cutover skipped one line of the timeline above. Someone left the DNS TTL at 3600 because lowering it a week early felt like premature risk. We flipped the A record at the top of the window, and forty percent of traffic was still hitting TKGI two hours later while caches slowly expired. Orders split across both platforms, and I spent the next morning reconciling about 1900 rows by hand. Every cutover I have run since steers at a GSLB and lowers the TTL seven days out, no exceptions.

For the traffic layer, my pick is a weighted GSLB shift with the TTL lowered a week ahead, because it gives you a dial and a fast reverse. Avoid the bare DNS flip for anything carrying real users or real writes, since it hands control of the timeline to every resolver on the internet. For the safety layer, keep TKGI warm and untouched through the full rollback window, so a bad cutover is a weight change and not a restore from backup.

One decision splits teams, how long to keep the source warm after a clean cutover. A day feels tidy, but the failures that force a rollback, a slow memory leak on the new platform or a batch job that only runs nightly, often surface after the first full business cycle. Hold the source at ratio zero for at least one business cycle past cutover, three to five business days in the running example, then decommission with the confidence that nothing hidden is waiting to bite.

Clean result checklist: DNS TTL lowered a week early and verified with dig from two resolvers.
Fresh final backup taken and restored to the target before the freeze.
Traffic shifted at the GSLB in steps, error rate held inside the budget.
Real write confirmed on the target and zero writes left on the source.
TKGI kept warm at ratio zero, rollback proven to take seconds.

Two questions come up on every cutover call. Can we cut over with a plain DNS change if we have no GSLB? You can, but lower the TTL well ahead and accept a slow, hard to reverse shift, or stand up a simple reverse proxy in front of both platforms for the day so you have a control point. Do we take a backup even though the pilot passed? Always, a pilot proves the procedure while a fresh backup protects the specific data you are about to move, and the two are not interchangeable.

For the mechanics behind this move, the ingress and DNS cutover part sets up the addresses this shift steers, and the pilot part is the rehearsal this production run repeats. The TKGI to VKS guide holds the full map, and the series hub links the related VKS and VCF 9 work.

On Monday, open your DNS zone and check the TTL on the records you will cut over. If any still read 3600, lower them to 60 today, because that one edit a week early is the difference between a cutover you control and a cutover that controls you. Next part takes the migration to its end, decommissioning TKGI and Ops Manager once the warm window closes.

TKGI to VKS Series · Part 23 of 26
« Previous: Part 22  |  Guide  |  Next: Part 24 »

References

VMware Cloud Foundation blog, Mastering Application Migration to VKS
Velero docs, cluster migration case
Velero on GitHub, backup and migrate Kubernetes applications

About The Author


Discover more from Journal of Intelligent Infrastructure

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure

Subscribe now to keep reading and get access to the full archive.

Continue reading