Sarah Savage logo

A fusion of development and business

Using common networks for communicating via microservices

If you have ever worked in a microservices architecture, you know that you have many related but separate services, often in isolated development environments. For example, you may have the identity service separate from the user creation service, which is separate from other services.

Using mocks (like WireMock) is a good idea for testing against APIs that you need to implement but don’t want to test directly against. It’s easy to include a mock server that implements the APIs you need. But what happens when you want to do more thorough testing, and you actually do want to test against real API endpoints?

If you’re using Docker, getting containers to communicate cross-network is nearly impossible. But there’s something you can do: create a common network.

Common Networks

In my parlance here, a “common network” is a network that different containers in different projects join for the purposes of communicating. I have three rules for a common network:

  1. The common network should be explicitly named in your Docker Compose file. For example, if you create a network called “store” in the “identity” directory, Docker Compose will create a network named identity_store. This defeats the purpose. So in your Docker Compose file you want to explicitly use the name parameter for naming the network.
  2. Do not add every container in your microservice to the new network! You don’t need them. If you’re using Apache with mod_php you only need to add the Apache container to the shared network. If you’re using Nginx and PHP-FPM you must add the Nginx container to the network, but you should consider adding the FPM container, too – the reason is, if the FPM container needs to call another service, that request will originate from the FPM container and it needs to be able to access the webserver of the other service.
  3. Use descriptive container names. If your webserver (Nginx or Apache) is named nginx or apache then you’ll end up with Docker Compose creating a name for you – usually <directory>-nginx-<#> or identity-nginx-1 and you’ll need to refer to it by this name. Problem is, that name isn’t stable – it’s based on rules that Docker Compose uses and could be different and thus break your integration. Instead, if you name your webserver identity then you can use https://identity from containers inside the network to call it directly.

Defining a common network

So how do you define a common network? The first step is to define the network inside your Docker Compose file, like this:

networks:
  identity:
    driver: "bridge"
  common:
    driver: "bridge"
    name: "common_network"

Note that I have defined two different bridge networks here: the `identity` network that is internal to all the services in my `identity` project, and the common network, which I have explicitly named common_network.

Next, you can tell Docker to add your webserver by adding the common network name to the service:

services:
  identity:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    networks:
      - identity
      - common

Note that we are not required to use the full name of the network (we referred to the network as `common` but it’s full name is common_network). Also note that we named the specific webservice identity so that we can easily access it from other containers.

Manually adding containers to a network

Let’s say you don’t want to modify the Docker Compose file and you want to add containers to your network directly and manually. No worries.

You can run the docker network connect command to connect a container to a particular network. For example, you could run docker network connect common_network identity to add the identity container to the common_network.

About that pesky error message…

Ah yes, astute readers will notice that Docker creates an error message when you define a network and a container joins it, but the network already exists. The error looks like this:

WARN[0020] a network with name common_network exists but was not created for project "identity".
Set `external: true` to use an existing network

While annoying, this error is benign. It just means that the network was already created, but was not created specifically for your project. Docker will create the network, if it doesn’t exist, and it will join the network, if it does exist. If you set the external: true value on the network configuration, the network will not be created when you run docker compose up resulting in an error. Instead I prefer to get the warning, and ignore it, as it doesn’t impact me.

As a side note, there is no way that I am aware of to tell Docker to “create a network if it doesn’t exist, and then join it”.

Conclusion

By adding Docker containers that need to communicate to a centralized network, they can seamlessly communicate internally. You don’t need to add all your containers to that network – network interactions between PHP and your database can happen on the project-specific network – but the network containers that are on the common network can communicate and interact. This makes testing simpler and easier when testing external microservices with end-to-end tests or complex interactions.

Thoughts? Leave a reply

Your email address will not be published. Required fields are marked *