SoCruel.NU

The domain that loves BSD

Home About Me Archive Contact

How to implement gdnsd on FreeBSD

I was looking for a solution to increase the availability of my public web sites, see my previous post for the introduction to this.

gdnsd is an authoritative-only name server. The initial ‘g’ stands for geographic, as gdnsd offers a plugin system for geographic (or other sorts of) balancing, redirection, and service-state-concious failover. This post explains how gdnsd is implemented at SoCruel.NU to achieve the availability goals, which are:

  • have a failover possibility in place for the public SoCruel.NU web sites in case the web sites become unavailable on the primary location in cases of i.e. internet connection down or server hardware failure
  • a failover must take place in a couple of minutes (2 to 3 minutes - so this amount of down time is accepted)

To make these goals happen the simplefo gdnsd plugin is used. This is one of the more simple plugins of gdnsd. More complex setups with geographic balacing, redirection and service-state-concious failover scenario’s are possible!

Requirements

The following requirements have to be in place to be able to implement what is described in this post:

  • have a public DNS zone running on the internet with full administrative control - for this blog post this is assumed to be mydomain.tld
  • the name servers of the public DNS zone mydomain.tld are: ns1.mydomain.tld, ns2.mydomain.tld and ns3.mydomain.tld
  • have 2 up to date FreeBSD systems running with a direct connection on the internet with a public IP address (assumed 2.4.6.8 and 3.5.7.9, respectively, for this post) for running gdnsd
  • the FQDN of these 2 gdnsd servers are: gdns1.mydomain.tld and gdns2.mydomain.tld
  • have 2 web servers running with, assumed for this blog post, public IP addresses 11.12.13.1 and 22.33.44.5. These web servers run the public web site webapp.mydomain.tld.

Please replace all assumed names and IP addresses with your own, if you want to implement this in your own environment.

Install gdnsd (version 2)

gdnsd has currently 2 major versions: version 2.x and version 3.x. Both are available in the FreeBSD ports and packages collection. Use the following command to install the gdnsd package on the gdns1.mydomain.tld and gdns2.mydomain.tld systems:

$ sudo pkg install gdnsd2

The installation package has created the directory /usr/local/etc/gdnsd, which is where gdnsd stores its configuration. We create two new directories here to store our plugin and service type configurations:

$ sudo mkdir /usr/local/gdnsd/plugins.d
$ sudo mkdir /usr/local/gdnsd/service-types.d

Before we start the configuration of gdnsd we create the admin_state file (otherwise gdnsd complains at startup):

$ sudo touch /var/db/gdnsd/admin_state

For more information about the admin_state see the gdnsd wiki.

Configure gdnsd (version 2)

The configuration of this gdnsd implementation consist of 4 parts:

Each of these is discussed below. All 4 must be performed on both the gdns1.mydomain.tld and gdns2.mydomain.tld systems. The configuration of gdns1.mydomain.tld is shown.

gdnsd main config file

The main gdnsd configuration file on FreeBSD is /usr/local/etc/gdnsd/config. This configuration file is clearly documented on the gdnsd GitHub wiki. The main configuration file used for this implementaion looks like:

options => {
   listen => [ 2.4.6.8, 127.0.0.1 ]
   dns_port => 53
   http_listen => [ 127.0.0.1 ]
   http_port => 3506
   chaos_response => "Just a nameserver."
}

service_types => {
   $include{service-types.d}
}

plugins => {
   simplefo => {
      $include{plugins.d}
   }
}

The following is configured here:

  • the gdnsd daemon listens on the IP addresses 2.4.6.8 and localhost on port 53
  • the gdnsd web interface listens on localhost port 3506
  • the DNS chaos record is set to “Just a nameserver”
  • the gdnsd service type configuration files in the directory /usr/local/etc/gdnsd/service-types.d are loaded
  • the simplefo plugin is used
  • the gdnsd plugin configuration files in the directory /usr/local/etc/gdnsd/plugins.d are loaded

gdnsd service type configuration

With a gdnsd service type you define how your end points, in this case the web servers, are monitored by gdnsd. The status is shown on the gdnsd web interface (see the main config file).

The name of the service type for our webapp.mydomain.tld URL is http_monitor_webapp and its configuration is:

http_monitor_webapp => {
   plugin => http_status
   ok_codes => [200]
   vhost => webapp.mydomain.tld
   url_path => /monitor.html
   interval = 15
   timeout = 5
   up_thresh = 5
   ok_thresh = 3
   down_thresh = 6
}

