DNS for Service Discovery is a feature that can update an HAProxy configuration during runtime, such as IP addresses and ports, without making explicit changes to configuration files.

You can configure HAProxy to inspect DNS SRV records so it can set ports and IP addresses. An SRV record returns a hostname and port number. The hostname, in turn, maps to an A record that contains the IP address.

[DNS SRV records]

About DNS SRV records

DNS SRV records are resources used to identify computers that host specific services. They are contained in the ANSWER section of DNS responses and have the following structure:

_service._proto.name. TTL class SRV priority weight port target

where:

_service

Standard network service name (taken from /etc/services) or a port number

_proto

Standard protocol name ("tcp" or "udp")

name

Name of the service, i.e. the name used in the query

TTL

Validity period for the response (HAProxy ignores this field because it maintains its own expiry data defined in the configuration)

class

DNS class ("IN")

SRV

DNS record type ("SRV")

priority

Priority of the target host. Lower value = higher preference (HAProxy ignores this field but may use it later to indicate active / backup state)

weight

Relative weight in case of records with the same priority. Higher number = higher preference

port

Port where the service is configured

target

Hostname of the machine providing the service, ending in a dot

Note

Usually, the DNS server also returns the resolution for the targets mentioned, and it provides that information in the ADDITIONAL SECTION.

Configure DNS A and SRV records

Update your DNS nameserver to resolve a service name, such as myservice.example.local, to one or more hostnames and ports via SRV records. Those hostnames should resolve to IP addresses using A records.

  1. Add DNS A records that resolve multiple hostnames, such as host1, host2 and host3, to different IP addresses.

  2. Add the same number of SRV records that resolve a service name, such as _myservice._tcp.example.local, to the hostnames you defined and the port on the host where the service listens.

Query the nameserver

  • Query the nameserver directly with the dig tool to ensure that it returns the correct records, as follows:

    $ dig @192.168.50.30 -p 53 SRV _myservice._tcp.example.local
    ;; QUESTION SECTION:
    ;_myservice._tcp.example.local. IN      SRV
    
    ;; ANSWER SECTION:
    _myservice._tcp.example.local. 0 IN     SRV     0 0 8080 host1.
    _myservice._tcp.example.local. 0 IN     SRV     0 0 8081 host2.
    _myservice._tcp.example.local. 0 IN     SRV     0 0 8082 host3.
    
    ;; ADDITIONAL SECTION:
    host1.                  0       IN      A       192.168.50.31
    host2.                  0       IN      A       192.168.50.32
    host3.                  0       IN      A       192.168.50.33

Configure a resolvers section

You add a resolvers section in your HAProxy configuration file to set the DNS nameservers for HAProxy to watch for changes.

  1. Edit the HAProxy configuration file hapee-lb.cfg

  2. Add a resolvers section.

  3. Add one or more nameserver lines to specify the IP addresses and ports of your DNS nameservers.

  4. Set the accepted_payload_size to 8192 to allow larger DNS payloads, which is required to receive more server IPs within a single DNS result.

resolvers mydns
    nameserver dns1 192.168.50.30:53
    accepted_payload_size 8192

Add a server-template

  • Use a server-template in a backend to set the template for the server lines when HAProxy queries your DNS servers, as follows:

    server-template <prefix> <num | range> <fqdn>[:<port>] [params*]

    where:

    prefix

    A string used as a prefix for each server name. (e.g. web)

    num

    The number of servers you want to generate automatically. This number will increment and append to the prefix of each server when forming its name.

    range

    The number of servers you want to generate automatically, specified as starting and ending numbers to append to the prefix of each server when forming its name.

    fqdn

    The fully-qualified domain name used to query the nameservers.

    port

    A hardcoded port number.

    params*

    Additional server parameters.

    backend webservers
        balance roundrobin
        server-template web 5 _myservice._tcp.example.local resolvers mydns check init-addr none

    In this example, the server-template directive:

    • Adds the specified number of servers (5) to the backend.

    • Appends "web" as a prefix to their names.

    • Queries the service name _myservice._tcp.example.local.

    • Have the SRV records fill in the ports.

    • Specifies the resolvers section mydns

    • The init-addr none argument means that HAProxy can initialize without having to resolve the IP addresses at startup. It can resolve them during runtime.

    This is equivalent to adding a backend to HAProxy that looks like this:

    backend webservers
        balance roundrobin
        server web1 192.168.50.31:8080 check
        server web2 192.168.50.32:8081 check
        server web3 192.168.50.33:8082 check
        server web4 check disabled
        server web5 check disabled

When you add more records to your nameserver, they automatically go into the backend to fill the web4 and web5 slots.

Inspect servers with the Runtime API

When you run the command show servers state servers from HAProxy's Runtime API, it shows both the IP address and port for each server:

echo "show servers state servers" | nc localhost 9000
# be_id  be_name  srv_id  srv_name  srv_addr       srv_fqdn  srv_port  srvrecord
3        servers  1       web1      192.168.50.33  host3     8082      _myservice._tcp.example.local
3        servers  2       web2      192.168.50.32  host2     8081      _myservice._tcp.example.local
3        servers  3       web3      192.168.50.31  host1     8080      _myservice._tcp.example.local
3        servers  4       web4      -              -         0         _myservice._tcp.example.local
3        servers  5       web5      -              -         0         _myservice._tcp.example.local