how to link docker container to each other with docker-compose
Asked Answered
A

5

33

I have to setup a mongo replica set with docker-compose. For the replica set the containers have to know each other.

I tried in docker-compose.yml

    dbreplicasetpart1:
      image: mongo:2.6.8
      expose:
        - '27018'
      links:
        - replicasetpart2
        - replicasetpart3
      cap_add:
        - NET_ADMIN

    dbreplicasetpart2:
      image: mongo:2.6.8
      links:
        - replicasetpart1
        - replicasetpart3
      expose:
        - '27019'
      cap_add:
        - NET_ADMIN
...

I get an circular import message. But if I remove the back-link to dbreplicasetpart1 I can't ping from dbreplicasetpart2 to dbreplicasetpart1. What is the solution?

Algonquian answered 27/3, 2015 at 18:23 Comment(1)
All the answers here predate the new Docker Networking introduced in Docker 1.9. What you do now is to create a network first and pass it to the docker-compose up. I'll add an example as an answer below.Solution
S
45

Updated for Docker 1.10

Docker 1.10 allows the definition of networks within the compose file. Here's the updated code

version: "2"

services:
  replica1:
    image: mongo:2.6.8
    container_name: replica1
    networks:
      - my-net
    ports:
      - "27018"
    environment:
      REPLICA2_URL: "http://replica2:27019"
  replica2:
    image: mongo:2.6.8
    container_name: replica2
    networks:
      - my-net
    ports:
      - "27019"
    environment:
      REPLICA1_URL: "http://replica1:27018"

networks:
  my-net:
    driver: bridge

Previous answer for Docker 1.9

As of Docker 1.9, the solution to this is to create a custom network and pass it to the docker-compose up command.

  1. Create a network docker network create --driver bridge my-net

  2. Reference that network as an environment variable (${NETWORK})in the docker-compose.yml files. Eg:

```

replica1:
  image: mongo:2.6.8
  container_name: replica1
  net: ${NETWORK}
  ports:
    - "27018"
  environment:
    REPLICA2_URL: "http://replica2:27019"

replica2:
  image: mongo:2.6.8
  container_name: replica2
  net: ${NETWORK}
  ports:
    - "27019"
  environment:
    REPLICA1_URL: "http://replica1:27018"

```

Note that replica1 in http://replica1:27018 will resolve to the ip address of the replica1 service (container). No need to hardcode ip addresses; An entry for replica1 is automatically added to the /etc/host of the replica2 container. Same goes for the replica1 container. Docker will add an entry for replica2 in its /etc/host file.

  1. Call docker-compose, passing it the network you created NETWORK=my-net docker-compose up -d -f docker-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.

Solution answered 27/12, 2015 at 1:18 Comment(2)
Due to the changes in Docker 1.9 this is the way better solution. Hence this should be the accepted answer IMHO.Bedivere
Trying to get the same thing working for mysql, have tried the above, maybe I'm missing something, can't get it working. Would greatly appreciate if you could take a look: #49059609Engagement
A
13

You should use the ambassador pattern:

https://docs.docker.com/engine/admin/ambassador_pattern_linking/

Basically you create an intermediate component that bridges both of them together. You can see an example that we use with Spring Cloud's Eureka discovery service:

ambassador:
  image: cpuguy83/docker-grand-ambassador
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"
  command: "-name eureka_1 -name eureka2_1 "

eureka:
  links:
    - "ambassador:eureka2"

eureka2:
  links:
    - "ambassador:eureka"

For simplicity, I only copied the links

Autobus answered 30/3, 2015 at 9:15 Comment(8)
With configuration like yours I got this error: ambassador_1 | time="2015-03-30T11:34:06Z" level=info msg="Error creating proxy for %s %s:%s - %vdockerfoo_dbreplicasetpart1_1172.17.0.17927017listen tcp 0.0.0.0:27017: bind: address already in use" ambassador_1 | time="2015-03-30T11:34:06Z" level=info msg="Error creating proxies for container dockerfoo_dbreplicasetpart1_1"Algonquian
Could you post your config with the ambassador? The hack below is not the best approach as the IP addresses might change in a different environment.Autobus
Miguel, we figured out to use your ambassador solution, see my config in the answer below.Algonquian
In a docker-compose environment, you have to adjust your -name arguments for docker-grand-ambassador, like @MichaelK. solution below shows: <COMPOSE_PROJECT_NAME>_<service name>_1, see docs.docker.com/compose/reference/overview and -p flag docs.docker.com/compose/reference/docker-composeAegaeon
This does not work in Mac OSX because Docker is running in a VM, so there's no access to /var/run/docker.sock. What would I do to solve this on Mac?Causeuse
I was told that mounting the docker socket /var/run/docker.sock to a container is inherently evil, as this would allow that container to control all local docker containers and implies a huge security hole.Celia
DO NOT USE THIS APPROACH FOR >=Docker 1.10 Take a look at the bridge network answerSassoon
doesn't work for me ERROR: Circular dependency between ambassador and ...Olcott
S
1

