Skip to content

Instantly share code, notes, and snippets.

@barnybug
Last active November 23, 2019 23:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save barnybug/6375322 to your computer and use it in GitHub Desktop.
Save barnybug/6375322 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# Script to run dynamic dns for docker containers.
# DNS is served by dnsmasq running on the docker0 gateway ip, and dynamically
# updated at containers come and go.
#
# To use from docker, just provide the --dns option:
# docker run --dns <gateway> ...
# The gateway ip you need will be printed when this script is run.
import os
import logging
import json
import re
import select
import subprocess
import tempfile
def path(name):
for entry in os.getenv('PATH').split(':'):
fname = os.path.join(entry, name)
if os.path.exists(fname):
return fname
raise ValueError("Couldn't find %s on PATH" % (name))
# Constants
DOCKERIFACE = 'docker0'
DNSMASQ = path('dnsmasq')
DOCKER = path('docker')
class Monitor(object):
def __init__(self):
(fd, self.hosts) = tempfile.mkstemp(suffix='.hosts')
self.gateway = self.get_iface_addr()
logging.info("Listening on %s" % self.gateway)
logging.info("You should run docker as: docker run --dns %s ..." % self.gateway)
self.events = None
os.chmod(self.hosts, 0644)
def get_iface_addr(self):
output = subprocess.check_output('ip addr show dev %s' % DOCKERIFACE, shell=True)
m = re.search(r'inet (\d+\.\d+\.\d+\.\d+)', output)
if not m:
raise ValueError("Unable to get docker ip address")
return m.group(1)
def get_docker_ids(self):
ids = []
lines = subprocess.check_output('docker ps', shell=True).split('\n')
for line in lines[1:]:
if not line:
continue
id, image, command, created, status, ports = re.split('\s{2,}', line)
ids.append(id)
return ids
def build_hosts(self):
logging.info('Updating hosts file')
with file(self.hosts, 'w') as fout:
ids = self.get_docker_ids()
if not ids:
return None
data = subprocess.check_output('docker inspect %s' % ' '.join(ids), shell=True)
i = json.loads(data)
for container in i:
hostname = container['Config']['Hostname']
ipaddress = container['NetworkSettings']['IPAddress']
print >>fout, '%s %s' % (ipaddress, hostname)
logging.info('Found: %s %s' % (ipaddress, hostname))
logging.info('Generated hosts file with %s entries: %s' % (len(i), self.hosts))
def start_dnsmasq(self):
args = [DNSMASQ, '--addn-hosts', self.hosts, '--bind-interfaces', '--listen-address', self.gateway, '--keep-in-foreground']
self.dnsmasq = subprocess.Popen(args)
logging.info("Started dnsmasq pid: %d" % self.dnsmasq.pid)
def stop_dnsmasq(self):
logging.info("Terminating dnsmasq pid: %d" % self.dnsmasq.pid)
self.dnsmasq.terminate()
def restart_dnsmasq(self):
self.stop_dnsmasq()
self.start_dnsmasq()
def tail_events(self):
if not self.events:
self.events = subprocess.Popen([DOCKER, 'events'], stdout=subprocess.PIPE)
logging.info('Tailing docker events...')
f = self.events.stdout
# wait on data
while select.select([f.fileno()], [], []):
data = f.readline()
event = data.strip().split()[-1]
logging.info('Received docker event: %s' % event)
if event in ('start', 'die'):
self.build_hosts()
self.restart_dnsmasq()
def run(self):
self.build_hosts()
self.start_dnsmasq()
self.tail_events()
if __name__=='__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)-15s %(message)s')
monitor = Monitor()
monitor.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment