Ben Summers’ blog

Multicast DNS, dev VMs, and multi-tenant applications

Using an automatically installed virtual machine for development is a wonderful way to work, as it’s quick to set up and closely matches your production environment.

But there’s one slight problem: What URL do you type into the web browser to see your application? The popular Vagrant solves this by forwarding a port on your host OS to the virtual machine, so you can just type in http://localhost:8080 and log into your app.

But there are two problems with this.

Firstly, you’re not using the same port as you use in production. Even little things like this make a difference, you really do need to make development as close as possible to production.

And secondly, what if you’re developing a multi-tenant application where each customer has their own hostname, and to test, you need to be able to use multiple hostnames?

Vagrant and multiple hostnames

The Vagrant solution does allow you to edit /etc/hosts and add some more aliases to localhost, but this is not particularly elegant, and you still use the wrong port. Or you could use DNS, but then you have to run a local DNS server, and make sure the entries match the IP addresses dynamically allocated to your VM. (There are Vagrant plugins which can do this.)

Using /etc/hosts or DNS is hard to automate, and is not a good solution to the problem. But there is a better way.

Multicast DNS

Apple needed to use readable machine names on a local network without a nameserver, so implemented multicast DNS. mDNS behaves much the same was as DNS, except there’s no central server, and names in the .local domain are discovered by sending and receiving network broadcasts on the local network subnet.

The networking in VMware and VirtualBox creates virtual network subnets, accessible only to your computer — perfect for using mDNS! And you can do all the configuration inside the virtual machine, fitting neatly into your existing configuration management setup.

Mac OS X comes with mDNS built in and enabled by default, so there’s nothing to install. Bonjour is available for Windows, but if you’re using VMware on Mac OS X to run Windows VMs for testing, it’ll just resolve the mDNS names on the Windows VMs without you having to do anything.

It really does “just work”, as designed.

How I’m using mDNS

I’m currently revisiting our development setup. A lot of things have changed since I last did this in about 2009.

I’m now using OmniOS, an open derivative of Solaris. (Watch this usenix talk for why it’s so appealling.) This comes with Apple’s mDNSResponder built in, just requiring a svcadm enable dns/multicast to run and configure. It automatically broadcasts hostname.local (where hostname is the hostname of your VM), and the .local domain is usable by any process running on that VM.

Without any further work, you can log into the VM with ssh hostname.local and access the application at https://hostname.local.

For a little less typing, I’ve added some short names in my ~/.ssh/config file, like this:

    Host v0
      HostName dev-vm0.local

I can now just type ssh v0 to log into that VM.

Registering multiple hostnames

Simply enabling mDNS is enough to allow you to use a single name with no configuration on your host OS. But what about all the other hostnames we want to create to test the multi-tenancy?

mDNSResponder doesn’t support wildcards. But as part of the package, there’s a client library which allows you to register any hostnames you want. There’s even a Java library which wraps it, which is perfect for JRuby users like us.

Here’s some code:


require ‘/usr/share/lib/java/dnssd.jar’

module MulticastDNSRegistration

  THIS_ADDRESS = java.net.InetAddress.getLocalHost().getAddress()

  class RegistrarListener
    def recordRegistered(record, flags)
    end
    def operationFailed(service, errorCode)
      puts “WARNING: mDNS operation failed”
    end
  end

  DNSSD = com.apple.dnssd.DNSSD
  @@registrar = DNSSD.createRecordRegistrar(RegistrarListener.new)

  @@registrar.registerRecord(
     DNSSD::UNIQUE,
     DNSSD::ALL_INTERFACES,
     “an.example.host.local”,
     1, # A
     1, # IN
     THIS_ADDRESS,
     3600 # TTL
   )
end

This registers an.example.host.local in mDNS with the IP address of the VM, and of course, you can use any address which ends in .local and register as many as you like.

In our application, I just added a small bit of code which registers all the DNS names of the configured tenants, and magically, they’re usable.

It takes longer to write about it than to use it

I think I’ve spent longer writing about mDNS than I did to get it set up. It’s a nice elegant solution to an annoying problem.

There are mDNS packages available for all major OSes, and even a pure Java implementation. Try it yourself!

 

UPDATE — fixing slow ssh logins

If there’s a delay of a few seconds before ssh logs you in, check that IPv6 is enabled on the VM. It just needs a link local IPv6 address, which you can configure on an OpenSolaris derived OS with ipadm create-addr -T addrconf e1000g0/v6

Because Mac OS X prefers to use IPv6, it will attempt an IPv6 mDNS name lookup first. If the VM can’t respond because it hasn’t got working IPv6, there will be a 5 second timeout, then it’ll try IPv4 and succeed.

If you can’t configure IPv6, add the -4 switch to the ssh command, or AddressFamily inet in your ~/.ssh/config file.

Many thanks to James for identifying the problem.

 

COMMENTS

blog comments powered by Disqus

 

Hello, I’m Ben.

I’m the Technical Director of Haplo Services, an open source platform for information management.

 

About this blog

 

Twitter: @bensummers

 

Subscribe

Jobs at Haplo
Come and work with me!