1. Overview

One of the main features of Docker is creating and isolating networks.

In this tutorial, we'll see how to extract information about networks and the containers they hold.

2. Networking in Docker

When we run a Docker container, we can define what ports we want to expose to the outside world. What this means is that we use (or create) an isolated network and put our container inside. We can decide how we'll communicate both with and inside this network.

Let's create a few containers and configure networking between them. They will all internally work on port 8080, and they will be placed in two networks.

Each of them will host a simple “Hello World” HTTP service:

version: "3.5"

services:
  test1:
    image: node
    command: node -e "const http = require('http'); http.createServer((req, res) => { res.write('Hello from test1\n'); res.end() }).listen(8080)"
    ports:
      - "8080:8080"
    networks:
      - network1
  test2:
    image: node
    command: node -e "const http = require('http'); http.createServer((req, res) => { res.write('Hello from test2\n'); res.end() }).listen(8080)"
    ports:
      - "8081:8080"
    networks:
      - network1
      - network2
  test3:
    image: node
    command: node -e "const http = require('http'); http.createServer((req, res) => { res.write('Hello from test3\n'); res.end() }).listen(8080)"
    ports:
      - "8082:8080"
    networks:
      - network2

networks:
  network1:
    name: network1
  network2:
    name: network2

Here's a diagram of these containers for a more visual representation:

Let's start them all with the docker-compose command:

$ docker-compose up -d
Starting bael_test2_1 ... done
Starting bael_test3_1 ... done
Starting bael_test1_1 ... done

3. Inspecting the Network

Firstly, let's list all available Docker's networks:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
86e6a8138c0d        bridge              bridge              local
73402de5766c        host                host                local
e943f7124776        network1            bridge              local
3b9a28673a16        network2            bridge              local
9361d16a834a        none                null                local

We can see the bridge network, which is the default network used when we use the docker run command. Also, we can see the networks we created with a docker-compose command.

Let's inspect them with the docker inspect command:

$ docker inspect network1 network2
[
    {
        "Name": "network1",
        "Id": "e943f7124776d45a1481ee26795b2dba3f2ab51f000d875a179a99ce832eee9f",
        "Created": "2020-08-22T10:38:22.198709146Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        // output cutout for brevity
    }
],
    {
        "Name": "network2",
        // output cutout for brevity
    }
}

This will produce lengthy, detailed output. We rarely need all of this information. Fortunately, we can format it using Go templates and extract only the elements that suit our needs. Let's get only the subnet of network1:

$ docker inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' network1
172.22.0.0/16

4. Inspecting the Container

Similarly, we can inspect a specific container. First, let's list all containers with their identifiers:

$ docker ps --format 'table {{.ID}}\t{{.Names}}'
CONTAINER ID        NAMES
78c10f03ad89        bael_test2_1
f229dde68f3b        bael_test3_1
b09a8f47e2a8        bael_test1_1

Now we'll use the container's ID as an argument to the inspect command to find its IP address. Similarly to networks, we can format output to get just the information we need. We'll check the second container and its address in both networks we created:

$ docker inspect 78c10f03ad89 --format '{{.NetworkSettings.Networks.network1.IPAddress}}'
172.22.0.2
$ docker inspect 78c10f03ad89 --format '{{.NetworkSettings.Networks.network2.IPAddress}}'
172.23.0.3

Alternatively, we can print hosts directly from a container using the docker exec command:

$ docker exec 78c10f03ad89 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.22.0.2    78c10f03ad89
172.23.0.3    78c10f03ad89

5. Communication Between Containers

Using knowledge about our Docker networks, we can establish communication between containers in the same network.

First, let's get inside the  “test1” container:

$ docker exec -it b09a8f47e2a8 /bin/bash

Then, use curl to send a request to the “test2” container:

[email protected]:/# curl 172.22.0.2:8080
Hello from test2

Since we're inside the Docker's network, we can also use the alias instead of the IP address. Docker's builtin DNS service will resolve the address for us:

[email protected]:/# curl test2:8080
Hello from test2

Mind that we can't connect to the “test3” container because it's in a different network. Connecting by the IP address will time out:

[email protected]:/# curl 172.23.0.2:8080

Connecting by the alias will also fail because the DNS service won't recognize it:

[email protected]:/# curl test3:8080
curl: (6) Could not resolve host: test3

To make this work, we need to add “test3” container to “network1” (from outside of the container):

$ docker network connect --alias test3 network1 f229dde68f3b

Now request to “test3” will work correctly:

[email protected]:/# curl test3:8080
Hello from test3

6. Conclusion

In this tutorial, we've seen how to configure networks for Docker containers and then query information about them.