This document provides an in-depth explanation of the High Availability (HA) concepts implemented in this RKE2 Kubernetes cluster.
- What is RKE2?
- HA Architecture Overview
- etcd Cluster Design
- Control Plane HA
- Network Load Balancer Strategy
- Node Roles and Taints
- Cilium CNI
- Cluster Initialization Process
- Failure Scenarios and Recovery
- Scaling Considerations
- RKE2 vs Other Distributions
RKE2 (Rancher Kubernetes Engine 2), also known as RKE Government, is a fully conformant Kubernetes distribution that focuses on security and compliance.
| Feature | Description |
|---|---|
| Security-First | CIS Kubernetes Benchmark compliant by default |
| Embedded etcd | Built-in etcd for simpler operations |
| Containerd Runtime | Uses containerd instead of Docker |
| Cilium Support | eBPF-based networking with advanced features |
| Air-Gap Ready | Can run in disconnected environments |
| FIPS Compliance | Available FIPS 140-2 compliant builds |
┌─────────────────────────────────────────────────────────────────────────┐
│ RKE2 SERVER NODE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ rke2-server Process │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ kube-api │ │ kube- │ │ kube-ctrl- │ │ │
│ │ │ server │ │ scheduler │ │ manager │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ etcd │ │ cloud-ctrl- │ │ kube-proxy │ │ │
│ │ │ (embedded) │ │ manager │ │ (optional) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ containerd Runtime │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ kubelet │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ RKE2 AGENT NODE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ rke2-agent Process │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ kubelet │ │ kube-proxy │ │ │
│ │ │ │ │ (optional) │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ containerd Runtime │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
The number 3 is critical for HA:
┌─────────────────────────────────────────────────────────────────────────┐
│ QUORUM REQUIREMENTS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Total Nodes │ Quorum Needed │ Tolerated Failures │ Status │
│ ────────────│───────────────│────────────────────│─────────────────────│
│ 1 │ 1 │ 0 │ No HA │
│ 2 │ 2 │ 0 │ No HA (split-brain) │
│ 3 │ 2 │ 1 │ HA (Recommended) │
│ 5 │ 3 │ 2 │ Enhanced HA │
│ 7 │ 4 │ 3 │ Maximum HA │
│ │
│ Formula: Quorum = (N/2) + 1 │
│ Formula: Failures = N - Quorum │
│ │
└─────────────────────────────────────────────────────────────────────────┘
| Aspect | 3 Nodes | 5 Nodes |
|---|---|---|
| Fault Tolerance | 1 failure | 2 failures |
| Cost | Lower | Higher |
| etcd Latency | Lower | Higher (more replication) |
| Typical Use | Production | Critical Production |
This cluster uses 3 nodes as the optimal balance for most production workloads.
RKE2 uses embedded etcd rather than an external etcd cluster:
┌────────────────────────────────────────────────────────────────────────────────┐
│ ETCD CLUSTER TOPOLOGY │
├────────────────────────────────────────────────────────────────────────────────┤
│ │
│ Control Plane 1 Control Plane 2 Control Plane 3 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ │ │ │ │ │
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │
│ │ │ etcd │ │◄───────►│ │ etcd │ │◄───────►│ │ etcd │ │ │
│ │ │ Member │ │ Raft │ │ Member │ │ Raft │ │ Member │ │ │
│ │ │ :2379 │ │ 2380 │ │ :2379 │ │ 2380 │ │ :2379 │ │ │
│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │
│ │ │ │ │ │ │ │
│ │ ▲ │ │ ▲ │ │ ▲ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ ▼ │ │ ▼ │ │ ▼ │ │
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │
│ │ │ kube-api │ │ │ │ kube-api │ │ │ │ kube-api │ │ │
│ │ │ server │ │ │ │ server │ │ │ │ server │ │ │
│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │
│ │ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ Ports: │
│ 2379 - Client communication │
│ 2380 - Peer communication │
│ 2381 - Metrics │
│ │
└────────────────────────────────────────────────────────────────────────────────┘
etcd uses Raft for leader election and log replication:
┌─────────────────────────────────────────────────────────────────────────┐
│ RAFT CONSENSUS FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ WRITE REQUEST: │
│ │
│ 1. Client sends write to any member │
│ ┌────────┐ │
│ │ Client │ ─────► PUT /registry/pods/nginx │
│ └────────┘ │
│ │ │
│ ▼ │
│ 2. Forward to Leader (if not leader) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Follower │ ───► │ Leader │ │ Follower │ │
│ └────────────┘ └─────┬──────┘ └────────────┘ │
│ │ │
│ 3. Leader appends to log │ │
│ ┌─────────────────────────▼──────────────────────────┐ │
│ │ Log: [entry1, entry2, ..., PUT nginx] │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ 4. Leader replicates to followers │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Follower │ ◄── │ Leader │ ──► │ Follower │ │
│ │ (ACK) │ │ │ │ (ACK) │ │
│ └────────────┘ └─────┬──────┘ └────────────┘ │
│ │ │
│ 5. Once majority ACK, Leader commits │
│ ┌─────────────────────────▼──────────────────────────┐ │
│ │ Committed: [entry1, entry2, ..., PUT nginx] ✓ │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ 6. Response to client │ │
│ ┌────────┐ ▼ │
│ │ Client │ ◄───── 200 OK │
│ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
/var/lib/rancher/rke2/server/db/
├── etcd/
│ ├── member/
│ │ ├── snap/ # Snapshot files
│ │ └── wal/ # Write-ahead log
│ └── name # Member name
┌─────────────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE COMPONENTS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Component │ Instances │ HA Mechanism │
│ ───────────────────│───────────│───────────────────────────────────── │
│ kube-apiserver │ 3 │ Load balanced via NLB │
│ kube-scheduler │ 3 │ Leader election (only 1 active) │
│ kube-controller │ 3 │ Leader election (only 1 active) │
│ etcd │ 3 │ Raft consensus (quorum) │
│ cloud-controller │ 3 │ Leader election (only 1 active) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
For scheduler and controller-manager:
┌─────────────────────────────────────────────────────────────────────────┐
│ LEADER ELECTION │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Initial State: │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │ │ CP-3 │ │
│ │ scheduler│ │ scheduler│ │ scheduler│ │
│ │ STANDBY │ │ STANDBY │ │ STANDBY │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ After Election: │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │ │ CP-3 │ │
│ │ scheduler│ │ scheduler│ │ scheduler│ │
│ │ LEADER ◄┼────┼─ STANDBY │ │ STANDBY │ │
│ │ (active) │ │ (watches)│ │ (watches)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ If Leader Fails: │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │ │ CP-3 │ │
│ │ scheduler│ │ scheduler│ │ scheduler│ │
│ │ DEAD │ │ LEADER ◄┼────┼─ STANDBY │ │
│ │ ✗ ✗ │ │ (elected)│ │ (watches)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Election uses Kubernetes Lease objects in kube-system namespace │
│ │
└─────────────────────────────────────────────────────────────────────────┘
| Load Balancer Type | Layer | Best For |
|---|---|---|
| Application LB (ALB) | 7 (HTTP) | Web applications |
| Network LB (NLB) | 4 (TCP/UDP) | Kubernetes API ✓ |
| Classic LB | 4/7 | Legacy |
NLB is used because:
- TCP passthrough (doesn't terminate TLS)
- Preserves source IPs
- Ultra-low latency
- Static IPs (optional)
┌─────────────────────────────────────────────────────────────────────────┐
│ NLB TARGET DISTRIBUTION │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Incoming Request │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ NLB │ │
│ │ Cross-Zone: ON │ │
│ └────────┬────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │ │ CP-3 │ │
│ │ AZ-a │ │ AZ-b │ │ AZ-c │ │
│ │ │ │ │ │ │ │
│ │ Weight: │ │ Weight: │ │ Weight: │ │
│ │ 33% │ │ 33% │ │ 34% │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Health Checks: │
│ - Protocol: TCP │
│ - Interval: 30 seconds │
│ - Healthy threshold: 3 │
│ - Unhealthy threshold: 3 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
The RKE2 config includes TLS Subject Alternative Names:
tls-san:
- "${nlb_dns_name}" # NLB DNS for external access
- "localhost" # Local access
- "127.0.0.1" # Local accessThis ensures the API server certificate is valid when accessed via the NLB.
node-taint:
- "CriticalAddonsOnly=true:NoExecute"This taint prevents regular workloads from being scheduled on control plane nodes:
┌─────────────────────────────────────────────────────────────────────────┐
│ TAINT EFFECT │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Control Plane Node │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Taint: CriticalAddonsOnly=true:NoExecute │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ ALLOWED (with toleration): │ │ │
│ │ │ - CoreDNS │ │ │
│ │ │ - Cilium │ │ │
│ │ │ - kube-proxy │ │ │
│ │ │ - Metrics server │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ BLOCKED (no toleration): │ │ │
│ │ │ - Your application pods │ │ │
│ │ │ - Databases │ │ │
│ │ │ - Web servers │ │ │
│ │ │ - Any regular workload │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ Worker Node │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ No Taint │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ ALLOWED: │ │ │
│ │ │ - All pods (system and user workloads) │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Nodes are automatically labeled with:
# Control Plane nodes
node-role.kubernetes.io/control-plane: "true"
node-role.kubernetes.io/master: "true"
node-role.kubernetes.io/etcd: "true"
# Worker nodes
node-role.kubernetes.io/worker: "true"This cluster uses Cilium as the Container Network Interface (CNI):
| Feature | Benefit |
|---|---|
| eBPF-based | High performance, low overhead |
| Network Policies | L3/L4/L7 policy enforcement |
| Service Mesh | Optional sidecar-less mesh |
| Observability | Hubble for flow visibility |
| Multi-Cluster | ClusterMesh support |
┌─────────────────────────────────────────────────────────────────────────┐
│ CILIUM ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Node 1 Node 2 │
│ ┌───────────────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │
│ │ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │ │
│ │ │Pod A│ │Pod B│ │ │ │Pod C│ │Pod D│ │ │
│ │ │10.42│ │10.42│ │ │ │10.42│ │10.42│ │ │
│ │ │.0.5 │ │.0.6 │ │ │ │.1.5 │ │.1.6 │ │ │
│ │ └──┬──┘ └──┬──┘ │ │ └──┬──┘ └──┬──┘ │ │
│ │ │ │ │ │ │ │ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌────▼────┐ │ │ ┌────▼────┐ │ │
│ │ │ Cilium │ │ │ │ Cilium │ │ │
│ │ │ Agent │ │ │ │ Agent │ │ │
│ │ │ (eBPF) │ │ │ │ (eBPF) │ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ └──────────┼────────────┘ └──────────┼───────────┘ │
│ │ │ │
│ │ VXLAN Tunnel (8472) │ │
│ └──────────────────────────────────┘ │
│ │
│ Pod CIDR: 10.42.0.0/16 │
│ VXLAN Port: 8472 │
│ Health Check Port: 4240 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ CLUSTER INITIALIZATION SEQUENCE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ T+0min CP-1 starts (first server) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ • No "server" in config (initializes new cluster) │ │
│ │ • Creates etcd cluster (single member) │ │
│ │ • Generates cluster CA certificates │ │
│ │ • Starts kube-apiserver │ │
│ │ • Deploys Cilium CNI │ │
│ │ • Becomes ready on :6443 and :9345 │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ T+2min CP-2 starts (joins via NLB) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ • Waits for https://<nlb>:9345/ping │ │
│ │ • "server: https://<nlb>:9345" in config │ │
│ │ • Downloads CA certs from CP-1 │ │
│ │ • Joins etcd cluster (2 members) │ │
│ │ • Starts kube-apiserver (connected to local etcd) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ T+4min CP-3 starts (joins via NLB) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ • Same process as CP-2 │ │
│ │ • Joins etcd cluster (3 members - quorum achieved) │ │
│ │ • Cluster now fully HA │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ T+6min Workers start (join via NLB) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ • Wait for both :9345 AND :6443 to be healthy │ │
│ │ • Install as rke2-agent (not server) │ │
│ │ • Register with API server │ │
│ │ • Receive Cilium agent from DaemonSet │ │
│ │ • Ready for workloads │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
First Server (CP-1):
# No server field - initializes cluster
token: "shared-secret-token"
tls-san:
- "nlb-dns-name"Additional Servers (CP-2, CP-3):
server: https://nlb-dns-name:9345 # Join via NLB
token: "shared-secret-token"
tls-san:
- "nlb-dns-name"Agents (Workers):
server: https://nlb-dns-name:9345 # Join via NLB
token: "shared-secret-token"┌─────────────────────────────────────────────────────────────────────────┐
│ FAILURE SCENARIOS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Scenario │ Impact │ Recovery │
│ ──────────────────────│────────────────────│────────────────────────── │
│ 1 CP failure │ None (automatic) │ NLB routes around │
│ │ │ etcd 2/3 quorum ok │
│ ──────────────────────│────────────────────│────────────────────────── │
│ 2 CP failures │ API read-only │ Manual: restore 1 CP │
│ │ No new deployments │ etcd loses quorum │
│ ──────────────────────│────────────────────│────────────────────────── │
│ 1 Worker failure │ Pods rescheduled │ Automatic by K8s │
│ │ Brief disruption │ │
│ ──────────────────────│────────────────────│────────────────────────── │
│ All workers fail │ Workloads down │ Manual: restore workers │
│ │ Cluster healthy │ Pods reschedule │
│ ──────────────────────│────────────────────│────────────────────────── │
│ 1 AZ failure │ Minimal (2 AZs ok) │ Automatic failover │
│ │ │ │
│ ──────────────────────│────────────────────│────────────────────────── │
│ 2 AZ failures │ Cluster down │ Manual: wait for AWS │
│ │ │ │
│ ──────────────────────│────────────────────│────────────────────────── │
│ NLB failure │ External access │ Rare (AWS managed) │
│ │ down │ Use direct CP IP │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE FAILURE RECOVERY │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ BEFORE FAILURE (healthy cluster): │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │◄──►│ CP-2 │◄──►│ CP-3 │ │
│ │ LEADER │ │ FOLLOWER │ │ FOLLOWER │ │
│ │ etcd │ │ etcd │ │ etcd │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ T+0:00 - CP-1 FAILS │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │◄──►│ CP-3 │ │
│ │ ✗ ✗ │ │ FOLLOWER │ │ FOLLOWER │ │
│ │ DOWN │ │ etcd │ │ etcd │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ T+0:05 - NLB detects failure (health check) │
│ T+0:10 - etcd election timeout triggers │
│ T+0:15 - New leader elected │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CP-1 │ │ CP-2 │◄──►│ CP-3 │ │
│ │ ✗ ✗ │ │ LEADER │ │ FOLLOWER │ │
│ │ DOWN │ │ etcd │ │ etcd │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ NLB routes to CP-2 & CP-3 only │
│ Cluster fully operational │
│ │
└─────────────────────────────────────────────────────────────────────────┘
| Use Case | Instance Type | vCPU | Memory |
|---|---|---|---|
| Development | t3.medium | 2 | 4 GiB |
| Small Production | t3.large | 2 | 8 GiB |
| Medium Production | m5.xlarge | 4 | 16 GiB |
| Large Production | m5.2xlarge | 8 | 32 GiB |
To add more workers, update terraform.tfvars:
worker_count = 5 # Increase from 3Then apply:
terraform applyRecommended: Keep 3 or 5 control plane nodes
To scale to 5 CP nodes:
control_plane_count = 5Note: You'll also need 5 availability zones or allow AZ imbalance.
| Feature | RKE2 | K3s | EKS | kubeadm |
|---|---|---|---|---|
| etcd | Embedded | SQLite/Embedded | Managed | External/Embedded |
| Runtime | containerd | containerd | containerd | configurable |
| CIS Compliance | Default | Requires config | Partial | Manual |
| FIPS Support | Yes | No | No | No |
| Cloud Integration | Manual | Manual | Native | Manual |
| Complexity | Low | Very Low | Medium | High |
| Air-Gap | Easy | Easy | Hard | Medium |
Back to Main README | Previous: Architecture | Next: Deployment Guide