Observe, Migrate, Recover, and Map Ambient Mesh to EKS
Treat mesh adoption as a reversible product migration whose evidence climbs from node proxies to customer requests.
Run it in the public monorepo
This course is built around the public Zheta Kubernetes Lab monorepo. The excerpt below is runnable source, not pseudocode.
Source: scripts/failure-istio-ambient.sh
: "${TARGET_POD:?set TARGET_POD to the reviewed pod name}"
: "${TARGET_POD_UID:?set TARGET_POD_UID to the reviewed pod UID}"
positive_control() {
kubectl --context "$MESH_CONTEXT" get --raw=/readyz >/dev/null
[ "$(kubectl --context "$MESH_CONTEXT" -n zheta-forge get deployment/control-plane -o jsonpath='{.status.availableReplicas}')" -ge 1 ]
}
Code to reality
- Declared intent
- Require one reviewed Pod name and immutable UID while preserving API and product availability before bounded failure.
- Interpreter
- The shell validates context, approval, UID, label, owner, and cardinality before kubectl invokes the owning controller path.
- Software effect
- Only the exact approved proxy can terminate; one different replacement UID must appear and product policy probes must still pass.
- Hardware effect
- The selected node stops and starts a proxy process, reloads image layers, and re-establishes encrypted connections.
- Observable evidence
- Name/UID, label, owner, unique replacement, API readiness, control-plane availability, allowed POST, denied GET, and recovery time prove recovery.
Start with the people and the result they need
The source tables below remain the detailed contract. Begin with these customer paths:
- D34-UC-01
- Person: Enterprise builder
- Job: Continue generating and previewing applications while mesh infrastructure changes
- Observable result: Approved Forge request and intentional denial remain correct before and after one proxy replacement
- D34-UC-02
- Person: Platform migration commander
- Job: Unenroll Forge or remove shared mesh components with bounded ownership
- Observable result: Forge labels/policies are removed first; shared releases remain unless separately approved
Turn each customer job into a testable story
Now turn each customer job into a story with a result that an engineer can check:
- D34-US-01
- Story: As an enterprise builder, I want proxy failure recovery proven with my product request, so that controller health is connected to customer outcome
- Observable acceptance: Evidence correlates old/new proxy UIDs, recovery time, allowed and denied traces, source revision, environment, and run
- D34-US-02
- Story: As a migration commander, I want application unenrollment separated from shared control-plane removal, so that rollback does not delete another…
- Observable acceptance: Context and approval gates, removed Forge labels/policies, preserved releases/CRDs, and post-rollback request are recorded
Add real state and observable proof
Finally trace each story through the system that owns its state and the evidence that proves the outcome:
- D34-FLOW-01
- Trigger: Approved operator selects ztunnel-recovery or waypoint-recovery
- Responsible systems: ztunnel DaemonSet, waypoint Gateway controller, Kubernetes scheduler/kubelet, Istiod, Forge services
- Authoritative state: Kubernetes API for proxy desired/observed state; Forge/evidence stores for customer result
- Owned record: MeshMigrationRevision
- Observable evidence: Exact target, old/new UID, DaemonSet or Gateway status, recovery time, product traces, environment, time, incident ID
- Failure signal: Empty/ambiguous target, unreplaced Pod, unprogrammed Gateway, changed denial semantics, or failed replay
- D34-FLOW-02
- Trigger: Migration commander invokes bounded Forge rollback
- Responsible systems: namespace labels, Forge Gateway/policies, Helm release owners, Gateway API owner
- Authoritative state: Git and Kubernetes API for Forge state; Helm release metadata for shared mesh; external approval record
- Owned record: ProxyRecoveryEvent
- Observable evidence: Approval, context, removed objects, preserved shared resources, customer response, owner, environment, time, rollback ID
- Failure signal: Wrong context, missing approval, shared release removal without separate flag, CRD deletion, or broken direct path
The enterprise problem and today’s slice
Enterprise problem: A mesh can report healthy controllers while customer traffic is denied, bypasses a waypoint, or depends on one failed proxy; uninstalling shared infrastructure during rollback can widen the outage. Whole-course context: The incoming Forge path has ambient enrollment, strict mTLS, ServiceAccount L4 policy, and destination L7 policy, but no production claim. Today’s slice: We observe each boundary, inject one approved ztunnel or waypoint failure, verify controller recovery, and unenroll Forge separately from shared Istio removal. We map the same responsibilities to private Amazon EKS without pretending local Kind evidence proves AWS. End-of-day evidence: Context, versions, proxy/workload status, policy, positive/denied traces, old/new proxy UIDs, recovery time, rollback result, unaffected control, environment, run, and cost owner form the migration dossier. Still unsolved: Day 35 keeps production blocked until real EKS and customer evidence agree.
Customer use cases
Controller readiness is not customer readiness, so migration must preserve the full Forge lifecycle through normal, failed, recovered, and rolled-back states.
| Use case ID | Actor | Customer job | Success outcome | Denial or recovery evidence |
|---|---|---|---|---|
| D34-UC-01 | Enterprise builder | Continue generating and previewing applications while mesh infrastructure changes | Approved Forge request and intentional denial remain correct before and after one proxy replacement | Old/new ztunnel or waypoint UID, controller recovery, request replay, and unaffected positive control agree |
| D34-UC-02 | Platform migration commander | Unenroll Forge or remove shared mesh components with bounded ownership | Forge labels/policies are removed first; shared releases remain unless separately approved | Wrong context or absent approval vetoes action; Gateway API CRDs remain because they may have other owners |
Actor-centred user stories
Recovery without a product replay can restore a proxy while leaving the customer path broken.
| Story ID | Use case IDs | User story | Observable acceptance conditions |
|---|---|---|---|
| D34-US-01 | D34-UC-01 | As an enterprise builder, I want proxy failure recovery proven with my product request, so that controller health is connected to customer outcome | Evidence correlates old/new proxy UIDs, recovery time, allowed and denied traces, source revision, environment, and run |
| D34-US-02 | D34-UC-02 | As a migration commander, I want application unenrollment separated from shared control-plane removal, so that rollback does not delete another team’s cluster capability | Context and approval gates, removed Forge labels/policies, preserved releases/CRDs, and post-rollback request are recorded |
End-to-end product flows
A useful failure lab resolves one exact target and preserves an unaffected control, avoiding broad chaos that teaches little.
| Flow ID | Use case IDs | Path | Trigger | Numbered steps | Terminal evidence |
|---|---|---|---|---|---|
| D34-FLOW-01 | D34-UC-01 | Recovery | Approved operator selects ztunnel-recovery or waypoint-recovery | 1. Verify context and healthy product precondition. 2. Resolve one labelled proxy Pod. 3. Reject empty target. 4. Delete only that Pod. 5. Wait for owning controller. 6. Replay allowed and denied requests. 7. Probe unaffected service. | Exact target, old/new UID, DaemonSet or Gateway status, recovery time, product traces, environment, time, incident ID |
| D34-FLOW-02 | D34-UC-02 | Recovery | Migration commander invokes bounded Forge rollback | 1. Verify context and approval. 2. Remove namespace mesh labels. 3. Delete Forge waypoint and policies. 4. Preserve shared Istio releases by default. 5. Preserve Gateway API CRDs. 6. Verify direct baseline path. | Approval, context, removed objects, preserved shared resources, customer response, owner, environment, time, rollback ID |
The customer request remains the stable migration measure. Proxy status matters because it supports this outcome, not as an end by itself.
System design derived from the flows
Different controllers repair different failures, so diagnosis climbs from nodes and proxies through policy to the product response.
| Use case ID | Entry point | Responsible services | Authoritative store | Failure evidence |
|---|---|---|---|---|
| D34-UC-01 | Approved failure script and Forge smoke path | ztunnel DaemonSet, waypoint Gateway controller, Kubernetes scheduler/kubelet, Istiod, Forge services | Kubernetes API for proxy desired/observed state; Forge/evidence stores for customer result | Empty/ambiguous target, unreplaced Pod, unprogrammed Gateway, changed denial semantics, or failed replay |
| D34-UC-02 | Approved rollback script | namespace labels, Forge Gateway/policies, Helm release owners, Gateway API owner | Git and Kubernetes API for Forge state; Helm release metadata for shared mesh; external approval record | Wrong context, missing approval, shared release removal without separate flag, CRD deletion, or broken direct path |
The added controller path distinguishes ztunnel and waypoint recovery from application rollback. Both finish at the same customer evidence.
On Amazon EKS, AWS runs the Kubernetes control plane, while the platform team still owns Istio installation, worker-node capacity, CNI compatibility, mesh policy, workload behavior, telemetry, upgrades, and cost. Istio provides a platform setup path for EKS, but that guidance does not transfer these responsibilities to AWS (Istio platform setup).
Data model and ownership
Migration decisions need durable correlation, but they must not take ownership of application data or shared cluster resources.
Generated-application database: Not created in this slice — generated applications keep their tenant data while runtime and evidence owners record mesh migration state.
| Record or entity | Store and owner | Primary key | Foreign key or opaque reference | Tenant key | Material constraint | Lifecycle and deletion | Use case IDs |
|---|---|---|---|---|---|---|---|
| MeshMigrationRevision | Git and approval store, owned by Platform Networking | revision plus environment | Opaque Istio/Gateway versions, overlay, and approver refs | environment_id | One exact context, bounded scope, rollback owner, and immutable source | Draft, approve, apply, supersede, retain with release; never deletes app data | D34-UC-01, D34-UC-02 |
| ProxyRecoveryEvent | Evidence store, owned by SRE | incident_id plus target_uid | Opaque replacement UID, controller generation, and trace refs | environment_id | Exactly one validated target and one unaffected positive control | Append before mutation, close after replay, retain by incident policy | D34-UC-01 |
| MeshRollbackRecord | Evidence/approval store, owned by Platform Networking | rollback_id | Opaque removed Forge objects and preserved shared release refs | environment_id | Shared control plane removal requires separate explicit approval; CRDs remain | Open, execute, verify, close; retain after Forge unenrollment | D34-UC-02 |
The migration, incident, and rollback records make ownership and retention explicit. They reference product traces without becoming product authorities.
Run the bounded recovery loop
Deleting an arbitrary proxy can affect an unknown blast radius, so the checked-in script requires the exact context, explicit approval, and one nonempty labelled target.
Run recovery only after the staged evidence is sealed. Day 32 must contain the paired admission NetworkPolicies, reviewed evidence/generator/broker Pod UIDs, independent kindnet timeout, an HTTP GET/send/read that returns no response bytes, a candidate timeout/reset/broken-pipe/abort/zero-byte EOF, one destination-ztunnel record containing target IP:port plus evidence identity plus denial marker, and control-plane POST before/after. Day 33 must contain the Accepted/Programmed waypoint, waypoint-only L4 rewrite, executable POST success, and executable GET 403. Rendered policy, a candidate transport failure, and proxy status are not substitutes for the same-record denial plus live controls.
: "${MESH_CONTEXT:?set the exact kubectl context}"
kubectl --context "$MESH_CONTEXT" -n istio-system get pods -l app=ztunnel -o custom-columns=NAME:.metadata.name,UID:.metadata.uid,NODE:.spec.nodeName
MESH_FAILURE_APPROVED=1 TARGET_POD="${TARGET_POD:?review one exact name}" TARGET_POD_UID="${TARGET_POD_UID:?record its immutable UID}" ./scripts/failure-istio-ambient.sh ztunnel-recovery
# Or test the destination proxy independently:
MESH_FAILURE_APPROVED=1 TARGET_POD="$TARGET_POD" TARGET_POD_UID="$TARGET_POD_UID" ./scripts/failure-istio-ambient.sh waypoint-recovery
./scripts/verify-istio-ambient.sh forge
# Replay an approved Forge request and one expected denial before closing evidence.
| Effect field | What happens |
|---|---|
| Declared intent | Replace one reviewed name/UID whose label and owning controller match the selected drill, then prove a unique replacement and product policy |
| Interpreter | Shell validates context, approval, UID, label, owner, and cardinality; kubectl calls the API; DaemonSet or Deployment reconciles replacement |
| Software effect | One proxy identity terminates, a replacement becomes ready/programmed, and policy desired state remains |
| Hardware effect | A node stops one process and starts another, consuming CPU, memory, image storage, and network connections |
| Evidence | Target/replacement UIDs, exact owner/label, one replacement, rollout status, API/product positive controls, allowed POST, denied GET, and recovery time |
Roll back application ownership before cluster ownership
Removing shared Istio first can strand enrolled traffic and affect other namespaces, so rollback defaults to Forge-only unenrollment.
: "${MESH_CONTEXT:?set the exact kubectl context}"
MESH_ROLLBACK_APPROVED=1 ./scripts/rollback-istio-ambient.sh
The checked-in rollback supports only the local overlay and vetoes any other environment. It removes Forge enrollment and Forge-owned mesh resources, restores the local base NetworkPolicies, waits for all product Deployments, and proves an internal generation request. Shared Istio releases and Gateway API CRDs always remain because another owner may use them. It does not prove an AWS rollback; EKS requires a separately implemented and approved procedure against the intended private cluster, node groups, identities, and billing boundary.
Map local evidence to private EKS
Local Kind nodes collapse power, disk, and network failure domains onto one laptop, so their proxy recovery demonstrates reconciliation but not production availability.
| Layer | Local Kind proof | Private Amazon EKS proof | Owner |
|---|---|---|---|
| Hardware | Docker-backed node containers | EC2 worker instances across intended zones | Developer locally; AWS supplies hardware, platform owns node policy/cost |
| Kubernetes API | Kind control-plane container | Private EKS endpoint and access entries | Developer locally; AWS operates managed control plane |
| Mesh control plane | Pinned Helm releases and Gateway CRDs | Same approved versions installed into intended EKS cluster | Platform Networking |
| Data plane | ztunnel DaemonSet and waypoint Pod | ztunnel on every eligible node plus bounded waypoint capacity | Platform Networking and SRE |
| Product | Local Forge positive/denied traces | Production URL, identity, policy, SLO, cost, and recovery traces | Forge owners and launch commander |
The invariant question is unchanged: which owner declares each layer, which controller reconciles it, and which customer evidence proves the intended environment rather than a nearby cluster?
Before and after, side by side
A static mesh configuration previously had no bounded failure or exit proof, so operators could not distinguish recoverable proxy loss from a customer outage. The migrated state has an approved recovery loop and a Forge-first rollback boundary.
Key takeaways
Mesh migration is complete only when normal traffic, denial, failure recovery, and rollback all have product evidence.
- Diagnose substrate and proxies before policy and product symptoms.
- Delete one validated target, wait for its actual owner, then replay customer behavior.
- Unenroll the application before considering removal of shared cluster capability.
- Map local control relationships to EKS without promoting local evidence into an AWS claim.
Checklist
The migration dossier must be complete enough for Day 35 to veto production when any boundary is stale.
- [ ] Exact context, source, versions, approval, and healthy precondition recorded
- [ ] Local bypass run seals admitted transport, reviewed Pod UIDs, direct denial, destination-ztunnel observation, and before/after waypoint controls
- [ ] One ztunnel or waypoint failure recovered with old/new UIDs
- [ ] Approved and denied Forge traces replayed with unaffected control
- [ ] Forge-only rollback preserves shared releases and Gateway API CRDs
- [ ] EKS owners, private access, worker capacity, observability, and cost gaps named