Connect two instances of docker-compose [duplicate] - docker

This question already has answers here:
Communication between multiple docker-compose projects
(20 answers)
Closed 4 months ago.
I have a dockerized application with a few services running using docker-compose. I'd like to connect this application with ElasticSearch/Logstash/Kibana (ELK) using another docker-compose application, docker-elk. Both of them are running in the same docker machine in development. In production, that will probably not be the case.
How can I configure my application's docker-compose.yml to link to the ELK stack?

Update Jun 2016
The answer below is outdated starting with docker 1.10. See this other similar answer for the new solution.
https://stackoverflow.com/a/34476794/1556338
Old answer
Create a network:
$ docker network create --driver bridge my-net
Reference that network as an environment variable (${NETWORK})in the docker-compose.yml files. Eg:
pg:
image: postgres:9.4.4
container_name: pg
net: ${NETWORK}
ports:
- "5432"
myapp:
image: quay.io/myco/myapp
container_name: myapp
environment:
DATABASE_URL: "http://pg:5432"
net: ${NETWORK}
ports:
- "3000:3000"
Note that pg in http://pg:5432 will resolve to the ip address of the pg service (container). No need to hardcode ip addresses; An entry for pg is automatically added to the /etc/host of the myapp container.
Call docker-compose, passing it the network you created:
$ NETWORK=my-net docker-compose up -d -f docker-compose.yml -f other-compose.yml
I've created a bridge network above which only works within one node (host). Good for dev. If you need to get two nodes to talk to each other, you need to create an overlay network. Same principle though. You pass the network name to the docker-compose up command.

You could also create a network with docker outside your docker-compose :
docker network create my-shared-network
And in your docker-compose.yml :
version: '2'
services:
pg:
image: postgres:9.4.4
container_name: pg
expose:
- "5432"
networks:
default:
external:
name: my-shared-network
And in your second docker-compose.yml :
version: '2'
myapp:
image: quay.io/myco/myapp
container_name: myapp
environment:
DATABASE_URL: "http://pg:5432"
net: ${NETWORK}
expose:
- "3000"
networks:
default:
external:
name: my-shared-network
And both instances will be able to see each other, without open ports on host, you just need to expose ports, and there will see each other through the network : "my-shared-network".

If you set a predictable project name for the first composition you can use external_links to reference external containers by name from a different compose file.
In the next docker-compose release (1.6) you will be able to use user defined networks, and have both compositions join the same network.

Take a look at multi-host docker networking
Networking is a feature of Docker Engine that allows you to create
virtual networks and attach containers to them so you can create the
network topology that is right for your application. The networked
containers can even span multiple hosts, so you don’t have to worry
about what host your container lands on. They seamlessly communicate
with each other wherever they are – thus enabling true distributed
applications.

I didn't find any complete answer, so decided to explain it in a complete and simple way.
To connect two docker-compose you need a network and putting both docker-composes in that network,
you could create netwrok with docker network create name-of-network,
or you could simply put network declaration in networks option of docker-compose file and when you run docker-compose (docker-compose up) the network would be created automatically.
put the below lines in both docker-compose files
networks:
net-for-alpine:
name: test-db-net
Note: net-for-alpine is internal name of the network and it will be used inside of the docker-compose files and could be different,
test-db-net is external name of the network and must be same in two docker-compose files.
Assume we have docker-compose.db.yml and docker-compose.alpine.yml
docker-compose.apline.yml would be:
version: '3.8'
services:
alpine:
image: alpine:3.14
container_name: alpine
networks:
- net-for-alpine
# these two command keeps apline container running
stdin_open: true # docker run -i
tty: true # docker run -t
networks:
net-for-alpine:
name: test-db-net
docker-compose.db.yml would be:
version: '3.8'
services:
db:
image: postgres:13.4-alpine
container_name: psql
networks:
- net-for-db
networks:
net-for-db:
name: test-db-net
To test the network, go inside alpine container
docker exec -it alpine sh
then with following commands you can check the network
# if it returns 0 or see nothing as a result, network is established
nc -z psql (container name)
or
ping pgsql

Related

Docker compose networking between pods [duplicate]

