99

I plan to split my monolthic server up into many small docker containers but haven't found a good solution for "inter-container communication" yet. This is my target scenario:

Target scenario

I know how to link containers together and how to expose ports, but none of these solutions are satisfying to me.

Is there any solution to communicate via hostnames (container names) between the containers like in a traditional server network?

Matthew Murdoch
  • 30,874
  • 30
  • 96
  • 127
Patrick Gotthard
  • 1,101
  • 1
  • 8
  • 10
  • I have written a [document](http://docs.blowb.org/overview.html) recently doing exactly what you are looking for. It basically documents how to install multiple containers (one per process) and make them integrated. "inter-container communication" is [part of the game](http://docs.blowb.org/setup-host/dnsmasq.html). – xuhdev May 30 '15 at 20:16
  • docker 1.10 is out, and docker connect is awesome (https://github.com/docker/docker/blob/98aa1d24a3d7274a8056e57f385945e9829bd612/docs/reference/commandline/network_connect.md). See [my edited answer below](http://stackoverflow.com/a/30546288/6309) – VonC Feb 05 '16 at 09:04
  • 2
    I think you should try [docker-compose](https://docs.docker.com/compose/gettingstarted/). Works very well. – Suhas Chikkanna Sep 06 '17 at 16:34
  • I just found the [Tumtum Blog](http://blog.tutum.co) and stumbled upon [this paragraph in the official Docker documentation](https://docs.docker.com/userguide/dockerlinks/#updating-the-etchosts-file). I don't know whether I had missed this paragraph all the time or whether it was newly added but that should be exactly what I need :) – Patrick Gotthard Jun 11 '15 at 17:53

5 Answers5

230

The new networking feature allows you to connect to containers by their name, so if you create a new network, any container connected to that network can reach other containers by their name. Example:

1) Create new network

$ docker network create <network-name>       

2) Connect containers to network

$ docker run --net=<network-name> ...

or

$ docker network connect <network-name> <container-name>

3) Ping container by name

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

See this section of the documentation;

Note: Unlike legacy links the new networking will not create environment variables, nor share environment variables with other containers.

This feature currently doesn't support aliases

Hemerson Varela
  • 24,034
  • 16
  • 68
  • 69
  • 8
    Works great. Why the default network does not enable this by default ?? – Stéphane Nov 07 '17 at 09:09
  • 1
    The less obvious part is that, you need to restart an app running in a different container. So how can container A make an app running in container B to restart? Clearly, there seems to a need for some sort of a communication bus. At the top of my mind is to use Redis for signalling and inter-container communication.. So all containers subscribe to the redis chanel and it will be there that they will talk... How about of changes in published ports in docker-compose.yml file requiring a complete `docker-compose down,up,restart` ? – daparic Jan 04 '18 at 11:09
  • this is exactly what i've been looking for all day! didn't know you can refer to a network node by it's container name/id. thank you! – elliotwesoff Jan 11 '18 at 17:14
  • 4
    @Stéphane It's disabled in the default `bridge` network because of backwards compatibility but yes, I agree, it _should_ definitely be enabled by default! – helmesjo Jan 18 '18 at 17:01
27

Edit: After Docker 1.9, the docker network command (see below https://stackoverflow.com/a/35184695/977939) is the recommended way to achieve this.


My solution is to set up a dnsmasq on the host to have DNS record automatically updated: "A" records have the names of containers and point to the IP addresses of the containers automatically (every 10 sec). The automatic updating script is pasted here:

#!/bin/bash

# 10 seconds interval time by default
INTERVAL=${INTERVAL:-10}

# dnsmasq config directory
DNSMASQ_CONFIG=${DNSMASQ_CONFIG:-.}

# commands used in this script
DOCKER=${DOCKER:-docker}
SLEEP=${SLEEP:-sleep}
TAIL=${TAIL:-tail}

declare -A service_map

while true
do
    changed=false
    while read line
    do
        name=${line##* }
        ip=$(${DOCKER} inspect --format '{{.NetworkSettings.IPAddress}}' $name)
        if [ -z ${service_map[$name]} ] || [ ${service_map[$name]} != $ip ] # IP addr changed
        then
            service_map[$name]=$ip
            # write to file
            echo $name has a new IP Address $ip >&2
            echo "host-record=$name,$ip"  > "${DNSMASQ_CONFIG}/docker-$name"
            changed=true
        fi
    done < <(${DOCKER} ps | ${TAIL} -n +2)

    # a change of IP address occured, restart dnsmasq
    if [ $changed = true ]
    then
        systemctl restart dnsmasq
    fi

    ${SLEEP} $INTERVAL
done

Make sure your dnsmasq service is available on docker0. Then, start your container with --dns HOST_ADDRESS to use this mini dns service.

Reference: http://docs.blowb.org/setup-host/dnsmasq.html

Community
  • 1
  • 1
xuhdev
  • 8,018
  • 2
  • 41
  • 69
  • That looks interesting, much resilient than my --link answer. +1 – VonC May 30 '15 at 20:29
  • @VonC looks like the new libnetwork may replace this workaround. Let's see though. – xuhdev May 30 '15 at 20:33
  • @xuhdev I setup dnsmasq as in http://docs.blowb.org/setup-host/dnsmasq.html. But i face issues when using dig from docker container, it time out. But ping to the host docker0 interface ip works. Also dig with the same docker0 ip from docker host works. Do you have any sugestions? – Satheesh Feb 04 '16 at 21:44
  • 1
    @Satheesh Maybe it's your firewall settings that prevent your container from querying DNS from the host? – xuhdev Feb 05 '16 at 01:14
  • @xuhdev thanks it is the firewalld in my host machine, which caused the issue. Once I stooped firewalld, my container communicates with dnsmasq on host – Satheesh Feb 05 '16 at 22:52
  • @xuhdev the mentioned script "while read line" returns empty for me when run as service, but when i run manually, im able to get docker container processes. + read line ++ docker ps ++ tail -n +2 Get http://%2Fvar%2Frun%2Fdocker.sock/v1.20/containers/json: EOF. * Are you trying to connect to a TLS-enabled daemon without TLS? * Is your docker daemon up and running? + name= – Satheesh Feb 07 '16 at 02:29
17

That should be what --link is for, at least for the hostname part.
With docker 1.10, and PR 19242, that would be:

docker network create --net-alias=[]: Add network-scoped alias for the container

(see last section below)

That is what Updating the /etc/hosts file details

In addition to the environment variables, Docker adds a host entry for the source container to the /etc/hosts file.

For instance, launch an LDAP server:

docker run -t  --name openldap -d -p 389:389 larrycai/openldap

And define an image to test that LDAP server:

FROM ubuntu
RUN apt-get -y install ldap-utils
RUN touch /root/.bash_aliases
RUN echo "alias lds='ldapsearch -H ldap://internalopenldap -LL -b
ou=Users,dc=openstack,dc=org -D cn=admin,dc=openstack,dc=org -w
password'" > /root/.bash_aliases
ENTRYPOINT bash

You can expose the 'openldap' container as 'internalopenldap' within the test image with --link:

 docker run -it --rm --name ldp --link openldap:internalopenldap ldaptest

Then, if you type 'lds', that alias will work:

ldapsearch -H ldap://internalopenldap ...

That would return people. Meaning internalopenldap is correctly reached from the ldaptest image.


Of course, docker 1.7 will add libnetwork, which provides a native Go implementation for connecting containers. See the blog post.
It introduced a more complete architecture, with the Container Network Model (CNM)

https://blog.docker.com/media/2015/04/cnm-model.jpg

That will Update the Docker CLI with new “network” commands, and document how the “-net” flag is used to assign containers to networks.


docker 1.10 has a new section Network-scoped alias, now officially documented in network connect:

While links provide private name resolution that is localized within a container, the network-scoped alias provides a way for a container to be discovered by an alternate name by any other container within the scope of a particular network.
Unlike the link alias, which is defined by the consumer of a service, the network-scoped alias is defined by the container that is offering the service to the network.

Continuing with the above example, create another container in isolated_nw with a network alias.

$ docker run --net=isolated_nw -itd --name=container6 -alias app busybox
8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17

--alias=[]         

Add network-scoped alias for the container

You can use --link option to link another container with a preferred alias

You can pause, restart, and stop containers that are connected to a network. Paused containers remain connected and can be revealed by a network inspect. When the container is stopped, it does not appear on the network until you restart it.

If specified, the container's IP address(es) is reapplied when a stopped container is restarted. If the IP address is no longer available, the container fails to start.

One way to guarantee that the IP address is available is to specify an --ip-range when creating the network, and choose the static IP address(es) from outside that range. This ensures that the IP address is not given to another container while this container is not on the network.

$ docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 multi-host-network

$ docker network connect --ip 172.20.128.2 multi-host-network container2
$ docker network connect --link container1:c1 multi-host-network container2
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 3
    The problem of --link is, that you are unable to restart a container without restarting the linked containers too. When you look at my graphic, restarting the MySQL container will result in a cascade of other container restarts. – Patrick Gotthard May 30 '15 at 14:49
3

EDIT : It is not bleeding edge anymore : http://blog.docker.com/2016/02/docker-1-10/

Original Answer
I battled with it the whole night. If you're not afraid of bleeding edge, the latest version of Docker engine and Docker compose both implement libnetwork.

With the right config file (that need to be put in version 2), you will create services that will all see each other. And, bonus, you can scale them with docker-compose as well (you can scale any service you want that doesn't bind port on the host)

Here is an example file

version: "2"
services:
  router:
    build: services/router/
    ports:
      - "8080:8080"
  auth:
    build: services/auth/
  todo:
    build: services/todo/
  data:
    build: services/data/

And the reference for this new version of compose file: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
  • 822
  • 9
  • 19
1

As far as I know, by using only Docker this is not possible. You need some DNS to map container ip:s to hostnames.

If you want out of the box solution. One solution is to use for example Kontena. It comes with network overlay technology from Weave and this technology is used to create virtual private LAN networks for each service and every service can be reached by service_name.kontena.local-address.

Here is simple example of Wordpress application's YAML file where Wordpress service connects to MySQL server with wordpress-mysql.kontena.local address:

wordpress:                                                                         
  image: wordpress:4.1                                                             
  stateful: true                                                                   
  ports:                                                                           
    - 80:80                                                                      
  links:                                                                           
    - mysql:wordpress-mysql                                                        
  environment:                                                                     
    - WORDPRESS_DB_HOST=wordpress-mysql.kontena.local                              
    - WORDPRESS_DB_PASSWORD=secret                                                 
mysql:                                                                             
  image: mariadb:5.5                                                               
  stateful: true                                                                   
  environment:                                                                     
    - MYSQL_ROOT_PASSWORD=secret
Lauri
  • 4,336
  • 3
  • 18
  • 18