Docker networks

docker docker network

Today I want to talk about docker networks. When you install docker it creates 3 networks automatically. To list all the networks use docker network ls.

$ docker network ls
NETWORK ID          NAME                 DRIVER              SCOPE
9a383391d20c        bridge               bridge              local
060b58f23947        host                 host                local
cf28ccbe9537        none                 null                local

As you can imagine, these 3 networks are part of Docker implementation. When you create a container you have the --network flag that can use to specify in which network your container should run.

The host network is used to add a container to the hosts network stack. The none network is used to add a container to a container specific network stack.

You don’t need to interact with these two default networks. You can list them and inspect them but you can not remove them. Because you can create your own networks and use them to connect and make containers communicate to each other I think it’s worth to take a look on the default bridge network to see how it works.

Default bridge network

The bridge network represents the docker0 network which is present in all Docker installations. The Docker deamon connects the new containers to this network by default, unless you specifically tell it otherwise through --network flag. You could see this bridge on linux with ifconfig.

docker0   Link encap:Ethernet  HWaddr 02:42:b4:53:51:b2
          inet addr:172.17.0.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)

The default bridge network in detail can be seen with the command docker network inspect

$ docker network inspect bridge

[
    {
        "Name": "bridge",
        "Id": "9a383391d20c1d8b94f2f74068240e143a42fe1c5cf60e187af5b408d05acb00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

The container is automatically added to the default network through docker run command. In the same time it creates a Subnet and a Getway to the network.

$ docker run -itd --name=container busybox
82d6575acb647a0110d560e0c3841f5ac07a300025fecc0f99b3a606afc61d49

You can attach to a running container and investigate its configuration through the attach command.

$ docker attach container
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:63 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:9902 (9.6 KiB)  TX bytes:648 (648.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:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	82d6575acb64

Note: You can detach from the container and leave it running with CTRL-p CTRL-q.

You can also see the new container added to the default network if you inspect it once again. As you can see it is added in the “Containers” section.

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "9a383391d20c1d8b94f2f74068240e143a42fe1c5cf60e187af5b408d05acb00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "82d6575acb647a0110d560e0c3841f5ac07a300025fecc0f99b3a606afc61d49": {
                "Name": "container",
                "EndpointID": "abb28479dcc6e48af7b208cd0a6c852c7ebcc834dfdca052015dee06c653fcad",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

As you can imagine all the containers inside the default network can communicate to each other, but only by their IP address. Docker does not support automatic service discovery on the default bridge network.

Note! If you want to communicate between containers through the container names you have to use the legacy docker run --link but you should avoid this technique and instead create your own bridge networks.

So let’s say you create another container in the default network second-container and you try to ping the container from inside it. You would do something like this.

$ docker run -itd --name=second-container busybox
ae6c2924137537438167149d2f0aa83c5de3b74e9e200069c4cc1f6b18ba7e7e

$ docker attach second-container
/ # ping -w3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.172 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.149 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.123 ms

--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.123/0.148/0.172 ms

You can off course disconnet a container from the network by using docker network disconnect command.

$ docker network disconnect bridge second-container
$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "9a383391d20c1d8b94f2f74068240e143a42fe1c5cf60e187af5b408d05acb00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "82d6575acb647a0110d560e0c3841f5ac07a300025fecc0f99b3a606afc61d49": {
                "Name": "container",
                "EndpointID": "abb28479dcc6e48af7b208cd0a6c852c7ebcc834dfdca052015dee06c653fcad",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

As you can see the second-container isn’t in the bridge network anymore. If you list the containers docker ps you will still see it.

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
ae6c29241375        busybox             "sh"                30 minutes ago      Up 30 minutes                           second-container
82d6575acb64        busybox             "sh"                About an hour ago   Up About an hour                        container

If you try again to ping the container from inside second-container you will get this.

$ docker attach second-container
/ # ping -w3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
ping: sendto: Network is unreachable

Most used docker network commands

List all docker networks

$ docker network ls

Create a docker network

$ docker network create --driver=bridge <name>

Connect a container to a network

$ docker network connect <name> <container>

Disconnect a docker container from a network

$ docker network disconnect <name> <container>

Remove a docker network

$ docker network rm appnetwork

Inspect a docker network

$ docker network inspect <name>

Now that we know all this new commands, let’s create a new network for an app that you build.

$ docker network create --driver=bridge appnetwork
aa22406b9c055ecce93fd3d1f99ef26a1f9683c63a801a15d62d960ea746281c

$ docker network list
NETWORK ID          NAME                 DRIVER              SCOPE
aa22406b9c05        appnetwork           bridge              local
9a383391d20c        bridge               bridge              local
060b58f23947        host                 host                local
cf28ccbe9537        none                 null                local

$ docker network inspect appnetwork
[
    {
        "Name": "appnetwork",
        "Id": "1efe567faea63dd5d0f98993cd4b3fe09265dd8083bd0500b21af782b184a62e",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

After we create the appnetwork, we can launch containers on it using the docker run --network=<NETWORK> option.

$ docker run --network=appnetwork -itd --name=third-container busybox
a9d2489bff114170747deb78d1d7300575bc6b74da36d52120dacb56266e812f
$ docker network inspect appnetwork
[
    {
        "Name": "appnetwork",
        "Id": "aa22406b9c055ecce93fd3d1f99ef26a1f9683c63a801a15d62d960ea746281c",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/16",
                    "Gateway": "172.23.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "a9d2489bff114170747deb78d1d7300575bc6b74da36d52120dacb56266e812f": {
                "Name": "third-container",
                "EndpointID": "02f935901427d3b944c4c40ebd95c0d59250469f5546b1c30600b9cc60322bd2",
                "MacAddress": "02:42:ac:17:00:02",
                "IPv4Address": "172.23.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

And off course we can connect existing containers to our new network.

$ docker network connect appnetwork second-container
$ docker network inspect appnetwork
[
    {
        "Name": "appnetwork",
        "Id": "aa22406b9c055ecce93fd3d1f99ef26a1f9683c63a801a15d62d960ea746281c",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/16",
                    "Gateway": "172.23.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "89dd8ffec9978b7bd8479cd476bc2e52e7b98d70d3fc047422fbd9499b498a8d": {
                "Name": "second-container",
                "EndpointID": "e3fad0af4496a640aee1a00a427aec4ad96d25ecd8cc73f2e694eddede6e2a3e",
                "MacAddress": "02:42:ac:17:00:03",
                "IPv4Address": "172.23.0.3/16",
                "IPv6Address": ""
            },
            "a9d2489bff114170747deb78d1d7300575bc6b74da36d52120dacb56266e812f": {
                "Name": "third-container",
                "EndpointID": "02f935901427d3b944c4c40ebd95c0d59250469f5546b1c30600b9cc60322bd2",
                "MacAddress": "02:42:ac:17:00:02",
                "IPv4Address": "172.23.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

In our custom network we can communicate between containers using their names like this.

$ docker attach second-container
/ # ping -w3 third-container
PING third-container (172.23.0.2): 56 data bytes
64 bytes from 172.23.0.2: seq=0 ttl=64 time=0.123 ms
64 bytes from 172.23.0.2: seq=1 ttl=64 time=0.105 ms
64 bytes from 172.23.0.2: seq=2 ttl=64 time=0.127 ms

--- third-container ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.105/0.118/0.127 ms

Conclusion

Docker network new feature it’s amazing. Before it you could use the Docker link feature to allow containers to discover each other. With the introduction of Docker networks, containers can communicate with each other by its name automatically. You can still use --link option to bind containers but as I said before this is not recommended anymore. For more information about docker network, please refer to the official page.


If you liked this post, you can share it on Twitter. Also you can follow me on Github or endorse me on LinkedIn.