Yannick Pereira-Reis bio photo

Yannick Pereira-Reis

DevOps (docker swarm, haproxy, CI/CD, ELK, prometheus, grafana, ansible, automation, RabbitMQ, LVM, MySQL replication...) and fullstack web developer Symfony 2/3/4/5 + VueJs in Valence (France).

Twitter LinkedIn Github

I recently added a new service stack in my dev process with docker. This service stack is composed of many services I can use in different projects managed with docker-compose.

One of those services is mailcatcher and is usable with docker thanks to this image… or many others.

Docker

Docker-compose service layer

There are two ways I know to use this mailcatcher service inside many projetcs :

  • A mailcatcher container for each projet (docker-compose.yml):
app:
    build: docker/app
    links:
      - mailcatcher

mailcatcher:
    image: zolweb/docker-mailcatcher
  
  • A single mailcatcher container for all projects (catching all projects mails):
sudo docker run -d --name mailcatcher \
    zolweb/docker-mailcatcher

and inside your docker-compose.yml files :

app:
    build: docker/app
    external_links:
      - mailcatcher
  

Internally docker will automatically add new entry in the /etc/hosts file of each container using the mailcatcher service with the targeted container IP address:

root@mydomain:/var/www# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.7	mailcatcher mailcatcher_web_1

It allows you us to use this local domain name instead of the mailcatcher container IP address. Useful because this IP will change each time the service container is restarted.

The problem

Using links and external links can lead to problems. Indeed, sometimes (do not really know why or when) the container that as dependencies on other services, cannot reach the local host domains at startup.

I can see two reasons for this:

  • The service container is starting and the startup process of the main/app container is not finished yet.
  • The application you run (apache, nginx,…) starts before the /etc/hosts file has been updated internally by docker.

The solution

So far, the only solution I have to fix the problem is to wait for local domains to become reachable, thanks to a shell script (you need netcat/nc command installed):

Use this script in the CMD section of the Dockerfile or in a custom startup script.

You must set an environment variable to define hosts to wait for (with a specific format HOST:PORT):

  • db:3306
  • mainlatcher:1080
  • google.com:80
app:
    build: docker/app
    environment:
      WAIT_FOR_HOSTS: db:3306 mailcatcher:1080 google.com:80
    links:
      - db
    external_links:
      - mailcatcher

TIPS:

  • As you can see you can wait for an outside host to become available.
  • You can wait for links AND external_links.