sc68cal / python-dns-lb

Python asyncio based DNS loadbalancer
Do What The F*ck You Want To Public License
0 stars 0 forks source link

Python DNS loadbalancer daemon

This is a python based DNS service, that takes DNS queries for FQDNs, and returns DNS responses, based on what members in a pool are available.

Purpose

This was written to try out a couple of different technologies that I have been meaning to experiment with for a couple years now. One of the major features of recent versions of Python 3 that has been the support for asynchronous programming within the language itself (as opposed to libraries like twisted or gevent), and the ecosystem of libraries (like aiohttp) that take advantage of the new primitives that recent versions of Python 3 now provide.

I also wanted to try out rqlite, which has been on my radar for a number of years. I always wanted to try it, but never had an opportunity for greenfield development where I could build from the beginning with rqlite in mind.

Tech stack

It uses asyncio, the create_datagram_endpoint API, rqlite, dnspython, aiohttp, and uvloop (where supported).

rqlite database

rqlite development deployment

For development purposes, a single rqlite node can be run via podman

podman run -p4001:4001 docker.io/rqlite/rqlite

rqlite production cluster

For a real deployment, you can use the 3 node k8s deployment.

If you are using k3s you will need to create a PVC that uses the local-path storage

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rqlite-file
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 6Gi

Then modify the rqlite 3 node deployment the YAML slightly.

49c49
<       storageClassName: "standard"
---
>       storageClassName: "local-path"

This project also relies on foreign key constraints, so the kubernetes YAML for deploying rqlite also needs have the fk setting added.

21c21
<         args: ["-disco-mode=dns","-disco-config={\"name\":\"rqlite-svc-internal\"}","-bootstrap-expect","3", "-join-interval=1s", "-join-attempts=120"]
---
>         args: ["-disco-mode=dns","-disco-config={\"name\":\"rqlite-svc-internal\"}","-bootstrap-expect","3", "-join-interval=1s", "-join-attempts=120", "-fk=true"]

https://github.com/rqlite/rqlite

Schema and initial data

curl http://localhost:4001/db/execute -H 'Content-Type: application/json' -d @schema.json
curl 127.0.0.1:4001/db/execute -H "Content-Type: application/json" -d @populate_data.json
curl -G '127.0.0.1:4001/db/query?associative' --data-urlencode 'q=SELECT host FROM dns_zone_member JOIN dns_zone ON dns_zone_member.dns_zone = dns_zone.id WHERE dns_zone.name == "google.com."' | jq

Functional tests

You can test the server with the dig utility, which comes as part of bind-utils on most distributions. FreeBSD has it as part of the dns/bind-tools port.

dig @127.0.0.1 -p 9999 google.com