This document contains detailed flow diagrams illustrating the various processes in the HA RKE2 cluster deployment and operation.
- Terraform Deployment Flow
- Cluster Initialization Flow
- Node Join Process
- API Request Flow
- Pod Scheduling Flow
- etcd Write Flow
- Failure Recovery Flow
- Network Traffic Flow
┌──────────────────────────────────────────────────────────────────────────┐
│ TERRAFORM DEPLOYMENT FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ terraform apply │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 1: NETWORKING │ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ module.vpc │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ├──► aws_vpc.this │ │
│ │ ├──► aws_internet_gateway.this │ │
│ │ ├──► aws_subnet.public[0,1,2] │ │
│ │ ├──► aws_route_table.public │ │
│ │ └──► aws_route_table_association.public[0,1,2] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 2: SECURITY │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ module.security_groups │ │ │
│ │ └───────────┬──────────────┘ │ │
│ │ │ │ │
│ │ ├──► aws_security_group.control_plane │ │
│ │ ├──► aws_security_group.worker │ │
│ │ └──► aws_security_group_rule.* (15+ rules) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 3: LOAD BALANCER │ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ module.nlb │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ├──► aws_lb.this (NLB) │ │
│ │ ├──► aws_lb_target_group.k8s_api (:6443) │ │
│ │ ├──► aws_lb_target_group.rke2_supervisor (:9345) │ │
│ │ ├──► aws_lb_listener.k8s_api │ │
│ │ └──► aws_lb_listener.rke2_supervisor │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 4: CONTROL PLANE │ │
│ │ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ module.control_plane │ │ │
│ │ └───────────┬──────────┘ │ │
│ │ │ │ │
│ │ ├──► aws_instance.this[0] (CP-1, first server) │ │
│ │ ├──► aws_instance.this[1] (CP-2, joins) │ │
│ │ └──► aws_instance.this[2] (CP-3, joins) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 5: TARGET ATTACHMENTS │ │
│ │ │ │
│ │ aws_lb_target_group_attachment.k8s_api[0,1,2] │ │
│ │ aws_lb_target_group_attachment.rke2_supervisor[0,1,2] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PHASE 6: WORKERS │ │
│ │ │ │
│ │ ┌────────────────┐ │ │
│ │ │ module.workers │ (depends_on: control_plane, target_attachments)│ │
│ │ └───────┬────────┘ │ │
│ │ │ │ │
│ │ ├──► aws_instance.this[0] (Worker-1) │ │
│ │ ├──► aws_instance.this[1] (Worker-2) │ │
│ │ └──► aws_instance.this[2] (Worker-3) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Apply complete! Resources: 25 added │
│ │
└──────────────────────────────────────────────────────────────────────────┘
flowchart TD
A[terraform apply] --> B[module.vpc]
B --> C[module.security_groups]
C --> D[module.nlb]
D --> E[module.control_plane]
E --> F[Target Group Attachments]
F --> G[module.workers]
G --> H[Apply Complete]
subgraph VPC["VPC Resources"]
B --> B1[VPC]
B --> B2[Internet Gateway]
B --> B3[Subnets x3]
B --> B4[Route Tables]
end
subgraph Security["Security Resources"]
C --> C1[CP Security Group]
C --> C2[Worker Security Group]
C --> C3[Security Rules]
end
subgraph NLB["Load Balancer"]
D --> D1[Network LB]
D --> D2[Target Groups x2]
D --> D3[Listeners x2]
end
┌──────────────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE 1 INITIALIZATION │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ EC2 Instance Starts │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ USER DATA SCRIPT EXECUTION │ │
│ │ │ │
│ │ 1. System Preparation │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ apt-get update && apt-get upgrade │ │ │
│ │ │ Install: curl, wget, jq, nfs-common, open-iscsi │ │ │
│ │ │ swapoff -a │ │ │
│ │ │ Load kernel modules: overlay, br_netfilter │ │ │
│ │ │ Configure sysctl: ip_forward, bridge-nf-call-iptables │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 2. RKE2 Configuration │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ mkdir -p /etc/rancher/rke2 │ │ │
│ │ │ Write config.yaml: │ │ │
│ │ │ token: ${rke2_token} │ │ │
│ │ │ tls-san: [nlb_dns, localhost, 127.0.0.1] │ │ │
│ │ │ cni: cilium │ │ │
│ │ │ cluster-cidr: 10.42.0.0/16 │ │ │
│ │ │ service-cidr: 10.43.0.0/16 │ │ │
│ │ │ node-taint: CriticalAddonsOnly=true:NoExecute │ │ │
│ │ │ # NO "server:" field - this is first node │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 3. RKE2 Installation │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ curl -sfL https://get.rke2.io | \ │ │ │
│ │ │ INSTALL_RKE2_VERSION="${rke2_version}" \ │ │ │
│ │ │ INSTALL_RKE2_TYPE="server" sh - │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 4. Start RKE2 Server │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ systemctl enable rke2-server.service │ │ │
│ │ │ systemctl start rke2-server.service │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 5. Internal Initialization │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ • Generate cluster CA certificates │ │ │
│ │ │ • Initialize etcd (single member) │ │ │
│ │ │ • Start kube-apiserver │ │ │
│ │ │ • Start kube-scheduler │ │ │
│ │ │ • Start kube-controller-manager │ │ │
│ │ │ • Deploy Cilium CNI │ │ │
│ │ │ • Deploy CoreDNS │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 6. Wait for Ready │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ while ! kubectl get nodes; do sleep 10; done │ │ │
│ │ │ Node status: Ready │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ CP-1 Ready (API: :6443, Supervisor: :9345) │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE 2/3 JOIN PROCESS │
├───────────────────────────────────────────────────────────────────┤
│ │
│ CP-2 EC2 Instance Starts │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ System Preparation (same as CP-1) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RKE2 Configuration │ │
│ │ config.yaml: │ │
│ │ server: https://${nlb_dns}:9345 ◄── JOIN URL │ │
│ │ token: ${rke2_token} │ │
│ │ ... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ WAIT FOR FIRST SERVER │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ while ! curl -sk https://${nlb}:9345/ping; do │ │ │
│ │ │ sleep 10 │ │ │
│ │ │ retries++ │ │ │
│ │ │ if retries > 60; then exit 1; fi │ │ │
│ │ │ done │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ NLB routes to CP-1 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ JOIN PROCESS │ │
│ │ │ │
│ │ 1. Contact supervisor API via NLB │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ NLB:9345 ─────► CP-1:9345 v │ │ │
│ │ │ ◄───── CA certs, join token │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 2. Download CA certificates │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 3. Generate node certificates │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 4. Join etcd cluster │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ etcd member add CP-2 │ │ │
│ │ │ CP-1 etcd ◄──► CP-2 etcd (Raft) │ │ │
│ │ │ Cluster: 2 members │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 5. Start local kube-apiserver │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 6. Register node with cluster │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ CP-2 Joined and Ready │
│ │
│ ════════════════════════════════════════════════════════════ │
│ │
│ CP-3 follows same process │
│ After CP-3 joins: etcd has 3 members (quorum achieved) │
│ │
└───────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ WORKER NODE JOIN PROCESS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Worker EC2 Instance Starts │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ System Preparation │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ WAIT FOR CONTROL PLANE (More Strict) │ │
│ │ │ │
│ │ Check BOTH endpoints: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ curl -sk https://${nlb}:9345/ping ✓ │ │ │
│ │ │ curl -sk https://${nlb}:6443/healthz ✓ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Additional 30 second stability wait │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RKE2 Agent Configuration │ │
│ │ config.yaml: │ │
│ │ server: https://${nlb_dns}:9345 │ │
│ │ token: ${rke2_token} │ │
│ │ # NOTE: Much simpler config than server │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ RKE2 Agent Installation │ │
│ │ curl -sfL https://get.rke2.io | \ │ │
│ │ INSTALL_RKE2_TYPE="agent" sh - ◄── agent, not server│ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Start RKE2 Agent │ │
│ │ systemctl enable rke2-agent.service │ │
│ │ systemctl start rke2-agent.service │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ JOIN PROCESS │ │
│ │ │ │
│ │ 1. Contact supervisor via NLB │ │
│ │ 2. Download kubelet certificates │ │
│ │ 3. Start kubelet │ │
│ │ 4. Register with API server │ │
│ │ 5. Receive Cilium agent (DaemonSet) │ │
│ │ 6. Node becomes Ready │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Worker Ready for Workloads │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────┐
│ KUBECTL REQUEST FLOW │
├────────────────────────────────────────────────────────────────────┤
│ │
│ User: kubectl get pods │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ kubectl reads kubeconfig │ │
│ │ server: https://nlb-xxx.elb.amazonaws.com:6443 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ HTTPS Request │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ DNS Resolution │ │
│ │ nlb-xxx.elb.amazonaws.com → Multiple IPs (AZ-aware) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Network Load Balancer │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Health Check: │ │ │
│ │ │ CP-1: Healthy ✓ │ │ │
│ │ │ CP-2: Healthy ✓ │ │ │
│ │ │ CP-3: Healthy ✓ │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Load balancing: Round-robin to healthy target │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Routes to CP-2 (example) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-2: kube-apiserver │ │
│ │ │ │
│ │ 1. TLS Handshake (certificate validation) │ │
│ │ 2. Authentication (client cert from kubeconfig) │ │
│ │ 3. Authorization (RBAC check) │ │
│ │ 4. Admission (mutating/validating webhooks) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ GET /api/v1/namespaces/default/pods │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-2: etcd client │ │
│ │ │ │
│ │ Read from local etcd member │ │
│ │ (or forward to leader if needed) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Pod list data │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Response: 200 OK │ │
│ │ Body: { "items": [...] } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Back through NLB │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ kubectl displays output │ │
│ │ NAME READY STATUS RESTARTS AGE │ │
│ │ nginx 1/1 Running 0 5m │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ POD SCHEDULING FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ kubectl apply -f deployment.yaml │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ API Server receives Deployment │ │
│ │ Stores in etcd │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Watch event │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Deployment Controller │ │
│ │ Creates ReplicaSet │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Watch event │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ReplicaSet Controller │ │
│ │ Creates Pod objects (spec.nodeName = empty) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Watch event: Unscheduled pod │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ kube-scheduler (Leader on one CP) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ FILTERING PHASE │ │ │
│ │ │ │ │ │
│ │ │ Check each node: │ │ │
│ │ │ • CP-1: SKIP (taint: CriticalAddonsOnly) │ │ │
│ │ │ • CP-2: SKIP (taint: CriticalAddonsOnly) │ │ │
│ │ │ • CP-3: SKIP (taint: CriticalAddonsOnly) │ │ │
│ │ │ • Worker-1: OK ✓ │ │ │
│ │ │ • Worker-2: OK ✓ │ │ │
│ │ │ • Worker-3: OK ✓ │ │ │
│ │ │ │ │ │
│ │ │ Feasible nodes: [Worker-1, Worker-2, Worker-3] │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ SCORING PHASE │ │ │
│ │ │ │ │ │
│ │ │ Score each feasible node: │ │ │
│ │ │ • Worker-1: CPU=70%, Mem=60% → Score: 65 │ │ │
│ │ │ • Worker-2: CPU=40%, Mem=50% → Score: 85 ★ │ │ │
│ │ │ • Worker-3: CPU=80%, Mem=70% → Score: 55 │ │ │
│ │ │ │ │ │
│ │ │ Selected: Worker-2 (highest score) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Bind pod to Worker-2 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ API Server updates pod.spec.nodeName = "Worker-2" │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Watch event │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ kubelet on Worker-2 │ │
│ │ │ │
│ │ 1. Pull container image │ │
│ │ 2. Create container via containerd │ │
│ │ 3. Setup networking via Cilium CNI │ │
│ │ 4. Start container │ │
│ │ 5. Run health probes │ │
│ │ 6. Report status to API server │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Pod Running on Worker-2 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ ETCD WRITE FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ kubectl create deployment nginx --image=nginx │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Request reaches kube-apiserver (any CP) │ │
│ │ Example: CP-2 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Write request │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-2: Local etcd client │ │
│ │ PUT /registry/deployments/default/nginx │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Is CP-2 the etcd leader? │
│ │ │
│ ├───────────────────────────────────────────┐ │
│ │ NO: Forward to leader │ YES: Process │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Forward to CP-1 │ │ CP-2 is Leader │ │
│ │ (current leader) │ │ Process locally │ │
│ └─────────┬──────────┘ └─────────┬──────────┘ │
│ │ │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RAFT CONSENSUS (Leader: CP-1 in this example) │ │
│ │ │ │
│ │ Step 1: Append to local log │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ Log: [..., PUT nginx (uncommitted)] │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Step 2: Replicate to followers │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ ┌───────────┐ │ │ │
│ │ │ │ CP-1 │ │ │ │
│ │ │ │ (Leader) │ │ │ │
│ │ │ └─────┬─────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ AppendEntries│ AppendEntries │ │ │
│ │ │ ┌────────┴────────┐ │ │ │
│ │ │ ▼ ▼ │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │ CP-2 │ │ CP-3 │ │ │ │
│ │ │ │ (Follower)│ │ (Follower)│ │ │ │
│ │ │ └─────┬─────┘ └─────┬─────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └───── ACK ───────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ Quorum: 2/3 ✓ │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Step 3: Commit entry │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ Log: [..., PUT nginx (COMMITTED ✓)] │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Step 4: Notify followers to commit │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Response to API server: Success │ │
│ │ API server responds to kubectl: deployment created │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE FAILURE RECOVERY │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ INITIAL STATE (Healthy) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-1 CP-2 CP-3 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ etcd │ ◄───► │ etcd │ ◄───► │ etcd │ │ │
│ │ │LEADER │ │follower│ │follower│ │ │
│ │ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ NLB targets: [CP-1 ✓, CP-2 ✓, CP-3 ✓] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ T+0:00 - CP-1 CRASHES │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-1 CP-2 CP-3 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ ✗ ✗ │ │ etcd │ ◄───► │ etcd │ │ │
│ │ │ DOWN │ │follower│ │follower│ │ │
│ │ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ etcd: No leader, waiting for election timeout │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ T+0:01 - Election timeout (150-300ms) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ELECTION STARTS │ │
│ │ │ │
│ │ CP-2: Increments term, becomes candidate │ │
│ │ CP-2: Requests votes from CP-3 │ │
│ │ CP-3: Grants vote to CP-2 (log is up-to-date) │ │
│ │ CP-2: Receives majority (2/3), becomes LEADER │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ T+0:02 - New leader │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-1 CP-2 CP-3 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ ✗ ✗ │ │ etcd │ ◄───► │ etcd │ │ │
│ │ │ DOWN │ │LEADER │ │follower│ │ │
│ │ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ Cluster: Fully operational (2/3 quorum) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ T+0:30 - NLB health check fails for CP-1 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ NLB UPDATE │ │
│ │ │ │
│ │ NLB targets: [CP-1 ✗, CP-2 ✓, CP-3 ✓] │ │
│ │ │ │
│ │ Traffic now routes only to CP-2 and CP-3 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ T+5:00 - Admin fixes CP-1 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CP-1 REJOINS │ │
│ │ │ │
│ │ 1. CP-1 starts, joins as follower │ │
│ │ 2. Syncs log from leader (CP-2) │ │
│ │ 3. NLB health check passes │ │
│ │ 4. CP-1 added back to rotation │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RECOVERED STATE │ │
│ │ │ │
│ │ CP-1 CP-2 CP-3 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ etcd │ ◄───► │ etcd │ ◄───► │ etcd │ │ │
│ │ │follower│ │LEADER │ │follower│ │ │
│ │ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ NLB targets: [CP-1 ✓, CP-2 ✓, CP-3 ✓] │ │
│ │ Cluster: Fully HA again │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ POD-TO-POD (SAME NODE) TRAFFIC FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Worker-1 Node │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ ││
│ │ ┌──────────────┐ ┌──────────────┐ ││
│ │ │ Pod A │ │ Pod B │ ││
│ │ │ 10.42.3.10 │ │ 10.42.3.11 │ ││
│ │ │ │ │ │ ││
│ │ │ eth0 (veth) │ │ eth0 (veth) │ ││
│ │ └──────┬───────┘ └──────┬───────┘ ││
│ │ │ │ ││
│ │ │ 1. Send to 10.42.3.11 │ ││
│ │ ▼ │ ││
│ │ ┌──────────────────────────────────────────────────────────────┐ ││
│ │ │ Cilium eBPF (kernel) │ ││
│ │ │ │ ││
│ │ │ 2. eBPF lookup: 10.42.3.11 → local endpoint │ ││
│ │ │ 3. Direct packet redirect (no bridge/iptables) │ ││
│ │ │ │ ││
│ │ └──────────────────────────────────────────────────────────────┘ ││
│ │ │ ▲ ││
│ │ └──────────────────────────────┘ ││
│ │ 4. Delivered ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ │
│ Latency: ~50 microseconds (eBPF fast path) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ POD-TO-POD (CROSS NODE) TRAFFIC FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Worker-1 Node Worker-2 Node │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ │ │ │ │
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │
│ │ │ Pod A │ │ │ │ Pod C │ │ │
│ │ │ 10.42.3.10 │ │ │ │ 10.42.4.20 │ │ │
│ │ └──────┬───────┘ │ │ └──────┬───────┘ │ │
│ │ │ │ │ ▲ │ │
│ │ ▼ │ │ │ │ │
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │
│ │ │ Cilium Agent │ │ │ │ Cilium Agent │ │ │
│ │ │ (eBPF) │ │ │ │ (eBPF) │ │ │
│ │ └──────┬───────┘ │ │ └──────┬───────┘ │ │
│ │ │ │ │ ▲ │ │
│ │ 1. Lookup route │ │ 5. Decapsulate │ │
│ │ 10.42.4.0/24 │ │ VXLAN packet │ │
│ │ → Worker-2 │ │ │ │
│ │ │ │ │ │ │ │
│ │ 2. VXLAN │ │ │ │ │
│ │ encapsulate │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ ▼ │ │ │ │ │
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │
│ │ │ eth0 │ │ │ │ eth0 │ │ │
│ │ │ 10.0.1.x │ │ │ │ 10.0.2.x │ │ │
│ │ └──────┬───────┘ │ │ └──────┬───────┘ │ │
│ │ │ │ │ ▲ │ │
│ └─────────┼───────────┘ └──────────┼──────────┘ │
│ │ │ │
│ │ 3. UDP:8472 VXLAN packet │ │
│ │ Outer: 10.0.1.x → 10.0.2.x │ │
│ │ Inner: 10.42.3.10 → 10.42.4.20 │ │
│ │ │ │
│ └──────────────────────────────────────┘ │
│ 4. AWS VPC routing │
│ │
│ Latency: ~200-500 microseconds (VXLAN overhead) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ NODEPORT SERVICE TRAFFIC FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ External Client │
│ │ │
│ │ 1. HTTP request to Worker-1:30080 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Internet Gateway │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Security Group: Allow 30000-32767 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Worker-1 Node │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ Cilium eBPF / kube-proxy │ │ │
│ │ │ │ │ │
│ │ │ 2. NodePort 30080 → Service nginx-service │ │ │
│ │ │ 3. Service → Endpoint selection │ │ │
│ │ │ Endpoints: [10.42.3.10, 10.42.4.20, 10.42.5.30] │ │ │
│ │ │ 4. Load balance → 10.42.4.20 (Pod on Worker-2) │ │ │
│ │ │ 5. DNAT: dst 10.0.1.x:30080 → 10.42.4.20:80 │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ If pod is on another node... │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ VXLAN tunnel to Worker-2 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 6. VXLAN to Worker-2 │
│ ▼ │
│ Worker-2 Node │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 7. Deliver to Pod (10.42.4.20:80) │ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ │ │ nginx Pod │ ◄─── Request │ │
│ │ │ 10.42.4.20 │ ───► Response │ │
│ │ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 8. Response follows reverse path │
│ ▼ │
│ External Client receives response │
│ │
└──────────────────────────────────────────────────────────────────────────┘
flowchart TB
subgraph Internet["Internet"]
User[("User/kubectl")]
end
subgraph AWS["AWS Cloud"]
subgraph VPC["VPC 10.0.0.0/16"]
IGW[Internet Gateway]
subgraph NLB["Network Load Balancer"]
L1["Listener :6443"]
L2["Listener :9345"]
end
subgraph AZ1["AZ-a (10.0.1.0/24)"]
CP1["CP-1<br/>etcd + API"]
W1["Worker-1<br/>kubelet"]
end
subgraph AZ2["AZ-b (10.0.2.0/24)"]
CP2["CP-2<br/>etcd + API"]
W2["Worker-2<br/>kubelet"]
end
subgraph AZ3["AZ-c (10.0.3.0/24)"]
CP3["CP-3<br/>etcd + API"]
W3["Worker-3<br/>kubelet"]
end
end
end
User --> IGW
IGW --> L1 & L2
L1 --> CP1 & CP2 & CP3
L2 --> CP1 & CP2 & CP3
CP1 <-.->|etcd Raft| CP2
CP2 <-.->|etcd Raft| CP3
CP3 <-.->|etcd Raft| CP1
CP1 & CP2 & CP3 -->|kubelet API| W1 & W2 & W3
W1 <-.->|Cilium VXLAN| W2
W2 <-.->|Cilium VXLAN| W3
W3 <-.->|Cilium VXLAN| W1
sequenceDiagram
participant User
participant kubectl
participant NLB
participant API as kube-apiserver
participant etcd
participant Scheduler
participant kubelet
participant Pod
User->>kubectl: kubectl apply -f deploy.yaml
kubectl->>NLB: HTTPS POST /apis/apps/v1/deployments
NLB->>API: Forward to healthy CP
API->>etcd: Store Deployment
etcd-->>API: Committed (quorum)
API-->>kubectl: 201 Created
Note over API,Scheduler: Controller creates Pods
Scheduler->>API: Watch unscheduled pods
API-->>Scheduler: New pod (nodeName=empty)
Scheduler->>Scheduler: Filter & Score nodes
Scheduler->>API: Bind pod to Worker-2
API->>etcd: Update pod.spec.nodeName
kubelet->>API: Watch pods for this node
API-->>kubelet: New pod assigned
kubelet->>Pod: Create container
Pod-->>kubelet: Running
kubelet->>API: Update pod status
Back to Main README | Previous: Deployment Guide | Next: Troubleshooting