Skip to content

VIP Failover with Keepalived

Problem

With only 3 baremetal nodes, any single node failure must not take down the cluster. Two critical services need to survive a node death: the Kubernetes API server (used by kubectl, controllers, kubelet) and the VM external network gateway/DNS (used by all OpenStack VMs). Without failover, losing the node holding these IPs means losing the entire cluster or all VM connectivity.

Solution

Keepalived runs on all three nodes and manages two VRRP (Virtual Router Redundancy Protocol) instances. Each instance floats a virtual IP between nodes — if the MASTER dies, a BACKUP takes over within seconds.

VRRP Instance 1: Kubernetes API Server VIP

PropertyValue
VIP178.63.143.219/32
Interfaceeno1.4000 (VLAN 4000)
Virtual Router ID51
ProtocolVRRPv2, unicast between nodes

This is the kube-vip address. The kubernetes API server listens here, and all kubelets/controllers connect to it. If lucy (priority 100) dies, makise (priority 99) takes over the VIP. The kubelet on each node uses this address in its kubeconfig, so it reconnects to the new MASTER automatically.

VRRP Instance 2: External Network Gateway / DNS

PropertyValue
VIP172.16.0.254/16
Interfacebr-ex
Virtual Router ID52
ProtocolVRRPv2, unicast between nodes

This is the gateway for all OpenStack VMs on the external network. Neutron configures VMs to use 172.16.0.254 as their default route and DNS server. If the node holding this VIP fails, another node takes over and VM traffic routes through the new MASTER. dnsmasq on br-ex uses bind-dynamic to follow the VIP without restart.

Node Priorities

NodePriorityRole
lucy100MASTER (normally holds both VIPs)
makise99BACKUP
quinn98BACKUP

All nodes start in BACKUP state. The highest priority wins the election. Unicast peering (no multicast needed — Hetzner vSwitch doesn't support it):

nix
keepalived = {
  vrrpInstances."VI_1" = {  # API server VIP
    interface = "eno1.4000";
    state = "BACKUP";
    virtualRouterId = 51;
    priority = 100;          # per-node
    unicastSrcIp = "10.0.0.2";
    unicastPeers = [ "10.0.0.3" "10.0.0.4" ];
    virtualIps = [{ addr = "178.63.143.219/32"; dev = "eno1"; }];
  };
  vrrpInstances."VI_2" = {  # External gateway VIP
    interface = "eno1.4000";
    state = "BACKUP";
    virtualRouterId = 52;
    priority = 100;
    unicastSrcIp = "10.0.0.2";
    unicastPeers = [ "10.0.0.3" "10.0.0.4" ];
    virtualIps = [{ addr = "172.16.0.254/16"; dev = "br-ex"; }];
  };
};

Failure Scenarios

Node lostAPI VIP moves toGateway VIP moves toImpact
lucy (MASTER)makisemakiseBrief API + VM connectivity blip (~1-3s)
makiselucy (reclaims)lucy (reclaims)Same
quinnno change (lucy keeps)no change (lucy keeps)None — lucy was already MASTER

With 3 nodes, the cluster tolerates 1 node failure. Losing 2 nodes means losing quorum (etcd needs 2/3 members) and both VIPs.

Open source infrastructure documentation