Tricks of depends_on in docker-compose

Usually, I don’t like to write about bugs or flaws of docker-compose, cause I have hope, after short time it will be fixed, and this article will become outdated.
But a lot of people want to work with Docker-compose already now, and we all together step on the same rake. I want to tell you my Docker experience.
If we have only one container, and pack of services inside of it: nginx, php, postgresql or mysql, then you can’t understand Docker magic.
Such approach isn’t following to new trend: microservices, divide complex services to simple. Connect simple services between each other. Makes flexible.

Docker-compose

Whole magic of Docker mainly happening with two commands: “start” and “stop“. Is that docker-compose up and docker-compose down. Excute these commands in directory with file docker-compose.yml or any other yml describing docker services.

There are inconsistencies in the logic description of docker services by yml-file and real situation made Docker. For instance this yml file:

version: '2'

services:
  db:
    container_name: "rest_db"
    image: mysql:8.0
    volumes:
      - "app-db:/var/lib/mysql:rw"
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD = 12345
      - MYSQL_USER = symfony
      - MYSQL_PASSWORD = 12345
      - MYSQL_DATABASE = restapi
    networks:
      - mynet

  php:
    container_name: "rest_php"
    build: .
    command: /bin/sh -c "/app/app/config/docker/php-fpm-7/fixtures-mysql.sh"
    volumes:
      - "app-files:/app/web"
      - ./log/php-fpm:/var/log/php-fpm
      - ./app/config/docker/php-fpm-7/php-fpm.conf:/usr/local/etc/php-fpm.conf
      - ./app/config/docker/php-fpm-7/php.ini:/usr/local/etc/php/php.ini
      - ./web:/app/web
      - ./src:/app/src
    networks:
      - mynet
    depends_on:
      - db

  nginx:
    container_name: "rest_nginx"
    image: nginx:1.11.6-alpine
    dns:
      - 127.0.0.11
    volumes:
      - "app-files:/app/web"
      - ./app/config/docker/nginx/vhost.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php
    networks:
      - mynet
    ports:
      - "80:80"
    environment:
      - "PHP_FPM_HOST=php:9000"
      - "PHP_FPM_ROOT_DIR=/app/web"

networks:
  mynet:
    driver: bridge
    ipam:
      config:
      - subnet: 172.25.0.0/24

volumes:
  app-files: {}
  app-db: {}

Structure is it. There are services, networks and volumes.

In the networks I describe what exact IP subnet will be used. It’s not important, cause you can call services by hosts.
For example: db, php, nginx.

# ping db
PING db (172.25.0.2): 56 data bytes
64 bytes from 172.25.0.2: icmp_seq=0 ttl=64 time=0.068 ms
64 bytes from 172.25.0.2: icmp_seq=1 ttl=64 time=0.063 ms

# ping php
PING php (172.25.0.3): 56 data bytes
64 bytes from 172.25.0.3: icmp_seq=0 ttl=64 time=0.036 ms
64 bytes from 172.25.0.3: icmp_seq=1 ttl=64 time=0.046 ms

# ping nginx
PING nginx (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: icmp_seq=0 ttl=64 time=0.048 ms

Depends_on

In a lots of cases, building problems of docker-compose depends on our docker-tools skills and attention.
However, sometimes I find properties, which misleading functionality.
Let’s talk about depends_on, which honestly must be called run_it_also.

Nginx service depends on PHP service, and PHP service depends on DB. So, the chain of containers running must be following: DB -> PHP -> Nginx.

First container with DB service on 3306 or 5432 port. Then second one is PHP on 9000 port, and Nginx on 80 port.

An issue is depends_on don’t wait service-ready state, and just run containers in suggested in yml file order. However, we talk about services (not containers), so we mention order of Ready-to-work services. If launching services speed is different, then order of running — useless knowledge. In one case DB has been launched fast, in other one it was long-time initialize. Speed of start the same service can be different time to time.

Somebody throw in Docker documentation following “solution”:
https://docs.docker.com/compose/startup-order/
That’s example of wrong-way and negative experience.

But we still have no right way. We need to check availability of container with DB on Entrypoint or CMD. For MySQL I do it like this:

while ! curl –output /dev/null –silent –head –fail http://db:3306; do sleep 1 && echo -n .; done; echo “DB is UP!”;

and for PostgreSQL:

while ! curl -vs http://db:5432 2>&1 | grep HTTP ; do sleep 1 && echo -n .; done; echo DB is UP!;

https://github.com/onekit/rest-tutorial