Code Monkey home page Code Monkey logo

blog-connecting-compose-projects's Introduction

Connecting Two Docker Compose Projects

Let's assume I have two Docker Compose projects:

# project-a/compose.yml
version: '3.7'

services:
  app:
    image: alpine
    command: ['tail', '-f', '/dev/null']
# project-b/compose.yml
version: '3.7'

services:
  app:
    image: nginx

  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root

I want to connect from app service in project-a to app service in project-b.

Project Networking

Since those are separate Docker Compose configurations, each of them will have different network configurations.

$ docker compose --project-directory=project-a ps   
NAME                IMAGE               COMMAND               SERVICE             CREATED             STATUS              PORTS
project-a-app-1     alpine              "tail -f /dev/null"   app                 13 seconds ago      Up 3 seconds


$ docker compose --project-directory=project-b ps          
NAME                IMAGE               COMMAND                  SERVICE             CREATED             STATUS              PORTS
project-b-app-1     nginx               "/docker-entrypoint.…"   app                 18 minutes ago      Up 18 minutes       80/tcp
project-b-db-1      mysql               "docker-entrypoint.s…"   db                  18 minutes ago      Up 18 minutes       3306/tcp, 33060/tcp

Networking for service app in project-a:

$ docker inspect project-a-app-1 | jq '.[0].NetworkSettings.Networks'
{
  "project-a_default": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-a-app-1",
      "app",
      "e715c34bc4ad"
    ],
    "NetworkID": "6beb2220ce42d44b570c2bb95cd613994454eb075a17c82ae28608378d221306",
    "EndpointID": "2621f1306401f4752d23002a4e4713e84244bc08e5c421b4bde82e46610d0d17",
    "Gateway": "172.19.0.1",
    "IPAddress": "172.19.0.2",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:13:00:02",
    "DriverOpts": null
  }
}

Networking for service app in project-b:

$ docker inspect project-b-app-1 | jq '.[0].NetworkSettings.Networks'
{
  "project-b_default": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-b-app-1",
      "app",
      "c1fc59d59fb0"
    ],
    "NetworkID": "be7b70bc2897ad53021025bad3b937dd4abc159ebdc4a048f3a766e99f80ab99",
    "EndpointID": "1540af6b002f0cddb5643b80a484c4332ca2c2ad0c1b2b362c9cd8c7730e754d",
    "Gateway": "172.18.0.1",
    "IPAddress": "172.18.0.2",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:12:00:02",
    "DriverOpts": null
  }
}

When containers are not in the same network, they can't communicate and ping will fail.

$ docker compose --project-directory=project-a exec app ping -c 3 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes

--- 172.18.0.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

Connecting Projects

Both services need to be in the same network, and I can do this in two ways.

Quick and dirty way

By default, when a project is starting, Docker Compose will create a default network for the whole project to use. The name of this default network will be generated based on the project name (if not specified compose.yml parent directory name is used) and _default suffix. In the case of project-b, it is project-b_default. Each service that did not specify any network will be attached to the default network. I can use this knowledge to quickly connect project-a app service to default network in project-b.

In project-a/compose.yml I will specify an external network with name matching project-b default network.

# project-a/compose.yml
version: '3.7'

services:
  app:
    image: alpine
    command: ['tail', '-f', '/dev/null']
    networks:
      - project-b-network 

networks:
  project-b-network:
    external: true
    name: project-b_default # name matching project-b default network

To apply changes I will run docker compose up in project-a:

$ docker compose --project-directory=project-a up -d                     
[+] Running 1/1
 ✔ Container project-a-app-1  Started

Now app service is connected to the project-b default network.

$ docker inspect project-a-app-1 | jq '.[0].NetworkSettings.Networks'
{
  "project-b_default": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-a-app-1",
      "app",
      "92e7b9d1d09a"
    ],
    "NetworkID": "be7b70bc2897ad53021025bad3b937dd4abc159ebdc4a048f3a766e99f80ab99",
    "EndpointID": "539530074b89f1e2aa6b7ad724e0ca11ee6c9b6b3d3c1038b3a8fc243292eb58",
    "Gateway": "172.18.0.1",
    "IPAddress": "172.18.0.4",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:12:00:04",
    "DriverOpts": null
  }
}

The ping command should work now.

$ docker compose --project-directory=project-a exec app ping -c 3 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.090 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.081 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.078 ms

--- 172.18.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.078/0.081/0.090 ms

Container IPs often change, so using them is not a good option. Fortunately, with docker instead of using IPs, I can use network aliases.

$ docker compose --project-directory=project-a exec app ping -c 3 project-b-app-1
PING project-b-app-1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.085 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.078 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.080 ms

