Anentropic
Anentropic

Reputation: 33823

How to auto-configure hosts entry for multiple docker-machine VMs (OS X)

For a long time I only had one Docker VM on my laptop (first via boot2docker, then via docker-machine).

In this case it was easy, the Docker VM always had the same IP address and I could manually edit my /etc/hosts file to add a name like mysite.local pointing to the docker IP.

But recently I had to set up a second docker-machine VM. Now, I guess depending which order I start them up, my containers may be on one of two IP addresses. So my hand-made /etc/hosts entries don't always work.

I don't want to have to edit this file by hand every time. I'm looking for a way to automate it.

Upvotes: 2

Views: 2028

Answers (1)

Anentropic
Anentropic

Reputation: 33823

One useful tool is dnsmasq, which can be installed with Homebrew. Following the example here I set up two new 'top level domains' in my resolv.conf, one for each docker machine, by doing e.g.:

sudo tee /etc/resolver/dev >/dev/null <<EOF
nameserver 127.0.0.1
EOF

In other words: requests for any .dev hostnames will be resolved by my local dnsmasq instance (at 127.0.0.1).

I am using dnsmasq so I don't have to define specific hostnames, it can be set up to resolve wildcard i.e. all *.dev names to a given IP address (which will be the IP of one of my docker machines).

But I still need a way to update the dnsmasq config based on the current IP of my docker machine.

There is an open issue to formalise a way to achieve this in docker-machine:
https://github.com/docker/machine/issues/1792

In the meantime I have ended up with a bunch of aliases and utility functions in my .bashrc

alias dc='docker-compose'
alias dm='docker-machine'

dcexec() {
    # (for docker-compose containers)
    # because it's tedious having to type
    # `docker exec -it myproject_myservice_1 bash`
    # every time I want a shell into a running container
    if [ "$#" -gt 1 ]; then
        arg="$@"
    else
        arg="bash"
    fi
    docker exec -it "${PWD##*/}_$1_1" $arg
}
# eg `dcexec myservice`

dnsmasq-restart(){
    echo "Restarting dnsmasq..."
    sudo launchctl stop homebrew.mxcl.dnsmasq
    sudo launchctl start homebrew.mxcl.dnsmasq
}

docker-machine-env() {
    eval $(docker-machine env $1)
}
alias dme='docker-machine-env'

docker-machine-dns() {
    dmip=$(dm ip $1)
    dnsconf=/usr/local/etc/dnsmasq.conf
    if [ ! -e /etc/resolver/$1 ]; then
        echo "adding $1 entry to resolvers"
        sudo tee /etc/resolver/$1 >/dev/null <<EOF
nameserver 127.0.0.1
EOF
    fi
    if grep -q /$1/$dmip $dnsconf; then
        echo "correct dnsmasq entry already exists"
    elif grep -q ^address=/$1/ $dnsconf; then
        echo "hostname $1 already present in dnsmasq - updating"
        # remove line if ip already present for different hostname
        sed -i '' -E "\%^address=/[[:alnum:]_.-]+/$dmip%d" $dnsconf
        # update existing hostname with new ip
        sed -i '' -E "s%^(address=/$1)/([[:digit:].]+)$%\1/$dmip%g" $dnsconf
        dnsmasq-restart
    else
        # neither hostname nor ip are present
        echo "adding $1/$dmip entry to dnsmasq"
        echo "address=/$1/$dmip" >> /usr/local/etc/dnsmasq.conf
        dnsmasq-restart
    fi
}
docker-machine-use() {
    docker-machine start $1
    docker-machine-dns $1
    docker-machine-env $1
}
alias dmuse='docker-machine-use'

Now I can just type dmuse dev to start working on a project and it will ensure the docker-machine vm is running, dns entries ready and environment set (so the docker command works).

I'll note for anyone finding this: the stuff above is of course pretty specific to the use-case of developing with docker-machine local VMs on OSX.

Upvotes: 4

Related Questions