Addressing each of these VMs with a real hostname was proving to be difficult. We couldn’t just use the IP addresses of the machines because they’re unreasonably hard to remember, and other problems like browser cookies don’t work properly.
In the past, I’ve managed this by editing my local
/etc/hosts file (or the Windows equivalent, whatever that’s called now). Turns out this wasn’t ideal. If my hosts don’t sync up with my colleagues’ hosts, stuff (usually cookies) can go wrong, for example. Plus, I strongly believe in setting up an environment that can be managed remotely (when possible) so less-technical members of our team don’t find themselves toiling under the burden of managing an obscurely-formatted text file deep within the parts of their operating systems that they — in all fairness — shouldn’t touch. Oh, and you also can’t do wildcards there.
As I mentioned in a previous post, we have the great fortune of having all of our VM users conveniently on one operating system platform (Mac OS X), so this post will also focus there, but a similar strategy to this one could be used on Windows or Linux, without the shiny
resolver bits — you’d just have to run all of your host’s DNS traffic through a VM-managed name resolver; and these other operating systems might have something similar to
resolver that I simply haven’t been enlightened to, and surely someone will point out my error on Twitter or email (please).
The short version (which I just hinted at) is that we run a DNS server on our
gateway VM (all of our users have one of these), and we instruct the workstation’s operating system to resolve certain TLDs via this VM’s IP address.
We set up the VM side of this with configuration management, in our Salt sates. Our specific implementation is a little too hacky to share (we have a custom Python script that loads hostname configuration from disk, running within systemd), but I’ve recently been tinkering with Dnsmasq, and we might roll that out in the non-distant future.
Let’s say you want to manage the
.sean TLD. Let’s additionally say that you have an app called
saxophone (on a VM at
192.168.222.16) and another called
192.168.222.17), and you’d like to address these via URLs like
https://trombone.sean/, respectively. Let’s also say that you might want to make sure that
http://www.trombone.sean/ redirects to
trombone.sean (without the
www). Finally, let’s say that the
saxophone app has many subdomains like
cdn.saxophone.sean, etc. As you can see, we’re now out of one-liner territory in
/etc/hosts. (Well, maybe a couple long lines.)
To configure the DNS-resolving VM (“gateway” for us), with Dnsmasq, the configuration lines would look something like this:
You can test with:
$ dig +short @gateway.sean admin.saxophone.sean 192.168.222.16 $ dig +short @gateway.sean www.trombone.sean 192.168.222.17 $ dig +short @gateway.sean trombone.sean 192.168.222.17
Now we’ve got the VM side set up. How do we best instruct the OS to resolve the new (fake)
sean TLD “properly”?
Mac OS X has a mechanism called resolver that allows us to choose specific DNS servers for specific TLDs, which is very convenient.
Again, the short version of this is that you’d add the following line to
/etc/resolver/sean (assuming the gateway is on
192.168.222.2) on your workstation (not the VM):
Once complete (and
mDNSResponder has been reloaded), your computer will use the specified name server to resolve the
The longer version is that I don’t want to burden my VM users (especially those who get nervous touching anything in
/etc — and with good reason), with this additional bit of configuration, so we manage this in our
Vagrantfile, directly. Here’s an excerpt (we use something other than
sean, but this has been altered to be consistent with our examples):
# set up custom resolver if !File.exist? '/etc/resolver/sean' puts "Need to add the .sean resolver. We'll need sudo for this." puts "This should only happen once." print "\a" puts `sudo sh -c 'if [ ! -d /etc/resolver ]; then mkdir /etc/resolver; fi; echo "nameserver 192.168.222.2" > /etc/resolver/san; killall -HUP mDNSResponder;'` end
Then, when the day comes that we want to add a new app — call it
trumpet — we can do all of it through configuration management from the ops side. We create the new VM in Salt, and the next time the user’s
gateway is highstated (that is: the configuration management is applied), the
Vagrantfile is altered, and the DNS resolver configuration on the
gateway VM is changed. Once the user has done
vagrant up trumpet, they should be good to point their browsers at
https://trumpet.sean/. We don’t (specifically Vagrant doesn’t) even need
sudo on the workstation after the initial setup.