Docker Networking – Part1

This blog is part of my Docker series. In this blog, I will cover basics of Docker Networking that covers the ingress and egress connectivity for containers and how containers are linked together. I will cover advanced networking topics in next blog.

When docker service is started, a linux bridge is created on the host machine. The interfaces on the containers talk to the bridge and the bridge proxies to external world. Multiple containers on the same host can talk to each other through the linux bridge.

Following is the bridge created on host machine seen through ifconfig. The bridge gets allocated IP address 172.17.42.1.

docker0   Link encap:Ethernet  HWaddr 56:84:7a:fe:97:99  
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Now, lets create 2 containers:

$ sudo docker run -ti ubuntu:14.04 /bin/bash
$ sudo docker run -ti ubuntu:14.04 /bin/bash

Following is the “ifconfig” output inside the first Ubuntu container:

root@7c0054f31c66:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:83  
          inet addr:172.17.0.131  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:83/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:10 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:776 (776.0 B)  TX bytes:468 (468.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Following is the “ifconfig” output inside the second Ubuntu container:

root@43ad51a234ec:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:84  
          inet addr:172.17.0.132  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:84/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:3 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:238 (238.0 B)  TX bytes:238 (238.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Both the above containers will be able to ping each other as well as reach external world. We will see how this is done.

As mentioned in previous blog, Containers use Network namespaces. When first container is created, a new network namespace is created for the container. A vethernet link is created between the container and the linux bridge. Traffic sent from eth0 of the container reaches the bridge through the vethernet interface and gets switched thereafter.

Relevant ifconfig output in the host machine:

veth936b7a6 Link encap:Ethernet  HWaddr c2:ae:db:21:d4:fa  
          inet6 addr: fe80::c0ae:dbff:fe21:d4fa/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:9 errors:0 dropped:0 overruns:0 frame:0
          TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:650 (650.0 B)  TX bytes:1918 (1.9 KB)

vethae86977 Link encap:Ethernet  HWaddr 42:fa:c2:b9:fb:48  
          inet6 addr: fe80::40fa:c2ff:feb9:fb48/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:9 errors:0 dropped:0 overruns:0 frame:0
          TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:650 (650.0 B)  TX bytes:2386 (2.3 KB)

Output of linux bridge ports:

$ sudo brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.56847afe9799	no		veth936b7a6
							vethae86977

How does the Container connect to external world?
iptables nat table on the host is used to masquerade all external connections like below.

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0          

How to reach containers from outside world?

Lets start a simple container running the apache service on port 80 inside the container and map the port 80 to port 8080 on the host machine. We can start this container like below.(apachedocker is my container image with apache service running, the image is also available in docker hub)

$ sudo docker run -d -p 8080:80 smakam/apachedocker

Following is the “docker ps” output for this container:

$ sudo docker ps
CONTAINER ID     IMAGE                        COMMAND                CREATED        STATUS        PORTS                  NAMES
8b4cdcb89419     smakam/apachedocker:latest   "/usr/sbin/apache2ct   5 seconds ago  Up 4 seconds  0.0.0.0:8080->80/tcp

The port mapping is again done using iptables NAT option in host machine as below:

Chain DOCKER (2 references)
target     prot opt source               destination         
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.133:80

From localhost, browser pointed at “http://localhost:8080” will show the default webpage.

How to link 2 containers?

Linking multiple containers is needed as a typical application would be split between multiple containers and 1 container would need access services from another container. 1 approach is by statically exposing the ports and having another container use those ports. This makes the containers less secure. Docker provides a native approach to link containers and this allows the source container to share environment variables with destination container.

In the example below, lets create a web container and db container and let the web container link to the db container so that the db container’s environment variables can be accessed from the db container.

First step is to create the 2 containers:

$ sudo docker run -d  --name db training/postgres
$ sudo docker run -d -p 8080:80 --name web --link db:dblink smakam/apachedocker

Above, there is an alias specified called “dblink” and the db container’s environment variables are exposed to web container with a prefix of “dblink”. Lets look at the environment variables in web container.

# set|grep DBLINK
DBLINK_ENV_PG_VERSION=9.3
DBLINK_NAME=/web/dblink
DBLINK_PORT=tcp://172.17.0.3:5432
DBLINK_PORT_5432_TCP=tcp://172.17.0.3:5432
DBLINK_PORT_5432_TCP_ADDR=172.17.0.3
DBLINK_PORT_5432_TCP_PORT=5432
DBLINK_PORT_5432_TCP_PROTO=tcp

Now, lets try to create a simple database table from web container and check if thats successful. Here, we have used the environment variables rather than hardcoding IP and port numbers.

psql -h $DBLINK_PORT_5432_TCP_ADDR -p $DBLINK_PORT_5432_TCP_PORT  -U docker -c "CREATE TABLE projects ( title TEXT NOT NULL, description TEXT NOT NULL)"
psql -h $DBLINK_PORT_5432_TCP_ADDR -p $DBLINK_PORT_5432_TCP_PORT  -U docker -c "INSERT into projects VALUES ('first', 'sample')"
psql -h $DBLINK_PORT_5432_TCP_ADDR -p $DBLINK_PORT_5432_TCP_PORT  -U docker -c "SELECT * from projects"

Other than environment variables, hostname of db container is also accessible from web container through /etc/hosts.

# ping -c1 dblink
PING dblink (172.17.0.9) 56(84) bytes of data.
64 bytes from dblink (172.17.0.9): icmp_seq=1 ttl=64 time=0.090 ms

--- dblink ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.090/0.090/0.090/0.000 ms

Lets say that we restart the db container, the environment variables and hostname gets automatically updated in web container.

From host:
$ sudo docker restart db
From db container:
# set|grep DBL  
DBLINK_ENV_PG_VERSION=9.3
DBLINK_NAME=/web/dblink
DBLINK_PORT=tcp://172.17.0.7:5432
DBLINK_PORT_5432_TCP=tcp://172.17.0.7:5432
DBLINK_PORT_5432_TCP_ADDR=172.17.0.7
DBLINK_PORT_5432_TCP_PORT=5432
DBLINK_PORT_5432_TCP_PROTO=tcp

root@7098e10f60b9:/# cat /etc/hosts
172.17.0.9	dblink

 References:

One thought on “Docker Networking – Part1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s