Without updating the docker-compose.yml file,

docker network connect docker-compose-network-you-want-to-connect conatianer-name-from-another-docker-compose

More here

Saltern answered 12/2, 2019 at 5:12 Comment(0)
A
0

We figured out to use the solution with the ambassador. It is indeed the more comfortable solution. The configuration that works for us:

amba1:
  image: cpuguy83/docker-grand-ambassador
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"
  command: "-name cucumber_dbreplicasetpart1_1"

amba2:
  image: cpuguy83/docker-grand-ambassador
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"
  command: "-name cucumber_dbreplicasetpart2_1"

amba3:
  image: cpuguy83/docker-grand-ambassador
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"
  command: "-name cucumber_dbreplicasetpart3_1"

dbreplicasetpart1:
  image: 'mongo:2.6.8'
  hostname: dbreplicasetpart1
  command: >
    bash -c
    '
      mongod --fork --logpath mongo.log --smallfiles --replSet rs1
      echo "
        printjson(
          rs.initiate(
            {
              _id : \"rs1\",
              members : [
                {_id : 0, host : \"dbreplicasetpart1:27017\"},
                {_id : 1, host : \"dbreplicasetpart2:27017\"},
                {_id : 2, host : \"dbreplicasetpart3:27017\"},
              ]
            }
          )
        );
      " | mongo;
      tail -f mongo.log
    '
  links:
    - "amba2:dbreplicasetpart2"
    - "amba3:dbreplicasetpart3"

dbreplicasetpart2:
  image: 'mongo:2.6.8'
  hostname: dbreplicasetpart2
  command: >
    bash -c
    '
      mongod --fork --logpath mongo.log --smallfiles --replSet rs1
      echo "
        printjson(
          rs.initiate(
            {
              _id : \"rs1\",
              members : [
                {_id : 0, host : \"dbreplicasetpart1:27017\"},
                {_id : 1, host : \"dbreplicasetpart2:27017\"},
                {_id : 2, host : \"dbreplicasetpart3:27017\"},
              ]
            }
          )
        );
      " | mongo;
      tail -f mongo.log
    '
  links:
    - "amba1:dbreplicasetpart1"
    - "amba3:dbreplicasetpart3"

dbreplicasetpart3:
  image: 'mongo:2.6.8'
  hostname: dbreplicasetpart3
  command: >
    bash -c
    '
      mongod --fork --logpath mongo.log --smallfiles --replSet rs1
      echo "
        printjson(
          rs.initiate(
            {
              _id : \"rs1\",
              members : [
                {_id : 0, host : \"dbreplicasetpart1:27017\"},
                {_id : 1, host : \"dbreplicasetpart2:27017\"},
                {_id : 2, host : \"dbreplicasetpart3:27017\"},
              ]
            }
          )
        );
      " | mongo;
      tail -f mongo.log
    '
  links:
    - "amba1:dbreplicasetpart1"
    - "amba2:dbreplicasetpart2"
Algonquian answered 13/4, 2015 at 8:33 Comment(0)
D
0

Here's what should still work in Docker 1.7.1 (in case you stuck with CentOS 6):

etcd:
  image: elcolio/etcd:latest
skydns:
  image: outrider/skydns
  links:
    - etcd
  environment:
    ETCD_MACHINES: "http://etcd:4001"
    SKYDNS_DOMAIN: "docker"
    SKYDNS_PATH_PREFIX: my
    SKYDNS_NDOTS: 0
    SKYDNS_VERBOSE: "true"
    SKYDNS_ADDR: 0.0.0.0:53
  expose:
    - 53

my-service:
    image: alpine
    command: sh -c "dns_add my-service && ping my-service"
    links:
      - etcd
      - skydns

dns_add script:

#!/usr/bin/env sh

# This script configures resov.conf to use
# "skydns" name server with "docker" domain
# and adds a service name specified in the first argument
SERVICE_NAME=$1

waitforit () {
  HOST=$1
  PORT=$2
  TIME_OUT=${3:-30};
  END=$(($(date "+%s+$TIME_OUT")))
  while [ $(date "+%s") -lt $END ]
    do nc -z -w1 $HOST $PORT && break
  done
  return $END
}

# Use skydns to resolve names
echo "nameserver `resolveip -s skydns`" > /etc/resolv.conf
echo "search docker" >> /etc/resolv.conf

# Put yourself to DNS
ETCD_HOST=etcd
ETCD_PORT=4001
waitforit $ETCD_HOST $ETCD_PORT
HOST_IP=`resolveip -s $HOSTNAME`
apk update && apk add curl
curl -XPUT http://$ETCD_HOST:$ETCD_PORT/v2/keys/my/docker/$SERVICE_NAME -d value="{\"host\":\"$HOST_IP\"}"

Here's an explanation:

  1. We set up our own DNS server in a container
  2. We configure our containers to use that server
  3. We configure that DNS server using special HTTP requests
Diamine answered 13/10, 2016 at 7:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.