Load balancing DNS with keepalived

Keepalived currently only supports load balancing TCP traffic.  However DNS runs over UDP, so how can you load balance DNS traffic using keepalived?  I had mentioned in my Load Balancing vs Failover post that I’d give you a trick to accomplish this, so here it is…

Keepalived is essentially a wrapper around Linux Virtual Server (LVS) which is built into most modern kernels.  LVS supports load balancing of both TCP and UDP traffic.  (keepalived also includes VRRP and Healthchecks, but that’s off-topic)  Lucky for us, DNS servers happen to listen for TCP connections in addition to UDP.  They do this in order to handle queries that are larger than the old RFC limit of 512-bytes.

The simple trick I use in order to load balance DNS traffic is to configure keepalived to load balance TCP port 53 just like you would load balance any other TCP port, such as http or smtp.  Then use keepalived’s notify_up / notify_down script calling feature to manually configure LVS for load balancing the corresponding UDP port.

For example:

virtual_server 123.456.78.9 53 {
  delay_loop 6
  lb_algo wrr
  lb_kind DR
  persistence_timeout 0
  protocol TCP
  ha_suspend
  real_server 10.0.0.1 53 {
    weight 1
    notify_down "/sbin/ipvsadm -d -u 123.456.78.9:53 -r 10.0.0.1:53"
    notify_up   "/sbin/ipvsadm -a -u 123.456.78.9:53 -r 10.0.0.1:53 -g -w 1"
    TCP_CHECK {
      connect_timeout 6
    }
  }
  real_server 10.0.0.2 53 {
    weight 1
    notify_down "/sbin/ipvsadm -d -u 123.456.78.9:53 -r 10.0.0.2:53"
    notify_up   "/sbin/ipvsadm -a -u 123.456.78.9:53 -r 10.0.0.2:53 -g -w 1"
    TCP_CHECK {
      connect_timeout 6
    }
  }
  real_server 10.0.0.3 53 {
    weight 1
    notify_down "/sbin/ipvsadm -d -u 123.456.78.9:53 -r 10.0.0.3:53"
    notify_up   "/sbin/ipvsadm -a -u 123.456.78.9:53 -r 10.0.0.3:53 -g -w 1"
    TCP_CHECK {
      connect_timeout 6
    }
  }
}

Using the above configuration, whenever healthchecks finds that TCP port 53 is not responding on one of the real servers, it removes the TCP port from the pool of available servers, and then notify_down immediately calls ipvsadm to take the corresponding UDP port also out of it’s pool of available servers.  So keepalived (LVS) stops routing traffic for both TCP and UDP to the dead server whenever the TCP port fails.

Likewise, when healthchecks finds that TCP port 53 is back up, notify_up calls ipvsadm to add it back into the pool of available servers.

There is one additional thing that you must do in order to make this all work.  Keepalived does not call notify_up at startup, so you must also add the ipvsadm command to the keepalived init script, so that the UDP ports get brought up when keepalived launches.

Leave a Reply

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