Detailed Explanation of Docker Compose Network Settings

  • 2021-07-09 09:31:01
  • OfStack

Basic concepts

By default, Compose will create a network for our application, and each container of the service will join the network. In this way, the container can be accessed by other containers in the network, and not only that, it can also be accessed by other containers as hostname with service name.

By default, the application's network name is based on the project name of Compose, and the project name is based on the name of the directory where docker-compose. yml is located. To modify the project name, use the-project-name identity or the COMPOSE_PORJECT_NAME environment variable.

For example, suppose an application is in a directory named myapp, and docker-compose. yml looks like this: version: '2'


services:
 web:
 build: .
 ports:
  - "8000:8000"
 db:
 image: postgres

When we run docker-compose up, the following steps will be performed:

Create a network named myapp_default; Create a container using the configuration of the web service, which joins the network myapp_default under the name "web"; Create a container using the configuration of the db service, which joins the network myapp_default under the name "db".

Containers can access each other using service names (web or db) as hostname. For example, the web service can access the db container using postgres://db: 5432.

Update container

When the configuration of the service changes, you can update the configuration using the docker-compose up command.

At this point, Compose deletes the old container and creates a new container. The new container will join the network with a different IP address, and the name will remain unchanged. Any connection to the old container is closed, and the container refinds the new container and connects it. As mentioned earlier in links, by default, services can access each other using service names.

links

Allows us to define a 1 alias to access other services using that alias. For example: version: '2'


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres

This allows the web service to access the db service using either db or database as the hostname.

Specify a custom network

In some scenarios, the default network configuration can't meet our needs, so we can use networks command to customize the network. The networks command allows us to create more complex network topologies and specify custom network drivers and options. Not only that, we can also use networks to connect services to externally created networks that are not managed by Compose.

Here, we define two custom networks. version: '2'


services:
 proxy:
 build: ./proxy
 networks:
  - front
 app:
 build: ./app
 networks:
  - front
  - back
 db:
 image: postgres
 networks:
  - back

networks:
 front:
 # Use a custom driver
 driver: custom-driver-1
 back:
 # Use a custom driver which takes special options
 driver: custom-driver-2
 driver_opts:
  foo: "1"
  bar: "2"

Among them, proxy service is isolated from db service, and both use their own networks respectively; The app service communicates with both.

From this example, it is not difficult to find that the network isolation and connection between services can be conveniently realized by using networks command.

Configure the default network

In addition to customizing the network, we can also customize the configuration for the default network. version: '2'


services:
 web:
 build: .
 ports:
  - "8000:8000"
 db:
 image: postgres

networks:
 default:
 # Use a custom driver
 driver: custom-driver-1

In this way, you can specify a custom network driver for the application.

Use an existing network

In some scenarios, we do not need to create a new network, but only need to join an existing network. At this time, we can use the external option. Example:


networks:
 default:
 external:
  name: my-pre-existing-network 

Docker Compose Several Ways to Link External Containers

Links between containers are a common operation in Docker: It provides the ability to access a network service for one of the containers without exposing the required port to the Docker Host host. Support for this feature in Docker Compose is also convenient. However, if the container to be linked is not defined in the same docker-compose. yml, this is a little cumbersome and complicated.

When not using Docker Compose, it is relatively simple to link two containers and use the-link parameter. Take nginx image as an example:


docker run --rm --name test1 -d nginx # Open 1 Instances test1
docker run --rm --name test2 --link test1 -d nginx # Open 1 Instances test2 And with test1 Establish a link 

In this way, test2 is linked to test1 and can be used in test2 to access services in test1. If you use Docker Compose, this is much simpler. Using the nginx image above as an example, edit the docker-compose. yml file as follows:


version: "3"
services:
 test2:
 image: nginx
 depends_on:
  - test1
 links:
  - test1
 test1:
 image: nginx

The final effect is no different from the link established using the ordinary Docker command docker run xxxx. This is only one of the most ideal situations.

If containers are not defined in the same docker-compose. yml file, how should they be linked?

What if the container defined in the docker-compose. yml file needs to be linked to the container started by docker run xxx?

In view of these two typical situations, the following is a feasible method for my personal test:

Mode 1: Let the containers to be linked belong to the same external network

Let's also use the nginx image to simulate a scenario where we need to link two nignx containers (test1 and test2) managed by Docker Compose so that test2 can access the services provided in test1, where ping is available.

First, we define the contents of the docker-compose. yml file for the container test1 as follows:


version: "3"
services:
 test2:
 image: nginx
 container_name: test1
 networks:
  - default
  - app_net
networks:
 app_net:
 external: true

The content of container test2 is basically the same as that of test1, except that there is one more external_links. It should be specially explained that the recently released version of Docker no longer needs to use external_links to link containers, and the DNS service of containers can make correct judgments. Therefore, if you need to be compatible with the older version of Docker, the contents of docker-compose. yml file of container test2 are as follows:


version: "3"
services:
 test2:
 image: nginx
 networks:
  - default
  - app_net
 external_links:
  - test1
 container_name: test2
networks:
 app_net:
 external: true

Otherwise, the definitions of docker-compose. yml and test1 of test2 are completely identical, and there is no need to specify an additional external_links. For related questions, see related questions on stackoverflow: docker-compose + external container

As you can see, the same external network app_net is used in the definitions of both containers, so we need to re-create the external network with the following command before starting these two containers:


docker network create app_net

After that, start the two containers with the command docker-compose up-d, then execute docker exec-it test2 ping test1, and you will see the following output:


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres
0

It is proved that the two containers are successfully linked, and in turn, pingtest2 can communicate normally with ping in test1.

If we start a container (test3) by docker run-rm-name test3-d nginx and need to link it to test1 or test2, we can link it to the external network manually at this time:


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres
1

In this way, all three containers can access each other.

Mode 2: Change the network mode of the container that needs to be linked

You can change the network mode of the containers you want to link to each other to bridge, and specify the external containers to be linked to (external_links). Unlike Link Mode 1, which allows containers belonging to the same external network to access each other, this access is unidirectional. Or take the nginx container image as an example. If the container instance nginx1 needs to access the container instance nginx2, then doker-compose. yml of nginx2 is defined as:


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres
2

Corresponding to this, docker-compose. yml of nginx1 is defined as:


version: "3"
services:
 nginx1:
 image: nginx
 external_links:
  - nginx2
 container_name: nginx1
 network_mode: bridge

It should be noted that external_links here cannot be omitted, and the startup of nginx1 must be after nginx2, otherwise an error may be reported that the container nginx2 cannot be found. Next, we use ping to test the connectivity:


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres
4

The above can also fully prove that this way belongs to one-way communication.

In the actual application according to their own needs flexible choice of these two links, if you want to be lazy, you can choose the second. However, I prefer the first one. It is not difficult to see that both connectivity and flexibility are more friendly than the second one that changes the network mode.

Attached is docker-compose. yml file for detailed explanation of Compose and Docker compatibility:


services:
 web:
 build: .
 links:
  - "db:database"
 db:
 image: postgres
5

Related articles: