Generate configuration files based on DNS-SD records
When you manage clusters of servers, you often have to update configuration files when adding or removing machines, be it on the loadbalancer that forward your trafic to the backends, or on the deployment system that push your code on the servers. Having to change manually several configuration files implies that you can make errors or even forget to update one. To simplify this task, dnssdconf allows to use the DNS Service Discovery protocol to fetch the list of backend servers and generate configuration files based on templates. Thus, adding or removing a server in a cluster is as simple as changing one DNS record.
Adding dnssdconf in a crontab run every minute will ensure that your configuration files are always up to date.
dnssdconf [--logfile LOGFILE] [--loglevel {debug,info,warning,error,critical,fatal}] [--logconfig LOGCONFIG] [--config CONFIG] [--templatesdir TEMPLATESDIR] [--diff]
Arguments :
- --logfile : Log file
- --loglevel : Log level, choose between the following values :debug,info,warning,error,critical,fatal
- --logconfig : Logging configuration file (overrides --loglevel and --logfile). File format is described at https://docs.python.org/2/library/logging.config.html#logging-config-fileformat.
- --config : Config file, describing which files to generate and what DNS-SD services to fetch (default: /etc/dnssdconf/dnssdconf).
- --templatesdir : Default templates directory (default: /etc/dnssdconf/template)
- --diff : Show diff when files are updated (loglevel info)
- --backup [suffix] : Backup files with this suffix
- --noop : Do not write files, only pretend to
- --allowemptyconf : Do not emit a warning if dnssdconf config file is emty
The config file is a YAML file of which each main element is the description of a file to generate. Each file can have several properties :
- service_instances : map an identifier to a DNS name for each service instance to be fetched.
- template : path to the template. If it does not begin with a /, it is considered to be relative to the path specified with the --templatesdir CLI option.
- output : path to the output file. It it does not begin with a /, it is considered to be relative to the current working directory.
- exec : command to be executed after the file is updated (only if the content has changed). The command is passed to a shell.
- encoding : set the encoding of the template and output file. Default is ascii. Supported values are described at https://docs.python.org/2/library/codecs.html#standard-encodings.
- resolve_names : resolve names pointed by SRV records. If the value is True or 'any', resolve A and AAAA records, fail if both are not defined; if the value is 'both', resolve A and AAAA records, fail if any is not defined; if the value is 'ipv4' or 'ipv6', resolve respectively A and AAAA record and fail if it is not defined. Defaults to False.
haproxy:
output: /etc/haproxy/haproxy.cfg
template: haproxy.conf.mako
exec: service haproxy reload
service_instances:
web: web._http._tcp.mycompany.example
bdd: main._mysql._tcp.mycompany.example
nginx:
output: /etc/nginx/nginx.conf
[...]
The templates are rendered using the Mako template engine (http://docs.makotemplates.org/). Services instances are available as variables using the identifier set in the configuration file.
A service instance has two properties :
- servers : a dictionary representing the servers, each having the following properties :
- hostname
- port
- priority
- weight
- ipv4 (defined if resolve_names is True, 'any', 'both' or 'ipv4' and an A record exists for the server)
- ipv6 (defined if resolve_names is True, 'any', 'both' or 'ipv6' and an AAAA record exists for the server)
- config : a dictionary representing the instance properties, as defined in the TXT record associated with the service instance.
Example (for a haproxy configuration file, as the one in the example above) :
listen web
bind :80
% for server in web.servers:
server ${server.hostname.split('.')[0]} ${server.hostname}:${server.port} check inter 2000 fall 5 rise 2 maxconn ${server.weight * 2} weight ${server.weight}
% endfor
In this example, we used python expressions to extract a short host name from the FQDN, and to compute a connection limit from the weight.
The DNS-SD records must respect the format defined in the RFC 6763 (https://tools.ietf.org/html/rfc6763).
Each service instance must have at least :
- One TXT record, which may be empty.
- At least one SRV record
Example :
web._http._tcp.mycompany.example. IN TXT ""
web._http._tcp.mycompany.example. IN SRV 0 12 80 server1.mycompany.example.
web._http._tcp.mycompany.example. IN SRV 0 27 80 server2.mycompany.example.
web._http._tcp.mycompany.example. IN SRV 0 17 80 server3.mycompany.example.
dnssdconf depends on the following python libraries :
- dnspython (http://www.dnspython.org/)
- mako (http://www.makotemplates.org/)
- pyyaml (http://pyyaml.org/)
Under debian/ubuntu, they can be installed using the following packages : python-dnspython, python-mako and python-yaml.