Link external containers using docker-compose

If you use docker and docker-compose then you might have come across cases where you want services running across different networks to interact. Typically this need arises when we use a single database container like postgres and we want other containers created using different docker-compose files to use the database container. It can become really confusing and difficult to setup. I got stuck recently with a similar issue and resolved it. Hope it might be useful for you as well.

Present setup:

postgres database container running in the default bridge network. Initialised using command: docker run -d --name postgres postgres

Running a service with 2 containers – web and redis. Web runs the web server. Web server uses redis for queing jobs. That’s why it’s linked in links option of the docker-compose file. We have a postgres database running separately on the default network.

*Problem: Docker-compose creates a separate network for all the containers defined in the compose file. Which means if you want to connect any external container with these services its a problem. *

Option1 : Use the default docker bridge

default docker bridge is a network present on all Docker hosts. If you do not specify a different network, new containers are automatically connected to the default bridge network. We did not specify any network in the postgres container that we created and hence it would use the default docker bridge. If we run containers mentioned in our docker-compose file in this default network they would be able to communicate with each other. Below is a docker-compose file for that.

version: '3'
services:
  web:
    build: .
    image: some-image
    ports:
      - "3001:3000"
    dns: "8.8.8.8"
    volumes:
      - ".:/app"
    env_file: .env
    links:
      - redis:redis
    external_links:
      - postgres1
      # In production instead add external_links for db
    command: bash -c "rake assets:precompile & rake db:create && rake db:migrate && rails s"
    network_mode: bridge
  redis:
    image: redis:latest
    network_mode: bridge

Assuming you have initialised your postgres database using the command docker run -d --name postgres postgres then it would be created in the default docker bridge network. Using the network_mode: bridge option both web and redis containers are attached to the default docker network and hence web, redis and postgres are all part of the same network and thats why the external link would work without any issues.

Drawbacks: This works fine but this has a potential problem. Assuming you are going to run 30-40 services like this each running multiple containers then all those services have containers running in same network. So technically you are not getting network level isolation.

Option 2 : Use an external network

The idea here is to create an external network and connect containers wanting to interact with each other to that network. For example we have a web container and postgres container both running on different networks but they want to talk to each other. We can attach multiple networks with a paritcular container and hence if we create a new network testnetwork and attach it with both web and postgres containers then they would be able to interact. This way web and postgres would be able to talk but redis would not be able to interact with postgres which is what we need too.

Before running the below compose file, you must attach postgres container also to the testnetwork network which we are going to create.

Useful commands:
docker network create testnetwork
docker network connect testnetwork postgres

version: '3'
services:
  web:
    build: .
    image: some-image
    ports:
      - "3001:3000"
    dns: "8.8.8.8"
    volumes:
      - ".:/app"
    env_file: .env
    links:
      - redis:redis
    external_links:
      - postgres1
    command: bash -c "rake assets:precompile & rake db:create && rake db:migrate && rails s"
    networks:
      - testnetwork
  redis:
    image: redis:latest

networks:
  testnetwork:
    external: true

# We are not attaching redis to the testnetwork because it does not need access to the postgres database

Benefits of Option1 over Option2: in Option 2 our redis container is running in an isolated network and hence can’t be reached from outside. Option2 allows us isolation. In Option1 all containers can connect to each other because they are all part of the same network.

NOTE: I am not an expert on this topic, I’ve posted the solution which worked for me when I was stuck. Each method has its own pros and cons and you are requested to look them up carefully before using them in production setup.

Comments

comments