Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Docker Tip #80: Waiting for Detached Containers to Finish

blog/cards/docker-tips-and-tricks.jpg

You can run multiple containers in the background and then wait until they all finish before moving on with your script.

This use case came up recently when I wanted to spin up 4 containers in detached mode and run a bunch of tests in parallel. These tests take about 5 minutes to complete but I didn’t know exactly when each one would finish.

I wanted to wait until they all finished, determine whether or not they passed and do different things based on that outcome. Those actions need to happen after all 4 containers finished running (successfully or not).

One approach could have been to create an infinite while loop with Bash and inspect the container’s status every second and break out of the loop when it finished but that’s a lot of book keeping and boiler plate code.

There’s a Unix tool called wait which will watch a process for any type of state change and report back the exit status of the process you’re watching.

Docker builds on top of that with docker container wait <container> which does the same thing and returns the exit code of the container.

(Bad) An example without using wait and a working loop:

If you want to follow along, create a docker-nowait.sh file and paste this in:

#!/bin/sh

# Run a container in the background that says Hello every second for 5s.
docker container run --rm -d \
  alpine:latest ash -c "for i in \`seq 1 5\`; do sleep 1; echo 'Hello'; done"

# Move onto the next command in the script.
echo "Time to do something important!"  

Run it with sh docker-nowait.sh. You’ll notice it reports back the container ID but then immediately prints the next line even though the loop in the container takes 5 seconds to complete. That’s not what we want.

(Bad) An example without using wait and a broken command:

If you want to follow along, create a docker-nowait-broken.sh file and paste this in:

#!/bin/sh

# Run a container in the background that runs an invalid command.
docker container run --rm -d alpine:latest ash -c "nowaythiscommandexists"

# This will always be 0, even if the thing we run in the container fails.
echo "Status code of last run command: ${?}"

Run it with sh docker-nowait-broken.sh. Notice how it reports the status code of 0, which is the docker container run command itself, not the command we ran in the container. Imagine if that were a test suite command. That’s also not what we want.

(Good) An example using wait with a working loop:

If you want to follow along, create a docker-wait.sh file and paste this in:

#!/bin/sh

# Run a container in the background that says Hello every second for 5s.
docker container run --rm --name testwait -d \
  alpine:latest ash -c "for i in \`seq 1 5\`; do sleep 1; echo 'Hello'; done"

# In the run command we named the container "testwait", so we're using it
# here. We could pass in the container ID instead too.
status_code="$(docker container wait testwait)"

# You won't see this message for 5 seconds, and the status code will be
# whatever the container exited with. It will be 0 in this case since the loop
# will exit successfully.
echo "Status code of last run command: ${status_code}"

Run it with sh docker-wait.sh. This time it will wait 5 seconds for the loop to finish and report back the real status code of what was run in the container.

If that loop were broken or you tried to run an invalid command, you would get a status code that wasn’t 0. Try it out on your own!

Free Intro to Docker Email Course

Over 5 days you'll get 1 email per day that includes video and text from the premium Dive Into Docker course. By the end of the 5 days you'll have hands on experience using Docker to serve a website.


Comments