--- project-b-app-1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.078/0.081/0.085 ms

Why is it not a good solution?

For a few reasons:

  1. I need to remember to start project-b first, otherwise project-a will not start
  2. Project names can change, and network names will change too
  3. project-a can also connect to other services in project-b default network (like db service) which may not be desired

Connecting using an external docker network

Most of the problems with the previous approach can be fixed by creating an external docker network and using it for services that need to interconnect.

$ docker network create project-a-b-network
395af51e4e71cec84f5e7c81e76d7063135048c0ae255ca8eb656853b94de335

I will use a new external network with services that need to interconnect.

New configuration for project-a:

version: '3.7'

services:
  app:
    image: alpine
    command: ['tail', '-f', '/dev/null']
    networks:
      - project-a-b-network

networks:
  project-a-b-network:
    external: true

New configuration for project-b:

version: '3.7'

services:
  app:
    image: nginx
    networks:
      default:
      project-a-b-network:
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root

networks:
  project-a-b-network:
    external: true

I need to apply changes:

$ docker compose --project-directory=project-a up -d                        
[+] Running 1/1
 ✔ Container project-a-app-1  Started


$ docker compose --project-directory=project-b up -d                 
[+] Running 2/2
 ✔ Container project-b-app-1  Started                                                                                                                                                                                                       0.9s 
 ✔ Container project-b-db-1   Running

Now, app service in project-a will be using the new external network.

$ docker inspect project-a-app-1 | jq '.[0].NetworkSettings.Networks'
{
  "project-a-b-network": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-a-app-1",
      "app",
      "106cd9833d6d"
    ],
    "NetworkID": "395af51e4e71cec84f5e7c81e76d7063135048c0ae255ca8eb656853b94de335",
    "EndpointID": "cbead073238355c02dc0953cf8d648f0517bbf4d03639ac3a2a7efdbd6f29a76",
    "Gateway": "172.20.0.1",
    "IPAddress": "172.20.0.2",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:14:00:02",
    "DriverOpts": null
  }
}

app service in project-b is also using the new network in addition to the default one. It is using both because it needs to also communicate with the db service.

$ docker inspect project-b-app-1 | jq '.[0].NetworkSettings.Networks'
{
  "project-a-b-network": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-b-app-1",
      "app",
      "eb88f1cf5a82"
    ],
    "NetworkID": "395af51e4e71cec84f5e7c81e76d7063135048c0ae255ca8eb656853b94de335",
    "EndpointID": "beafe3a2833fd69b86682a8c642b56bf202b3582f63f1fb9c015dd851c13089e",
    "Gateway": "172.20.0.1",
    "IPAddress": "172.20.0.3",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:14:00:03",
    "DriverOpts": null
  },
  "project-b_default": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-b-app-1",
      "app",
      "eb88f1cf5a82"
    ],
    "NetworkID": "be7b70bc2897ad53021025bad3b937dd4abc159ebdc4a048f3a766e99f80ab99",
    "EndpointID": "34285accb2ab9633c57de26436499ac9c6ebd34e9cb29d0da42922fb930c48bd",
    "Gateway": "172.18.0.1",
    "IPAddress": "172.18.0.2",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:12:00:02",
    "DriverOpts": null
  }
}

Ping is working fine from app service in project-a.

$ docker compose --project-directory=project-a exec app ping -c 3 project-b-app-1
PING project-b-app-1 (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.071 ms
64 bytes from 172.20.0.3: seq=1 ttl=64 time=0.082 ms
64 bytes from 172.20.0.3: seq=2 ttl=64 time=0.081 ms

--- project-b-app-1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.071/0.078/0.082 m

db service in project-b cannot be accessed from project-a.

$ docker compose --project-directory=project-a exec app ping -c 3 172.18.0.3     
PING 172.18.0.3 (172.18.0.3): 56 data bytes

--- 172.18.0.3 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

It is inaccessible because it is not in the same network.

$ docker inspect project-b-db-1 | jq '.[0].NetworkSettings.Networks'         
{
  "project-b_default": {
    "IPAMConfig": null,
    "Links": null,
    "Aliases": [
      "project-b-db-1",
      "db",
      "0b7ebade4bfb"
    ],
    "NetworkID": "be7b70bc2897ad53021025bad3b937dd4abc159ebdc4a048f3a766e99f80ab99",
    "EndpointID": "2369f221e48c9a55cf6cb5598aaacb3667f09a7bfadc3a19cbe802df43693f75",
    "Gateway": "172.18.0.1",
    "IPAddress": "172.18.0.3",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:12:00:03",
    "DriverOpts": null
  }
}

blog-connecting-compose-projects's People

Contributors

piotrekkr avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.