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
1 thought on “Docker Networking – Part1”