I spent some time this week working on building a Docker image using a Dockerfile. In the process I learned a little about networking with Docker that I wanted to record here before I forget about it.
One of the steps in building my image was to update the list of packages using apt-get update. Mysteriously during the build I would get these errors:
sudo docker build -t="build_2013-10-03" . Uploading context 20480 bytes Step 1 : FROM colinsurprenant/ruby-1.9.3-p448 ---> 6d1e62cb5cff ... Step 5 : RUN apt-get install --assume-yes software-properties-common sudo libmysqlclient-dev vim ---> Running in 481577d7acec ... Err http://us.archive.ubuntu.com/ubuntu/ raring/main libapt-inst1.5 amd64 0.9.7.7ubuntu4 Something wicked happened resolving 'us.archive.ubuntu.com:http' (-11 - System error)
Logging in to the container gave me a pointer in the form of a warning and an confirmation of the problem:
sudo docker run -i -t colinsurprenant/ruby-1.9.3-p448 /usr/bin/env bash WARNING: IPv4 forwarding is disabled. root@0836328ec06a:/# ping us.archive.ubuntu.com ping: unknown host us.archive.ubuntu.com
Since Docker containers are run inside a namespace and AuFS is used to hold their files, the only thing shared between the host OS and the container is the kernel. For IP traffic to move between guest and host the kernel must be set to do IP forwarding.
To enable this I needed to use the sysctl command and then restart the Docker daemon:
sudo sysctl -w net.ipv4.ip_forward=1
That little test solved my problem and so the next step was to ensure that the new setting would survive a reboot. As with almost all things on Linux, it just meant editing a configuration file:
sudo vim /etc/sysctl.conf # Kernel sysctl configuration file for Red Hat Linux # # For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and # sysctl.conf(5) for more details. # Controls IP packet forwarding net.ipv4.ip_forward = 1
My next learning about how networking works with Docker came from wanting the app in my container to access the MySQL database on the host. It turns out that docker creates a network interface:
mike@sleepycat:~☺ ifconfig docker0 Link encap:Ethernet HWaddr 9e:b5:ca:76:70:c3 inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::9cb5:caff:fe76:70c3/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:65 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:10675 (10.6 KB) ...
So on the host side I need to get MySQL to bind to 172.17.42.1, and containers (which will all end up on the 172.x.x.x network) will just connect to that. Don’t forget that your MySQL user ‘x’@’localhost’ won’t be able to connect when its logging in from 172.x.x.x and that you will need to add the “host: 172.17.42.1” to your database.yml.
The only real down side to all this Docker stuff has been that the extra layer of abstraction can make things a little hard on the head. The potential for repeatable, self documenting app deployments using Dockerfiles is pretty exciting. I’m impressed with what I have seen and I know that what I am doing is still pretty primitive. We’ll see what’s next.