Docker compose dependency

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services. Then, with a single command, you create and start all the services from your configuration.

A simple example of this is a set of end-to-end tests for a web application. When the test suite begins to run, it is reasonable for it to assume that the web application that it is testing is up and running. Here is a docker-compose.yml file that models this.
#cat docker-compose.yml
version: '2'
services:
e2e_tests:
image: my_e2e_tests
depends_on:
- web
web:
image: my_web_image
When we run docker-compose my_e2e_tests, Docker Compose will start our e2e_tests and our web service.

Problem?
We have a race condition. Because both services start at the same time, it is possible that the e2e_tests attempt to initiate a connection to the web service before the web service is ready to accept connections

Solution
One workaround is to modify our e2e_tests startup script so that it has knowledge of this fact and does something about it. It's pretty trivial to add some "preload" block of code to e2e_tests startup code so that it tries to connect to the web service, then retries, and sleeps, and tries again and again until it succeeds. Although this will work, it's not ideal. The reason being is that it gives your tests knowledge of the fact that they live in some sort of containerized world where the services it depends on were literally just started and needs some time to warm up. You're adding more and more "stuff" to the container that it otherwise wouldn't have. When running something such as end-to-end tests, it would be nice to just assume that thing they're testing is running and accepting connections.

Let us first modify our existing docker-compose.yml file
#cat docker-compose.yml
version: '2'
services:
e2e_tests:
image: ubuntu:14.04
depends_on:
- web
command: 'nc -vz web 8080'
web:
image: ubuntu:14.04
command: >
/bin/bash -c "
sleep 5;
nc -lk 0.0.0.0 8080;
"
Let's run this command:
# docker-compose run e2e_tests
Creating network "depends_default" with the default driver
Pulling web (ubuntu:14.04)...
14.04: Pulling from library/ubuntu
2e6e20c8e2e6: Pull complete
0551a797c01d: Pull complete
512123a864da: Pull complete
Digest: sha256:4a8a6fa8810a3e01352981b35165b0b28403fe2a4e2535e315b23b4a69cd130a
Status: Downloaded newer image for ubuntu:14.04
Creating depends_web_1 ... done
Creating depends_e2e_tests_run ... done
nc: connect to web port 8080 (tcp) failed: Connection refused
ERROR: 1

Now that we have reliably simulated the race condition, we can add in our "port-checking" service. This service will wait for the relevant ports to open up before continuing, at which point we can be certain that our services that are expected to be running on those ports have been fully started and are ready to accept connections.

Add the following snippet to the bottom of your docker-compose.yml
cat docker-compose.yml
start_dependencies:
image: ubuntu:14.04
depends_on:
- web
command: >
/bin/bash -c "
while ! nc -z web 8080;
do
echo sleeping;
sleep 1;
done;
echo Connected!;
"
This service attempts to make a TCP connection to port 8080 of the web container and loops until it is successful, sleeping 1 second on each loop. Once successful, the loop terminates, the message "Connected!" is printed to the terminal, and the service terminates.
Docker Compose will ensure that the dependent service (web) remains running even after this container terminates. this means is that, immediately after running the start_dependencies service, e2e_tests can be started with certainty that the web is ready to accept connections right away.
#docker-compose run start_dependencies
#docker-compose run e2e_tests
Connection to web 8080 port [tcp/http-alt] succeeded




Recent Comments

No comments

Leave a Comment