I have two separate docker-compose.yml files in two different folders:
~/front/docker-compose.yml
~/api/docker-compose.yml
How can I make sure that a container in front can send requests to a container in api?
I know that --default-gateway option can be set using docker run for an individual container, so that a specific IP address can be assigned to this container, but it seems that this option is not available when using docker-compose.
Currently I end up doing a docker inspect my_api_container_id and look at the gateway in the output. It works but the problem is that this IP is randomly attributed, so I can't rely on it.
Another form of this question might thus be:
Can I attribute a fixed IP address to a particular container using docker-compose?
But in the end what I'm looking after is:
How can two different docker-compose projects communicate with each other?
You just need to make sure that the containers you want to talk to each other are on the same network. Networks are a first-class docker construct, and not specific to compose.
# front/docker-compose.yml
version: '2'
services:
front:
...
networks:
- some-net
networks:
some-net:
driver: bridge
...
# api/docker-compose.yml
version: '2'
services:
api:
...
networks:
- front_some-net
networks:
front_some-net:
external: true
Note: Your app’s network is given a name based on the “project name”, which is based on the name of the directory it lives in, in this case a prefix front_ was added
They can then talk to each other using the service name. From front you can do ping api and vice versa.
UPDATE: As of compose file version 3.5:
This now works:
version: "3.5"
services:
proxy:
image: hello-world
ports:
- "80:80"
networks:
- proxynet
networks:
proxynet:
name: custom_network
docker-compose up -d will join a network called 'custom_network'. If it doesn't exist, it will be created!
root#ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done
Now, you can do this:
version: "2"
services:
web:
image: hello-world
networks:
- my-proxy-net
networks:
my-proxy-net:
external:
name: custom_network
This will create a container that will be on the external network.
I can't find any reference in the docs yet but it works!
Just a small adittion to #johnharris85's great answer,
when you are running a docker compose file, a "default" network is created
so you can just add it to the other compose file as an external network:
# front/docker-compose.yml
version: '2'
services:
front_service:
...
...
# api/docker-compose.yml
version: '2'
services:
api_service:
...
networks:
- front_default
networks:
front_default:
external: true
For me this approach was more suited because I did not own the first docker-compose file and wanted to communicate with it.
All containers from api can join the front default network with following config:
# api/docker-compose.yml
...
networks:
default:
external:
name: front_default
See docker compose guide: using a pre existing network (see at the bottom)
The previous posts information is correct, but it does not have details on how to link containers, which should be connected as "external_links".
Hope this example make more clear to you:
Suppose you have app1/docker-compose.yml, with two services (svc11 and svc12), and app2/docker-compose.yml with two more services (svc21 and svc22) and suppose you need to connect in a crossed fashion:
svc11 needs to connect to svc22's container
svc21 needs to connect to svc11's container.
So the configuration should be like this:
this is app1/docker-compose.yml:
version: '2'
services:
svc11:
container_name: container11
[..]
networks:
- default # this network
- app2_default # external network
external_links:
- container22:container22
[..]
svc12:
container_name: container12
[..]
networks:
default: # this network (app1)
driver: bridge
app2_default: # external network (app2)
external: true
this is app2/docker-compose.yml:
version: '2'
services:
svc21:
container_name: container21
[..]
networks:
- default # this network (app2)
- app1_default # external network (app1)
external_links:
- container11:container11
[..]
svc22:
container_name: container22
[..]
networks:
default: # this network (app2)
driver: bridge
app1_default: # external network (app1)
external: true
Everybody has explained really well, so I'll add the necessary code with just one simple explanation.
Use a network created outside of docker-compose (an "external" network) with docker-compose version 3.5+.
Further explanation can be found here.
First docker-compose.yml file should define network with name giveItANamePlease as follows.
networks:
my-network:
name: giveItANamePlease
driver: bridge
The services of first docker-compose.yml file can use network as follows:
networks:
- my-network
In second docker-compose file, we need to proxy the network by using the network name which we have used in first docker-compose file, which in this case is giveItANamePlease:
networks:
my-proxy-net:
external:
name: giveItANamePlease
And now you can use my-proxy-net in services of a second docker-compose.yml file as follows.
networks:
- my-proxy-net
Since Compose 1.18 (spec 3.5), you can just override the default network using your own custom name for all Compose YAML files you need. It is as simple as appending the following to them:
networks:
default:
name: my-app
The above assumes you have version set to 3.5 (or above if they don't deprecate it in 4+).
Other answers have pointed the same; this is a simplified summary.
UPDATE: As of docker-compose file version 3.5:
I came across a similar problem and I solved it by adding a small change in one of my docker-compose.yml project.
For instance, we have two API's scoring and ner. Scoring API needs to send a request to the ner API for processing the input request. In order to do that they both are supposed to share the same network.
Note: Every container has its own network which is automatically created at the time of running the app inside docker. For example ner API network will be created like ner_default and scoring API network will be named as scoring default. This solution will work for version: '3'.
As in the above scenario, my scoring API wants to communicate with ner API then I will add the following lines. This means Whenever I create the container for ner API then it automatically added to the scoring_default network.
networks:
default:
external:
name: scoring_default
ner/docker-compose.yml
version: '3'
services:
ner:
container_name: "ner_api"
build: .
...
networks:
default:
external:
name: scoring_default
scoring/docker-compose.yml
version: '3'
services:
api:
build: .
...
We can see this how the above containers are now a part of the same network called scoring_default using the command:
docker inspect scoring_default
{
"Name": "scoring_default",
....
"Containers": {
"14a6...28bf": {
"Name": "ner_api",
"EndpointID": "83b7...d6291",
"MacAddress": "0....",
"IPv4Address": "0.0....",
"IPv6Address": ""
},
"7b32...90d1": {
"Name": "scoring_api",
"EndpointID": "311...280d",
"MacAddress": "0.....3",
"IPv4Address": "1...0",
"IPv6Address": ""
},
...
}
You can add a .env file in all your projects containing COMPOSE_PROJECT_NAME=somename.
COMPOSE_PROJECT_NAME overrides the prefix used to name resources, as such all your projects will use somename_default as their network, making it possible for services to communicate with each other as they were in the same project.
NB: You'll get warnings for "orphaned" containers created from other projects.
So many answers!
First of all, avoid hyphens in entities names such as services and networks. They cause issues with name resolution.
Example: my-api won't work. myapi or api will work.
What worked for me is:
# api/docker-compose.yml
version: '3'
services:
api:
container_name: api
...
ports:
- 8081:8080
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
and
# front/docker-compose.yml
version: '3'
services:
front:
container_name: front
...
ports:
- 81:80
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
NOTE: I added ports to show how services can access each other, and how they are accessible from the host.
IMPORTANT: If you don't specify a network name, docker-compose will craft one for you. It uses the name of the folder the docker_compose.yml file is in. In this case: api_mynetwork and front_mynetwork. That will prevent communication between containers since they will by on different network, with very similar names.
Note that the network is defined exactly the same in both file, so you can start either service first and it will work. No need to specify which one is external, docker-compose will take care of managing that for you.
From the host
You can access either container using the published ports defined in docker-compose.yml.
You can access the Front container: curl http://localhost:81
You can access the API container: curl http://localhost:8081
From the API container
You can access the Front container using the original port, not the one you published in docker-compose.yml.
Example: curl http://front:80
From the Front container
You can access the API container using the original port, not the one you published in docker-compose.yml.
Example: curl http://api:8080
For using another docker-compose network you just do these(to share networks between docker-compose):
Run the first docker-compose project by up -d
Find the network name of the first docker-compose by: docker network ls(It contains the name of the root directory project)
Then use that name by this structure at below in the second docker-compose file.
second docker-compose.yml
version: '3'
services:
service-on-second-compose: # Define any names that you want.
.
.
.
networks:
- <put it here(the network name that comes from "docker network ls")>
networks:
- <put it here(the network name that comes from "docker network ls")>:
external: true
I would ensure all containers are docker-compose'd to the same network by composing them together at the same time, using:
docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d
If you are
trying to communicate between two containers from different docker-compose projects and don't want to use the same network (because let's say they would have PostgreSQL or Redis container on the same port and you would prefer to not changing these ports and not use it at the same network)
developing locally and want to imitate communication between two docker compose projects
running two docker-compose projects on localhost
developing especially Django apps or Django Rest Framework (drf) API and running app inside container on some exposed port
getting Connection refused while trying to communicate between two containers
And you want to
container api_a communicate to api_b (or vice versa) without the same "docker network"
(example below)
you can use "host" of the second container as IP of your computer and port that is mapped from inside Docker container. You can obtain IP of your computer with this script (from: Finding local IP addresses using Python's stdlib):
import socket
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except:
IP = '127.0.0.1'
finally:
s.close()
return IP
Example:
project_api_a/docker-compose.yml:
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_a
image: api_a:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_a container you are running Django app:
manage.py runserver 0.0.0.0:8000
and second docker-compose.yml from other project:
project_api_b/docker-compose-yml :
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_b
image: api_b:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_b container you are running Django app:
manage.py runserver 0.0.0.0:8001
And trying to connect from container api_a to api_b then URL of api_b container will be:
http://<get_ip_from_script_above>:8001/
It can be especially valuable if you are using even more than two(three or more) docker-compose projects and it's hard to provide common network for all of it - it's good workaround and solution
To connect two docker-compose you need a network and putting both docker-composes in that network,
you could create netwrok with docker network create name-of-network,
or you could simply put network declaration in networks option of docker-compose file and when you run docker-compose (docker-compose up) the network would be created automatically.
put the below lines in both docker-compose files
networks:
net-for-alpine:
name: test-db-net
Note: net-for-alpine is internal name of the network and it will be used inside of the docker-compose files and could be different,
test-db-net is external name of the network and must be same in two docker-compose files.
Assume we have docker-compose.db.yml and docker-compose.alpine.yml
docker-compose.apline.yml would be:
version: '3.8'
services:
alpine:
image: alpine:3.14
container_name: alpine
networks:
- net-for-alpine
# these two command keeps apline container running
stdin_open: true # docker run -i
tty: true # docker run -t
networks:
net-for-alpine:
name: test-db-net
docker-compose.db.yml would be:
version: '3.8'
services:
db:
image: postgres:13.4-alpine
container_name: psql
networks:
- net-for-db
networks:
net-for-db:
name: test-db-net
To test the network, go inside alpine container
docker exec -it alpine sh
then with following commands you could check the network
# if it returns 0 or see nothing as a result, network is established
nc -z psql (container name)
or
ping pgsql
I'm running multiple identical docker-compose.yml files in different directories, using .env files to make a slight difference. And use Nginx Proxy Manage to communicate with other services. here is my file:
make sure you have created a public network
docker network create nginx-proxy-man
/domain1.com/docker-compose.yml, /domain2.com/docker-compose.yml, ...
version: "3.9"
services:
webserver:
build:
context: ./bin/${PHPVERSION}
container_name: "${COMPOSE_PROJECT_NAME}-${PHPVERSION}"
...
networks:
- default # network outside
- internal # network internal
database:
build:
context: "./bin/${DATABASE}"
container_name: "${COMPOSE_PROJECT_NAME}-${DATABASE}"
...
networks:
- internal # network internal
networks:
default:
external: true
name: nginx-proxy-man
internal:
internal: true
.env file just change COMPOSE_PROJECT_NAME
COMPOSE_PROJECT_NAME=domain1_com
.
.
.
PHPVERSION=php56
DATABASE=mysql57
webserver.container_name: domain1_com-php56 - will join the default network (name: nginx-proxy-man), previously created for Nginx Proxy Manager to be accessible from the outside.
Note: container_name is unique in the same network.
database.container_name: domain1_com-mysql57 - easier to distinguish
In the same docker-compose.yml, the services will connect to each other via the service name because of the same network domain1_com_internal. And to be more secure, set this network with the option internal: true
Note, if you don't explicitly specify networks for each service, but just use a common external network for both docker-compose.yml, then it's likely that domain1_com will use domain2_com's database.
Another option is just running up the first module with the 'docker-compose' check the ip related with the module, and connect the second module with the previous net like external, and pointing the internal ip
example
app1 - new-network created in the service lines, mark as external: true at the bottom
app2 - indicate the "new-network" created by app1 when goes up, mark as external: true at the bottom, and set in the config to connect, the ip that app1 have in this net.
With this, you should be able to talk with each other
*this way is just for local-test focus, in order to don't do an over complex configuration
** I know is very 'patch way' but works for me and I think is so simple some other can take advantage of this
Answer for Docker Compose '3' and up
By default Docker Compose uses a bridge network to provision inter-container communication. Read this article for more info about inter-container networking.
What matters for you, is that by default Docker Compose creates a hostname that equals the service name in the docker-compose.yml file. Consider the following docker-compose.yml:
version: '3.9'
services:
server:
image: node:16.9.0
container_name: server
tty: true
stdin_open: true
depends_on:
- mongo
command: bash
mongo:
image: mongo
environment:
MONGO_INITDB_DATABASE: my-database
When you run docker-compose up, Docker will create a default network and assigns the service name as hostname for both mongo and server.
You can now access the backend container via:
docker exec -it server bash
And now you can ping the mongo container using Dockers internal network (default on port 27017 in this case):
curl -v http://mongo:27017/my-database
That's it. The same applies for your setup.
I have had a similar example where I was working with separate docker-compose files working on a docker swarm with an overlay network to do that all I had to do is change the networks parameters as so:
first docker-compose.yaml
version: '3.9'
.
.
.
networks:
net:
driver: overlay
attachable: true
docker-compose -p app up
since I have specified the app name as app using -p the initial network will be app_net.
Now in order to run another docker-compose with multiple services that will use the same network you will need to set these as so:
second docker-compose.yaml
version: '3.9'
.
.
.
networks:
net-ref:
external: true
name: app_net
docker stack deploy -c docker-compose.yml mystack
No matter what name you give to the stack the network will not be affected and will always refer to the existing external network called app_net.
PS: It's important to make sure to check your docker-compose version.
version: '2'
services:
bot:
build: .
volumes:
- '.:/home/node'
- /home/node/node_modules
networks:
- my-rede
mem_limit: 100m
memswap_limit: 100m
cpu_quota: 25000
container_name: 236948199393329152_585042339404185600_bot
command: node index.js
environment:
NODE_ENV: production
networks:
my-rede:
external:
name: name_rede_externa
Follow up of JohnHarris answer, just adding some more details which may be useful to someone: Lets take two docker-compose file and connect them through networks:
1st foldername/docker-compose.yml:
version: '2'
services:
some-contr:
container_name: []
build: .
...
networks:
- somenet
ports:
- "8080:8080"
expose:
# Opens port 8080 on the container
- "8080"
environment:
PORT: 8080
tty: true
networks:
boomnet:
driver: bridge
2nd docker-compose.yml:
version: '2'
services:
pushapiserver:
container_name: [container_name]
build: .
command: "tail -f /dev/null"
volumes:
- ./:/[work_dir]
working_dir: /[work dir]
image: [name of image]
ports:
- "8060:8066"
environment:
PORT: 8066
tty: true
networks:
- foldername_somenet
networks:
foldername_somenet:
external: true
Now you can make api calls to one another services(b/w diff containers)like:
http://pushapiserver:8066/send_push call from some code in files for 1st docker-compose.yml
Two common mistakes (atleast i made them few times):
take note of [foldername] in which your docker-compose.yml file is present. Please see above in 2nd docker-compose.yml i have added foldername in network bc docker create network by [foldername]_[networkname]
Port: this one is very common. Please note i have used 8066 when trying to make connection i.e. http://pushapiserver:8066/... 8066 is port of docker container(2nd docker-compose.yml) so when trying to talk with different docker compose.
docker will use docker container port[8066] and not host machine mapped port
[8060]

How to unset network mode in an overriding docker compose file?

Consider a part of some base docker-compose.yml file:
services:
foo:
image: bar
network_mode: host
.
.
.
Then, consider a docker-compose.prod.yml file which would override the base file's network mode and also set ports:
services:
foo:
ports:
- 'xxxx:yyyy'
network_mode: ?
I am looking for a value of ? such that the network_mode is considered unset. In other words, setting it to none or bridge doesn't seem to work, so I want it to just disappear, or use a value with such an effect (I don't think there is a default).
An alternative solution to this problem is to define three docker-compose files: docker-compose.yml, docker-compose.prod.yml, and docker-compose.dev.yml (or something equivalent, doesn't matter). It works fine (see below), but I would rather have 2 files only, and override the dev file with the prod file, rather than the other way around (it feels more natural this way).
A working version using three files:
docker-compose.yml
services:
foo:
image: bar
.
.
.
docker-compose.dev.yml
services:
foo:
network_mode: host
docker-compose.prod.yml
services:
foo:
ports:
- 'xxxx:yyyy'
Notes:
All files are using docker-compose version 3.
The specific setup which doesn't work with bridge network mode in my case is a collection of three services - one for running web stuff (exposed to public), one for celery workers (internal), and one for Redis (internal). Using bridge in web and/or celery results in being unable to connect to the Redis service.
I don't know how to remove a option from former docker-compose.yaml, but the bridge really works, you may want to have a double confirm.
If we have a look for docker default network, you could see bridge really there, if we do not set --net, default docker will use bridge:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
32a9a31082ae bridge bridge local
dcb12f4cc711 host host local
16c73acab8c9 none null local
I give a small example to prove it:
docker-compose.yaml:
version: "3"
services:
web:
image: python:3.7
network_mode: host
command: python -m http.server 9000
docker-compose.prod.yaml:
version: "3"
services:
web:
ports:
- 10000:9000
network_mode: bridge
docker-compose up
With this command, we will in fact just use docker-compose.yaml, at that time, if we open browser we can see could successfully visit e.g.: http://$ip:9000, this is because the network is host.
docker-compose -f docker-compose.yaml -f docker-compose.prod.yaml up
With this command, we will use 2 compose files, the latter one will override the duplicate option in the former one.
If you open browser, you will find you can't visit http://$ip/9000 now, you could just visit http://$ip/10000. This can prove that host loss effect, and the bridge override host successfully.
(NOTE: you will have to assure docker-compose.prod.yaml after docker-compose.yaml, the sequence order is important.)
UPDATE 20211017 based on your new comments:
If you want to addtional visit other container in your compose, you will have to define network_mode in that target service to use the same network of source service with network_mode: service:web, see network mode:
docker-compose.yaml:
version: "3"
services:
web:
image: python:3.7-alpine3.13
command: python -m http.server 9000
network_mode: host
db:
image: python:3.7-alpine3.13
command: python -m http.server 8000
network_mode: service:web
docker-compose.yaml:
version: "3"
services:
web:
ports:
- 10000:9000
network_mode: bridge
Then, after up, you could use something like next to directly visit the service in db:
docker-compose exec web wget http://localhost:8000 -O -
NOTE:
with network_mode: bridge in web, the web container nolonger use the default network which compose set up for you. As a result, you won't benifit from the auto dns setup by compose, which means in web you won't be able to access db container using the service name db.
to conquer this issue, you now could let db container use the network of web explicitly with network_mode: service:web. This means the 2 containers now share the same network namespace, then you no need to visit db with service name, but to use localhost. In web, you now could access db's 8000 port just with http://localhost:8000.
TL;DR
docker-compose.prod.yml
services:
foo:
network_mode: unset # Can be any string other than 'host'.
networks: [ default ]
ports: [ 80:80 ]
Docker Compose Default Network
This is the default behavior: if there's no networks: and no network_mode: defined for the service then it's attached to the default network.
Unfortunately an empty string is ignored by the YAML parser, so we can't override network_mode: back to '', what we can do is set networks: [ default ], but that won't work with network_mode: host so what can we set network_mode: to?
network_mode: is equivalent to Docker --network, if we set it to a network name networks: will override it, so we can use any value other than host which apparently triggers some special behavior. It's probably a good idea to set it to something that will fail without networks: and avoid default, bridge or none.
To restore the default behavior we need to add default to networks: and set network_mode: to something other than host.
Note that network_mode: default is equivalent to --network default or --network bridge which uses the default Docker network: bridge, and not the default project-wide Compose network: projectName_default.

CURL different docker-compose [duplicate]

I have two separate docker-compose.yml files in two different folders:
~/front/docker-compose.yml
~/api/docker-compose.yml
How can I make sure that a container in front can send requests to a container in api?
I know that --default-gateway option can be set using docker run for an individual container, so that a specific IP address can be assigned to this container, but it seems that this option is not available when using docker-compose.
Currently I end up doing a docker inspect my_api_container_id and look at the gateway in the output. It works but the problem is that this IP is randomly attributed, so I can't rely on it.
Another form of this question might thus be:
Can I attribute a fixed IP address to a particular container using docker-compose?
But in the end what I'm looking after is:
How can two different docker-compose projects communicate with each other?
You just need to make sure that the containers you want to talk to each other are on the same network. Networks are a first-class docker construct, and not specific to compose.
# front/docker-compose.yml
version: '2'
services:
front:
...
networks:
- some-net
networks:
some-net:
driver: bridge
...
# api/docker-compose.yml
version: '2'
services:
api:
...
networks:
- front_some-net
networks:
front_some-net:
external: true
Note: Your app’s network is given a name based on the “project name”, which is based on the name of the directory it lives in, in this case a prefix front_ was added
They can then talk to each other using the service name. From front you can do ping api and vice versa.
UPDATE: As of compose file version 3.5:
This now works:
version: "3.5"
services:
proxy:
image: hello-world
ports:
- "80:80"
networks:
- proxynet
networks:
proxynet:
name: custom_network
docker-compose up -d will join a network called 'custom_network'. If it doesn't exist, it will be created!
root#ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done
Now, you can do this:
version: "2"
services:
web:
image: hello-world
networks:
- my-proxy-net
networks:
my-proxy-net:
external:
name: custom_network
This will create a container that will be on the external network.
I can't find any reference in the docs yet but it works!
Just a small adittion to #johnharris85's great answer,
when you are running a docker compose file, a "default" network is created
so you can just add it to the other compose file as an external network:
# front/docker-compose.yml
version: '2'
services:
front_service:
...
...
# api/docker-compose.yml
version: '2'
services:
api_service:
...
networks:
- front_default
networks:
front_default:
external: true
For me this approach was more suited because I did not own the first docker-compose file and wanted to communicate with it.
All containers from api can join the front default network with following config:
# api/docker-compose.yml
...
networks:
default:
external:
name: front_default
See docker compose guide: using a pre existing network (see at the bottom)
The previous posts information is correct, but it does not have details on how to link containers, which should be connected as "external_links".
Hope this example make more clear to you:
Suppose you have app1/docker-compose.yml, with two services (svc11 and svc12), and app2/docker-compose.yml with two more services (svc21 and svc22) and suppose you need to connect in a crossed fashion:
svc11 needs to connect to svc22's container
svc21 needs to connect to svc11's container.
So the configuration should be like this:
this is app1/docker-compose.yml:
version: '2'
services:
svc11:
container_name: container11
[..]
networks:
- default # this network
- app2_default # external network
external_links:
- container22:container22
[..]
svc12:
container_name: container12
[..]
networks:
default: # this network (app1)
driver: bridge
app2_default: # external network (app2)
external: true
this is app2/docker-compose.yml:
version: '2'
services:
svc21:
container_name: container21
[..]
networks:
- default # this network (app2)
- app1_default # external network (app1)
external_links:
- container11:container11
[..]
svc22:
container_name: container22
[..]
networks:
default: # this network (app2)
driver: bridge
app1_default: # external network (app1)
external: true
Everybody has explained really well, so I'll add the necessary code with just one simple explanation.
Use a network created outside of docker-compose (an "external" network) with docker-compose version 3.5+.
Further explanation can be found here.
First docker-compose.yml file should define network with name giveItANamePlease as follows.
networks:
my-network:
name: giveItANamePlease
driver: bridge
The services of first docker-compose.yml file can use network as follows:
networks:
- my-network
In second docker-compose file, we need to proxy the network by using the network name which we have used in first docker-compose file, which in this case is giveItANamePlease:
networks:
my-proxy-net:
external:
name: giveItANamePlease
And now you can use my-proxy-net in services of a second docker-compose.yml file as follows.
networks:
- my-proxy-net
Since Compose 1.18 (spec 3.5), you can just override the default network using your own custom name for all Compose YAML files you need. It is as simple as appending the following to them:
networks:
default:
name: my-app
The above assumes you have version set to 3.5 (or above if they don't deprecate it in 4+).
Other answers have pointed the same; this is a simplified summary.
UPDATE: As of docker-compose file version 3.5:
I came across a similar problem and I solved it by adding a small change in one of my docker-compose.yml project.
For instance, we have two API's scoring and ner. Scoring API needs to send a request to the ner API for processing the input request. In order to do that they both are supposed to share the same network.
Note: Every container has its own network which is automatically created at the time of running the app inside docker. For example ner API network will be created like ner_default and scoring API network will be named as scoring default. This solution will work for version: '3'.
As in the above scenario, my scoring API wants to communicate with ner API then I will add the following lines. This means Whenever I create the container for ner API then it automatically added to the scoring_default network.
networks:
default:
external:
name: scoring_default
ner/docker-compose.yml
version: '3'
services:
ner:
container_name: "ner_api"
build: .
...
networks:
default:
external:
name: scoring_default
scoring/docker-compose.yml
version: '3'
services:
api:
build: .
...
We can see this how the above containers are now a part of the same network called scoring_default using the command:
docker inspect scoring_default
{
"Name": "scoring_default",
....
"Containers": {
"14a6...28bf": {
"Name": "ner_api",
"EndpointID": "83b7...d6291",
"MacAddress": "0....",
"IPv4Address": "0.0....",
"IPv6Address": ""
},
"7b32...90d1": {
"Name": "scoring_api",
"EndpointID": "311...280d",
"MacAddress": "0.....3",
"IPv4Address": "1...0",
"IPv6Address": ""
},
...
}
You can add a .env file in all your projects containing COMPOSE_PROJECT_NAME=somename.
COMPOSE_PROJECT_NAME overrides the prefix used to name resources, as such all your projects will use somename_default as their network, making it possible for services to communicate with each other as they were in the same project.
NB: You'll get warnings for "orphaned" containers created from other projects.
So many answers!
First of all, avoid hyphens in entities names such as services and networks. They cause issues with name resolution.
Example: my-api won't work. myapi or api will work.
What worked for me is:
# api/docker-compose.yml
version: '3'
services:
api:
container_name: api
...
ports:
- 8081:8080
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
and
# front/docker-compose.yml
version: '3'
services:
front:
container_name: front
...
ports:
- 81:80
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
NOTE: I added ports to show how services can access each other, and how they are accessible from the host.
IMPORTANT: If you don't specify a network name, docker-compose will craft one for you. It uses the name of the folder the docker_compose.yml file is in. In this case: api_mynetwork and front_mynetwork. That will prevent communication between containers since they will by on different network, with very similar names.
Note that the network is defined exactly the same in both file, so you can start either service first and it will work. No need to specify which one is external, docker-compose will take care of managing that for you.
From the host
You can access either container using the published ports defined in docker-compose.yml.
You can access the Front container: curl http://localhost:81
You can access the API container: curl http://localhost:8081
From the API container
You can access the Front container using the original port, not the one you published in docker-compose.yml.
Example: curl http://front:80
From the Front container
You can access the API container using the original port, not the one you published in docker-compose.yml.
Example: curl http://api:8080
For using another docker-compose network you just do these(to share networks between docker-compose):
Run the first docker-compose project by up -d
Find the network name of the first docker-compose by: docker network ls(It contains the name of the root directory project)
Then use that name by this structure at below in the second docker-compose file.
second docker-compose.yml
version: '3'
services:
service-on-second-compose: # Define any names that you want.
.
.
.
networks:
- <put it here(the network name that comes from "docker network ls")>
networks:
- <put it here(the network name that comes from "docker network ls")>:
external: true
I would ensure all containers are docker-compose'd to the same network by composing them together at the same time, using:
docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d
If you are
trying to communicate between two containers from different docker-compose projects and don't want to use the same network (because let's say they would have PostgreSQL or Redis container on the same port and you would prefer to not changing these ports and not use it at the same network)
developing locally and want to imitate communication between two docker compose projects
running two docker-compose projects on localhost
developing especially Django apps or Django Rest Framework (drf) API and running app inside container on some exposed port
getting Connection refused while trying to communicate between two containers
And you want to
container api_a communicate to api_b (or vice versa) without the same "docker network"
(example below)
you can use "host" of the second container as IP of your computer and port that is mapped from inside Docker container. You can obtain IP of your computer with this script (from: Finding local IP addresses using Python's stdlib):
import socket
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except:
IP = '127.0.0.1'
finally:
s.close()
return IP
Example:
project_api_a/docker-compose.yml:
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_a
image: api_a:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_a container you are running Django app:
manage.py runserver 0.0.0.0:8000
and second docker-compose.yml from other project:
project_api_b/docker-compose-yml :
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_b
image: api_b:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_b container you are running Django app:
manage.py runserver 0.0.0.0:8001
And trying to connect from container api_a to api_b then URL of api_b container will be:
http://<get_ip_from_script_above>:8001/
It can be especially valuable if you are using even more than two(three or more) docker-compose projects and it's hard to provide common network for all of it - it's good workaround and solution
To connect two docker-compose you need a network and putting both docker-composes in that network,
you could create netwrok with docker network create name-of-network,
or you could simply put network declaration in networks option of docker-compose file and when you run docker-compose (docker-compose up) the network would be created automatically.
put the below lines in both docker-compose files
networks:
net-for-alpine:
name: test-db-net
Note: net-for-alpine is internal name of the network and it will be used inside of the docker-compose files and could be different,
test-db-net is external name of the network and must be same in two docker-compose files.
Assume we have docker-compose.db.yml and docker-compose.alpine.yml
docker-compose.apline.yml would be:
version: '3.8'
services:
alpine:
image: alpine:3.14
container_name: alpine
networks:
- net-for-alpine
# these two command keeps apline container running
stdin_open: true # docker run -i
tty: true # docker run -t
networks:
net-for-alpine:
name: test-db-net
docker-compose.db.yml would be:
version: '3.8'
services:
db:
image: postgres:13.4-alpine
container_name: psql
networks:
- net-for-db
networks:
net-for-db:
name: test-db-net
To test the network, go inside alpine container
docker exec -it alpine sh
then with following commands you could check the network
# if it returns 0 or see nothing as a result, network is established
nc -z psql (container name)
or
ping pgsql
I'm running multiple identical docker-compose.yml files in different directories, using .env files to make a slight difference. And use Nginx Proxy Manage to communicate with other services. here is my file:
make sure you have created a public network
docker network create nginx-proxy-man
/domain1.com/docker-compose.yml, /domain2.com/docker-compose.yml, ...
version: "3.9"
services:
webserver:
build:
context: ./bin/${PHPVERSION}
container_name: "${COMPOSE_PROJECT_NAME}-${PHPVERSION}"
...
networks:
- default # network outside
- internal # network internal
database:
build:
context: "./bin/${DATABASE}"
container_name: "${COMPOSE_PROJECT_NAME}-${DATABASE}"
...
networks:
- internal # network internal
networks:
default:
external: true
name: nginx-proxy-man
internal:
internal: true
.env file just change COMPOSE_PROJECT_NAME
COMPOSE_PROJECT_NAME=domain1_com
.
.
.
PHPVERSION=php56
DATABASE=mysql57
webserver.container_name: domain1_com-php56 - will join the default network (name: nginx-proxy-man), previously created for Nginx Proxy Manager to be accessible from the outside.
Note: container_name is unique in the same network.
database.container_name: domain1_com-mysql57 - easier to distinguish
In the same docker-compose.yml, the services will connect to each other via the service name because of the same network domain1_com_internal. And to be more secure, set this network with the option internal: true
Note, if you don't explicitly specify networks for each service, but just use a common external network for both docker-compose.yml, then it's likely that domain1_com will use domain2_com's database.
Another option is just running up the first module with the 'docker-compose' check the ip related with the module, and connect the second module with the previous net like external, and pointing the internal ip
example
app1 - new-network created in the service lines, mark as external: true at the bottom
app2 - indicate the "new-network" created by app1 when goes up, mark as external: true at the bottom, and set in the config to connect, the ip that app1 have in this net.
With this, you should be able to talk with each other
*this way is just for local-test focus, in order to don't do an over complex configuration
** I know is very 'patch way' but works for me and I think is so simple some other can take advantage of this
Answer for Docker Compose '3' and up
By default Docker Compose uses a bridge network to provision inter-container communication. Read this article for more info about inter-container networking.
What matters for you, is that by default Docker Compose creates a hostname that equals the service name in the docker-compose.yml file. Consider the following docker-compose.yml:
version: '3.9'
services:
server:
image: node:16.9.0
container_name: server
tty: true
stdin_open: true
depends_on:
- mongo
command: bash
mongo:
image: mongo
environment:
MONGO_INITDB_DATABASE: my-database
When you run docker-compose up, Docker will create a default network and assigns the service name as hostname for both mongo and server.
You can now access the backend container via:
docker exec -it server bash
And now you can ping the mongo container using Dockers internal network (default on port 27017 in this case):
curl -v http://mongo:27017/my-database
That's it. The same applies for your setup.
I have had a similar example where I was working with separate docker-compose files working on a docker swarm with an overlay network to do that all I had to do is change the networks parameters as so:
first docker-compose.yaml
version: '3.9'
.
.
.
networks:
net:
driver: overlay
attachable: true
docker-compose -p app up
since I have specified the app name as app using -p the initial network will be app_net.
Now in order to run another docker-compose with multiple services that will use the same network you will need to set these as so:
second docker-compose.yaml
version: '3.9'
.
.
.
networks:
net-ref:
external: true
name: app_net
docker stack deploy -c docker-compose.yml mystack
No matter what name you give to the stack the network will not be affected and will always refer to the existing external network called app_net.
PS: It's important to make sure to check your docker-compose version.
version: '2'
services:
bot:
build: .
volumes:
- '.:/home/node'
- /home/node/node_modules
networks:
- my-rede
mem_limit: 100m
memswap_limit: 100m
cpu_quota: 25000
container_name: 236948199393329152_585042339404185600_bot
command: node index.js
environment:
NODE_ENV: production
networks:
my-rede:
external:
name: name_rede_externa
Follow up of JohnHarris answer, just adding some more details which may be useful to someone: Lets take two docker-compose file and connect them through networks:
1st foldername/docker-compose.yml:
version: '2'
services:
some-contr:
container_name: []
build: .
...
networks:
- somenet
ports:
- "8080:8080"
expose:
# Opens port 8080 on the container
- "8080"
environment:
PORT: 8080
tty: true
networks:
boomnet:
driver: bridge
2nd docker-compose.yml:
version: '2'
services:
pushapiserver:
container_name: [container_name]
build: .
command: "tail -f /dev/null"
volumes:
- ./:/[work_dir]
working_dir: /[work dir]
image: [name of image]
ports:
- "8060:8066"
environment:
PORT: 8066
tty: true
networks:
- foldername_somenet
networks:
foldername_somenet:
external: true
Now you can make api calls to one another services(b/w diff containers)like:
http://pushapiserver:8066/send_push call from some code in files for 1st docker-compose.yml
Two common mistakes (atleast i made them few times):
take note of [foldername] in which your docker-compose.yml file is present. Please see above in 2nd docker-compose.yml i have added foldername in network bc docker create network by [foldername]_[networkname]
Port: this one is very common. Please note i have used 8066 when trying to make connection i.e. http://pushapiserver:8066/... 8066 is port of docker container(2nd docker-compose.yml) so when trying to talk with different docker compose.
docker will use docker container port[8066] and not host machine mapped port
[8060]

Docker-compose network link

Docker 'link' feature will be deprecated as new feature 'networking' has been released (link). I'm making docker-compose with some containers, and it was fine with 'link' to connect each others(without any other commands).
Since I need to change link configuration to network, I have to make docker network before 'docker-compose up'. Is there any docker-compose feature that making docker network automatically? Or any other way to connecting each containers with some configuration?
By default, docker-compose with a v2 yml will spin up a network for your project. Any networks you define will also be created unless you explicitly tell it otherwise. Here's an example docker-compose.yml:
version: '2'
networks:
dbnet:
appnet:
services:
db:
image: busybox
command: tail -f /dev/null
networks:
- dbnet
app:
image: busybox
command: tail -f /dev/null
networks:
- dbnet
- appnet
proxy:
image: busybox
command: tail -f /dev/null
ports:
- 80
networks:
- appnet
And then when you spin it up, you'll see that it creates the networks defined:
$ docker-compose up -d
Creating network "test_dbnet" with the default driver
Creating network "test_appnet" with the default driver
Creating test_app_1
Creating test_db_1
Creating test_proxy_1
Note that linking containers also created an implicit dependency, so you may want to use depends_on in your yml to be explicit in any dependencies after removing your link.
docker-compose creates a default network for your compose project on itself. You only have to migrate your compose projects to version: '2' or version: '3' of the compose yaml format. Please read how to upgrade for more information.
With version 2 and 3, you don't have to specify links anymore, as all services will be in the default network if you don't explicitly specify other networks.
UPDATE: To make 2 containers talk to each other, you can simply use the service names which will resolve to container IPs. Links are now only required if for some reason a container expects a specific name, e.g. because it is hardcoded.

Communication between multiple docker-compose projects

I have two separate docker-compose.yml files in two different folders:
~/front/docker-compose.yml
~/api/docker-compose.yml
How can I make sure that a container in front can send requests to a container in api?
I know that --default-gateway option can be set using docker run for an individual container, so that a specific IP address can be assigned to this container, but it seems that this option is not available when using docker-compose.
Currently I end up doing a docker inspect my_api_container_id and look at the gateway in the output. It works but the problem is that this IP is randomly attributed, so I can't rely on it.
Another form of this question might thus be:
Can I attribute a fixed IP address to a particular container using docker-compose?
But in the end what I'm looking after is:
How can two different docker-compose projects communicate with each other?
You just need to make sure that the containers you want to talk to each other are on the same network. Networks are a first-class docker construct, and not specific to compose.
# front/docker-compose.yml
version: '2'
services:
front:
...
networks:
- some-net
networks:
some-net:
driver: bridge
...
# api/docker-compose.yml
version: '2'
services:
api:
...
networks:
- front_some-net
networks:
front_some-net:
external: true
Note: Your app’s network is given a name based on the “project name”, which is based on the name of the directory it lives in, in this case a prefix front_ was added
They can then talk to each other using the service name. From front you can do ping api and vice versa.
UPDATE: As of compose file version 3.5:
This now works:
version: "3.5"
services:
proxy:
image: hello-world
ports:
- "80:80"
networks:
- proxynet
networks:
proxynet:
name: custom_network
docker-compose up -d will join a network called 'custom_network'. If it doesn't exist, it will be created!
root#ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done
Now, you can do this:
version: "2"
services:
web:
image: hello-world
networks:
- my-proxy-net
networks:
my-proxy-net:
external:
name: custom_network
This will create a container that will be on the external network.
I can't find any reference in the docs yet but it works!
Just a small adittion to #johnharris85's great answer,
when you are running a docker compose file, a "default" network is created
so you can just add it to the other compose file as an external network:
# front/docker-compose.yml
version: '2'
services:
front_service:
...
...
# api/docker-compose.yml
version: '2'
services:
api_service:
...
networks:
- front_default
networks:
front_default:
external: true
For me this approach was more suited because I did not own the first docker-compose file and wanted to communicate with it.
All containers from api can join the front default network with following config:
# api/docker-compose.yml
...
networks:
default:
external:
name: front_default
See docker compose guide: using a pre existing network (see at the bottom)
The previous posts information is correct, but it does not have details on how to link containers, which should be connected as "external_links".
Hope this example make more clear to you:
Suppose you have app1/docker-compose.yml, with two services (svc11 and svc12), and app2/docker-compose.yml with two more services (svc21 and svc22) and suppose you need to connect in a crossed fashion:
svc11 needs to connect to svc22's container
svc21 needs to connect to svc11's container.
So the configuration should be like this:
this is app1/docker-compose.yml:
version: '2'
services:
svc11:
container_name: container11
[..]
networks:
- default # this network
- app2_default # external network
external_links:
- container22:container22
[..]
svc12:
container_name: container12
[..]
networks:
default: # this network (app1)
driver: bridge
app2_default: # external network (app2)
external: true
this is app2/docker-compose.yml:
version: '2'
services:
svc21:
container_name: container21
[..]
networks:
- default # this network (app2)
- app1_default # external network (app1)
external_links:
- container11:container11
[..]
svc22:
container_name: container22
[..]
networks:
default: # this network (app2)
driver: bridge
app1_default: # external network (app1)
external: true
Everybody has explained really well, so I'll add the necessary code with just one simple explanation.
Use a network created outside of docker-compose (an "external" network) with docker-compose version 3.5+.
Further explanation can be found here.
First docker-compose.yml file should define network with name giveItANamePlease as follows.
networks:
my-network:
name: giveItANamePlease
driver: bridge
The services of first docker-compose.yml file can use network as follows:
networks:
- my-network
In second docker-compose file, we need to proxy the network by using the network name which we have used in first docker-compose file, which in this case is giveItANamePlease:
networks:
my-proxy-net:
external:
name: giveItANamePlease
And now you can use my-proxy-net in services of a second docker-compose.yml file as follows.
networks:
- my-proxy-net
Since Compose 1.18 (spec 3.5), you can just override the default network using your own custom name for all Compose YAML files you need. It is as simple as appending the following to them:
networks:
default:
name: my-app
The above assumes you have version set to 3.5 (or above if they don't deprecate it in 4+).
Other answers have pointed the same; this is a simplified summary.
UPDATE: As of docker-compose file version 3.5:
I came across a similar problem and I solved it by adding a small change in one of my docker-compose.yml project.
For instance, we have two API's scoring and ner. Scoring API needs to send a request to the ner API for processing the input request. In order to do that they both are supposed to share the same network.
Note: Every container has its own network which is automatically created at the time of running the app inside docker. For example ner API network will be created like ner_default and scoring API network will be named as scoring default. This solution will work for version: '3'.
As in the above scenario, my scoring API wants to communicate with ner API then I will add the following lines. This means Whenever I create the container for ner API then it automatically added to the scoring_default network.
networks:
default:
external:
name: scoring_default
ner/docker-compose.yml
version: '3'
services:
ner:
container_name: "ner_api"
build: .
...
networks:
default:
external:
name: scoring_default
scoring/docker-compose.yml
version: '3'
services:
api:
build: .
...
We can see this how the above containers are now a part of the same network called scoring_default using the command:
docker inspect scoring_default
{
"Name": "scoring_default",
....
"Containers": {
"14a6...28bf": {
"Name": "ner_api",
"EndpointID": "83b7...d6291",
"MacAddress": "0....",
"IPv4Address": "0.0....",
"IPv6Address": ""
},
"7b32...90d1": {
"Name": "scoring_api",
"EndpointID": "311...280d",
"MacAddress": "0.....3",
"IPv4Address": "1...0",
"IPv6Address": ""
},
...
}
You can add a .env file in all your projects containing COMPOSE_PROJECT_NAME=somename.
COMPOSE_PROJECT_NAME overrides the prefix used to name resources, as such all your projects will use somename_default as their network, making it possible for services to communicate with each other as they were in the same project.
NB: You'll get warnings for "orphaned" containers created from other projects.
So many answers!
First of all, avoid hyphens in entities names such as services and networks. They cause issues with name resolution.
Example: my-api won't work. myapi or api will work.
What worked for me is:
# api/docker-compose.yml
version: '3'
services:
api:
container_name: api
...
ports:
- 8081:8080
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
and
# front/docker-compose.yml
version: '3'
services:
front:
container_name: front
...
ports:
- 81:80
networks:
- mynetwork
networks:
mynetwork:
name: mynetwork
NOTE: I added ports to show how services can access each other, and how they are accessible from the host.
IMPORTANT: If you don't specify a network name, docker-compose will craft one for you. It uses the name of the folder the docker_compose.yml file is in. In this case: api_mynetwork and front_mynetwork. That will prevent communication between containers since they will by on different network, with very similar names.
Note that the network is defined exactly the same in both file, so you can start either service first and it will work. No need to specify which one is external, docker-compose will take care of managing that for you.
From the host
You can access either container using the published ports defined in docker-compose.yml.
You can access the Front container: curl http://localhost:81
You can access the API container: curl http://localhost:8081
From the API container
You can access the Front container using the original port, not the one you published in docker-compose.yml.
Example: curl http://front:80
From the Front container
You can access the API container using the original port, not the one you published in docker-compose.yml.
Example: curl http://api:8080
For using another docker-compose network you just do these(to share networks between docker-compose):
Run the first docker-compose project by up -d
Find the network name of the first docker-compose by: docker network ls(It contains the name of the root directory project)
Then use that name by this structure at below in the second docker-compose file.
second docker-compose.yml
version: '3'
services:
service-on-second-compose: # Define any names that you want.
.
.
.
networks:
- <put it here(the network name that comes from "docker network ls")>
networks:
- <put it here(the network name that comes from "docker network ls")>:
external: true
I would ensure all containers are docker-compose'd to the same network by composing them together at the same time, using:
docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d
If you are
trying to communicate between two containers from different docker-compose projects and don't want to use the same network (because let's say they would have PostgreSQL or Redis container on the same port and you would prefer to not changing these ports and not use it at the same network)
developing locally and want to imitate communication between two docker compose projects
running two docker-compose projects on localhost
developing especially Django apps or Django Rest Framework (drf) API and running app inside container on some exposed port
getting Connection refused while trying to communicate between two containers
And you want to
container api_a communicate to api_b (or vice versa) without the same "docker network"
(example below)
you can use "host" of the second container as IP of your computer and port that is mapped from inside Docker container. You can obtain IP of your computer with this script (from: Finding local IP addresses using Python's stdlib):
import socket
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except:
IP = '127.0.0.1'
finally:
s.close()
return IP
Example:
project_api_a/docker-compose.yml:
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_a
image: api_a:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_a container you are running Django app:
manage.py runserver 0.0.0.0:8000
and second docker-compose.yml from other project:
project_api_b/docker-compose-yml :
networks:
app-tier:
driver: bridge
services:
api:
container_name: api_b
image: api_b:latest
depends_on:
- postgresql
networks:
- app-tier
inside api_b container you are running Django app:
manage.py runserver 0.0.0.0:8001
And trying to connect from container api_a to api_b then URL of api_b container will be:
http://<get_ip_from_script_above>:8001/
It can be especially valuable if you are using even more than two(three or more) docker-compose projects and it's hard to provide common network for all of it - it's good workaround and solution
To connect two docker-compose you need a network and putting both docker-composes in that network,
you could create netwrok with docker network create name-of-network,
or you could simply put network declaration in networks option of docker-compose file and when you run docker-compose (docker-compose up) the network would be created automatically.
put the below lines in both docker-compose files
networks:
net-for-alpine:
name: test-db-net
Note: net-for-alpine is internal name of the network and it will be used inside of the docker-compose files and could be different,
test-db-net is external name of the network and must be same in two docker-compose files.
Assume we have docker-compose.db.yml and docker-compose.alpine.yml
docker-compose.apline.yml would be:
version: '3.8'
services:
alpine:
image: alpine:3.14
container_name: alpine
networks:
- net-for-alpine
# these two command keeps apline container running
stdin_open: true # docker run -i
tty: true # docker run -t
networks:
net-for-alpine:
name: test-db-net
docker-compose.db.yml would be:
version: '3.8'
services:
db:
image: postgres:13.4-alpine
container_name: psql
networks:
- net-for-db
networks:
net-for-db:
name: test-db-net
To test the network, go inside alpine container
docker exec -it alpine sh
then with following commands you could check the network
# if it returns 0 or see nothing as a result, network is established
nc -z psql (container name)
or
ping pgsql
I'm running multiple identical docker-compose.yml files in different directories, using .env files to make a slight difference. And use Nginx Proxy Manage to communicate with other services. here is my file:
make sure you have created a public network
docker network create nginx-proxy-man
/domain1.com/docker-compose.yml, /domain2.com/docker-compose.yml, ...
version: "3.9"
services:
webserver:
build:
context: ./bin/${PHPVERSION}
container_name: "${COMPOSE_PROJECT_NAME}-${PHPVERSION}"
...
networks:
- default # network outside
- internal # network internal
database:
build:
context: "./bin/${DATABASE}"
container_name: "${COMPOSE_PROJECT_NAME}-${DATABASE}"
...
networks:
- internal # network internal
networks:
default:
external: true
name: nginx-proxy-man
internal:
internal: true
.env file just change COMPOSE_PROJECT_NAME
COMPOSE_PROJECT_NAME=domain1_com
.
.
.
PHPVERSION=php56
DATABASE=mysql57
webserver.container_name: domain1_com-php56 - will join the default network (name: nginx-proxy-man), previously created for Nginx Proxy Manager to be accessible from the outside.
Note: container_name is unique in the same network.
database.container_name: domain1_com-mysql57 - easier to distinguish
In the same docker-compose.yml, the services will connect to each other via the service name because of the same network domain1_com_internal. And to be more secure, set this network with the option internal: true
Note, if you don't explicitly specify networks for each service, but just use a common external network for both docker-compose.yml, then it's likely that domain1_com will use domain2_com's database.
Another option is just running up the first module with the 'docker-compose' check the ip related with the module, and connect the second module with the previous net like external, and pointing the internal ip
example
app1 - new-network created in the service lines, mark as external: true at the bottom
app2 - indicate the "new-network" created by app1 when goes up, mark as external: true at the bottom, and set in the config to connect, the ip that app1 have in this net.
With this, you should be able to talk with each other
*this way is just for local-test focus, in order to don't do an over complex configuration
** I know is very 'patch way' but works for me and I think is so simple some other can take advantage of this
Answer for Docker Compose '3' and up
By default Docker Compose uses a bridge network to provision inter-container communication. Read this article for more info about inter-container networking.
What matters for you, is that by default Docker Compose creates a hostname that equals the service name in the docker-compose.yml file. Consider the following docker-compose.yml:
version: '3.9'
services:
server:
image: node:16.9.0
container_name: server
tty: true
stdin_open: true
depends_on:
- mongo
command: bash
mongo:
image: mongo
environment:
MONGO_INITDB_DATABASE: my-database
When you run docker-compose up, Docker will create a default network and assigns the service name as hostname for both mongo and server.
You can now access the backend container via:
docker exec -it server bash
And now you can ping the mongo container using Dockers internal network (default on port 27017 in this case):
curl -v http://mongo:27017/my-database
That's it. The same applies for your setup.
I have had a similar example where I was working with separate docker-compose files working on a docker swarm with an overlay network to do that all I had to do is change the networks parameters as so:
first docker-compose.yaml
version: '3.9'
.
.
.
networks:
net:
driver: overlay
attachable: true
docker-compose -p app up
since I have specified the app name as app using -p the initial network will be app_net.
Now in order to run another docker-compose with multiple services that will use the same network you will need to set these as so:
second docker-compose.yaml
version: '3.9'
.
.
.
networks:
net-ref:
external: true
name: app_net
docker stack deploy -c docker-compose.yml mystack
No matter what name you give to the stack the network will not be affected and will always refer to the existing external network called app_net.
PS: It's important to make sure to check your docker-compose version.
version: '2'
services:
bot:
build: .
volumes:
- '.:/home/node'
- /home/node/node_modules
networks:
- my-rede
mem_limit: 100m
memswap_limit: 100m
cpu_quota: 25000
container_name: 236948199393329152_585042339404185600_bot
command: node index.js
environment:
NODE_ENV: production
networks:
my-rede:
external:
name: name_rede_externa
Follow up of JohnHarris answer, just adding some more details which may be useful to someone: Lets take two docker-compose file and connect them through networks:
1st foldername/docker-compose.yml:
version: '2'
services:
some-contr:
container_name: []
build: .
...
networks:
- somenet
ports:
- "8080:8080"
expose:
# Opens port 8080 on the container
- "8080"
environment:
PORT: 8080
tty: true
networks:
boomnet:
driver: bridge
2nd docker-compose.yml:
version: '2'
services:
pushapiserver:
container_name: [container_name]
build: .
command: "tail -f /dev/null"
volumes:
- ./:/[work_dir]
working_dir: /[work dir]
image: [name of image]
ports:
- "8060:8066"
environment:
PORT: 8066
tty: true
networks:
- foldername_somenet
networks:
foldername_somenet:
external: true
Now you can make api calls to one another services(b/w diff containers)like:
http://pushapiserver:8066/send_push call from some code in files for 1st docker-compose.yml
Two common mistakes (atleast i made them few times):
take note of [foldername] in which your docker-compose.yml file is present. Please see above in 2nd docker-compose.yml i have added foldername in network bc docker create network by [foldername]_[networkname]
Port: this one is very common. Please note i have used 8066 when trying to make connection i.e. http://pushapiserver:8066/... 8066 is port of docker container(2nd docker-compose.yml) so when trying to talk with different docker compose.
docker will use docker container port[8066] and not host machine mapped port
[8060]

Resources