This is copied and pasted in a file called webapp.conf and put it in the directory /usr/local/etc/gdnsd/service-types.d. This configuration states:

  • that we check our web servers using the HTTP protocol
  • only the HTTP code 200 is valid for our checks
  • we check for the virtual host URL webapp.mydomain.tld
  • we check for the file monitor.html
  • we use an interval (“number of seconds between successive monitoring requests for a given resource”) of 15 seconds between checks
  • the timeout (“Maximum time the monitoring code will wait for a successful response before giving up and considering the request to be a failure.”) is 5 seconds
  • the up threshold (“Number of monitoring requests which must succeed in a row without any failures to transition a given resource from the DOWN state to the UP state.”) is 5
  • the down threshold (“Failure counter to change to DOWN state. If the failure counter reaches down_thresh the resource is transitioned to the DOWN state.”) is 6
  • the ok threshold (“if ok_thresh successes occur in a row with no failures between them, the failure counter is reset back to zero.”) is set to 3

For more detailed information about the gdnsd service types see the gdnsd GitHub wiki.

gdnsd plugin configurations

With a gdnsd plugin you define the failover type of the service you load balance. gdnsd ships with 8 plugins. Here the simple primary -> secondary address failover is chosen using the simplefo plugin. Its configuration is:

webapp => {
   service_types => http_monitor_webapp
   primary => 11.12.13.1
   secondary => 22.33.44.5
}

This is copied and pasted in a file called webapp.conf and put it in the directory /usr/local/etc/gdnsd/plugins.d. This configuration states:

  • that the service type httpmonitorwebapp is used
  • the primary web server is the server with IP address 11.12.13.1
  • the secondary (failover) web server is the server with IP address 22.33.44.5

the zone file

The gdnsd zone files reside in the directory /usr/local/etc/gdnsd/zones. A file named webapp.mydomain.tld is created in this directory and the contents are as below:

$TTL 3600
$ORIGIN webapp.mydomain.tld.

@ IN SOA gdns1.mydomain.tld. hostmaster.mydomain.tld. (
           2020042201  ; serial number
           43200       ; Refresh
           3600        ; Retry
           864000      ; Expire
           7200        ; Min TTL
           )

    60           IN     NS   gdns1.mydomain.tld.
    60           IN     NS   gdns2.mydomain.tld.

@   15           DYNA   simplefo!webapp

A gdnsd zone file looks very much like a zone file of any other name server software. Except for the last line:

@   15           DYNA   simplefo!webapp

This is a DYNA record, which is for dynamically-determined address records (both A and AAAA) via plugin code. This line states the following:

  • this is a blank record (the ‘@’ character), which points the domain name, here webapp.mydomain.tld, to a server (here one of the web servers)
  • the TTL value is 15 seconds
  • the simplefo plugin is used for this record
  • the resource name of the plugin (here webapp)

Configure the mydomain.tld zone

Once you have done the above, one more item needs to be configured. And that is configure the name servers for the webapp.mydomain.tld domain in the mydomain.tld. zone (which runs on the ns1.mydomain.tld., ns2.mydomain.tld. and ns3.mydomain.tld. name servers). This is done in the last 2 lines of the example zone file shown below:

$ORIGIN mydomain.tld. ; default zone domain
$TTL 3600             ; default time to live

@ IN SOA ns1.mydomain.tld. hostmaster.mydomain.tld. (
           2020042201  ; serial number
           43200       ; Refresh
           3600        ; Retry
           864000      ; Expire
           86400       ; Min TTL
           )

           IN     NS      ns1.mydomain.tld.
           IN     NS      ns2.mydomain.tld.
           IN     NS      ns3.mydomain.tld.

webapp     IN     NS      gdns1.mydomain.tld.
           IN     NS      gdns2.mydomain.tld.

This example zone file can be used for name servers like BIND or NSD.

A failover explained

So what happens when a failover occurs for webapp.mydomain.tld from the primary web server to the secondary web server in case the primary web server becomes unavailable?

The name servers for the webapp.mydomain.tld zone have a time to live value configured of 60 seconds. gdnsd ‘knows’ that the primary web server is down after 6 x 15 seconds is 90 seconds. So after the next (the 7th) interval gdnsd answers queries with the IP address of the secondary (failover) web server. But the clients have to wait a little longer because of the time to live value of 60 seconds, as they will get an update after 120 seconds.

As soon as the primary web server becomes available again gdnsd takes up_thresh times interval seconds to change the query answers back using the primary web server IP address. The clients still depend on the time to live value of 60 seconds. But that is less important in this case, as now no downtime is part of this!

Resources

Some (other) resources about this subject:

Updated: April 28, 2020