Debugging and testing containerized applications from a Mac can be fiddly, (because you’re running containers inside a Host, which itself is running inside the OS), especially if you have multiple containers using the same port or if a second port is assigned to a value not known before a process starts (JMX, I’m looking at you).

With a few tiny tweaks to your Mac, you can connect with Docker containers running on Weave Net and Docker for Mac, without exposing ports on the Docker host – which is great for testing and debugging during application development.

The Apple Mac users in the Weave team were excited to see the recent release of the beta version of Docker for Mac and to try out the new xhyve based container host. We tried Weave Net and it works just as you’d expect, but Docker’s desire to create a better user experience on the Mac got us thinking – what could we do to make developing containerized applications on a Mac even easier?

Debugging by connecting directly to an application

When debugging or developing an application it’s incredibly useful to be able to connect an IDE and other developer tools directly to the application service ports – but this can be fiddly if the application is running inside a container. We can fix this!

Let’s launch Weave and an Apache HTTPD as a target for the connection test:

<code>weave launch
docker run --name=httpd -d --net=weave --hostname=web.weave.local httpd
docker inspect -f="{{.NetworkSettings.Networks.weave.IPAddress}}" httpd
</code>

The result of the last command is: 10.32.0.1, (which may be different for you). Now we can perform the negative proof, using:

<code>pidster@host$ curl http://10.32.0.1/
curl: (7) Failed to connect to 10.32.0.1 port 80: Connection refused</code>

No connection through to the web server, which is the expected outcome.

Quick explanation

You can download a BASH script that does all of the below for you, (and can provide verbose output), with the added benefit that it has a reset command to undo the configuration.

There’s a more detailed explanation about what’s happening just below, but first, here’s an example of the commands to execute that make it possible to connect directly to the container:

<code>weave expose
sudo ifconfig bridge100 -hostfilter en5
sudo route add -net 10.32.0.0/12 docker.local
sudo route add -host 172.17.0.1 docker.local
sudo mkdir -p /etc/resolver && sudo sh -c 'echo "nameserver 172.17.0.1" >/etc/resolver/weave.local'</code>

How does it work?

First we need to extract the interface that’s mapped from the Docker for Mac bridge interface. The default configuration for the bridge is to restrict traffic from other hosts, so we must reconfigure this.

Expose the Weave sub-net

Execute the command: weave expose
This grants the host access to all application containers in the default subnet.

Bridge interface changes

<code>pidster@host$ ifconfig bridge100
<strong>bridge100</strong>: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=3<RXCSUM,TXCSUM>
    ether a6:5e:60:bd:b2:64
    inet 192.168.64.1 netmask 0xffffff00 broadcast 192.168.64.255
    Configuration:
        id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
        maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
        root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
        ipfilter disabled flags 0x2
    <strong>member: en5</strong> flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 11 priority 0 path cost 0
    Address cache:
        7e:ef:23:10:cf:32 Vlan1 en5 872 flags=0<>
    nd6 options=1
    media: autoselect
    status: active
</code>

The member interface name is now used in the changes we make to the bridge interface configuration.
Execute the command: sudo ifconfig bridge100 -hostfilter en5
Modifies the configuration of the Docker for Mac bridge interface bridge100 to permit traffic on the member interface en5 from all hosts.

Routing table changes

Adding routes to the Weave sub-net and the DNS server means we can route traffic, but also resolve DNS names on Weave network. The Weave script comes in handy again, for extracting the information we need – it’ll probably be the same each time, but we’ll use a couple of commands to be sure; the sub-net:

<code>pidster@host$ weave report -f "{{ .IPAM.DefaultSubnet }}"
10.32.0.0/12
</code>

We also need the DNS server address:

<code>pidster@host$ weave report -f "{{ .DNS.Address }}"
172.17.0.1:53
</code>

The sub-net and the IP address are now used in the changes made to the routing table.
Execute the command: sudo route add -net 10.32.0.0/12 docker.local
Adds a route to the Mac’s routing table to the Weave sub-net.
Execute the command: sudo route add -host 172.17.0.1 docker.local
Adds the route to the IP that the Weave DNS server runs on to the Mac’s routing table.

Add a nameserver to the DNS resolver

Mac OSX DNS name resolution can be configured by adding nameserver entries to the /etc/resolv.conf, or more usefully individual files to the /etc/resolver directory – which you may need to create. It’s preferable to choose the latter, as it’s easier to undo and remove later. The results above can be used again to populate the nameserver value.

The weave script is used once again to extract the domain name:

<code>pidster@host$ weave report -f "{{ .DNS.Domain }}"
weave.local.</code>

The domain name is now used in the DNS resolver configuration.
Execute the command: sudo mkdir -p /etc/resolver && sudo sh -c 'echo "nameserver 172.17.0.1" >/etc/resolver/weave.local'
Adds the Weave DNS server IP to the Mac’s DNS resolver.

So now let’s test that the routing works:

<code>pidster@host$ curl http://10.32.0.1/
<html><body><h1><strong>It works!</strong></h1></body></html></code>

DNS resolution

The routing works, but what about DNS? Scope registers itself in Weave DNS, when present, so let’s launch a Weave Scope instance and see if it’s reachable in the browser:

<code>pidster@host$ scope launch
6740576284b7f45e6f982f94c0d1d75e4650803b048a984dbd7d9c0a64648e07
Weave Scope is reachable at the following URL(s):
  * http://10.32.0.2:4040/
  * http://192.168.64.6:4040/
</code>

Here’s the result: Scope is now resolvable using http://scope.weave.local/ (if you’ve followed along, click the link to open it in your own browser).

Using DNS to connect directly to a container.

And the Apache Web Server we started for the original test? The Weave Net plugin for Docker listens for hostnames assigned to the .weave.local domain – so this also is registered in Weave DNS and accessible via the browser:

Using DNS to connect directly from an OSX browser to the test container running inside the host

Summary

We showed that Weave Net works perfectly with the beta version of Docker for Mac and demonstrated that routing from the Mac to container IP addresses is possible, using a handy BASH script. This makes it easier to develop and debug applications directly from your Mac.