According to Controlling startup order in Compose, one can control the order in which Docker Compose starts containers by using a "wait-for-it" script. Script wait-for-it.sh
expects both a host:port
argument as well as the command that the script should execute when the port is available. The documentation recommends that Docker Compose invoke this script using the entrypoint:
option. However, if one uses this option, the container will no longer run its default ENTRYPOINT
or CMD
because entrypoint:
overrides the default.
How might one provide this default command to wait-for-it.sh
so that the script can invoke the default ENTRYPOINT
or CMD
when the condition for which it waits is satisfied?
In my case, I've implemented a script wait-for-file.sh
that polls waiting for a file to exist:
#!/bin/bash set -e waitFile="$1" shift cmd="$@" until test -e $waitFile do >&2 echo "Waiting for file [$waitFile]." sleep 1 done >&2 echo "Found file [$waitFile]." exec $cmd
Docker Compose invokes wait-for-file.sh
as the entry-point to a slightly custom container derived from tomcat:8-jre8
:
platinum-oms: image: opes/platinum-oms ports: - "8080:8080" volumes_from: - liquibase links: - postgres:postgres - activemq:activemq depends_on: - liquibase - activemq entrypoint: /wait-for-file.sh /var/run/liquibase/done
Before it exits successfully, another custom container liquibase
creates /var/run/liquibase/done
and so platinum-oms
effectively waits for container liquibase
to complete.
Once container liquibase
creates file /var/run/liquibase/done
, wait-for-file.sh
prints Found file [/var/run/liquibase/done].
, but fails to invoke default command catalina.sh run
in base container tomcat:8-jre8
. Why?
I created a simplified test scenario docker-compose-wait-for-file
to demonstrate my problem. Container ubuntu-wait-for-file
waits for container ubuntu-create-file
to create file /wait/done
and then I expect container ubuntu-wait-for-file
to invoke the default ubuntu
container command /bin/bash
, but instead, it exits. Why doesn't it work as I expect?
ENTRYPOINT is the other instruction used to configure how the container will run. Just like with CMD, you need to specify a command and parameters. However, in the case of ENTRYPOINT we cannot override the ENTRYPOINT instruction by adding command-line parameters to the `docker run` command.
There's a good reason for this: The problem of waiting for a database (for example) to be ready is really just a subset of a much larger problem of distributed systems. In production, your database could become unavailable or move hosts at any time. Your application needs to be resilient to these types of failures.
Here we’ve reverted to only making Docker Compose wait for the api container to start. The web-app service accepts responsibility for checking whether api is healthy. It uses the Wait-for-It script to detect when the container is accessible on port 8080. Wait-for-It will then launch the web app container’s real command, defined as node app.js.
The dockerize tool gives you the ability to wait for services on a specified protocol ( file, tcp, tcp4, tcp6, http, https and unix) before starting your application: timeout. You can optionally specify how long to wait for the services to become available by using the -timeout # argument (Default: 10 seconds).
It permits to wait for a fixed amount of seconds and/or to wait until a TCP port is open on a target image. Like for the dockerize tool, you need to add the docker-compose-wait tool in your application Dockerfile.
The command sh -c “/wait && /sayhello” will run the wait tool and then your application, here /sayhello. When docker-compose is started (or Kubernetes or docker stack or whatever), your application will be started only when all the pairs host:port in the WAIT_HOSTS variable are available.
However, if one uses this option, the container will no longer run its default
ENTRYPOINT
orCMD
command becauseentrypoint:
overrides the default.
That is expected, which is why the wait-for-it
is presented as a wrapper script.
It does allow to execute a "subcommand" though:
wait-for-it.sh host:port [-s] [-t timeout] [-- command args] ^^^^^^^^^^^^
The subcommand will be executed regardless if the service is up or not.
If you wish to execute the subcommand only if the service is up, add the--strict
argument.
That means the CMD
part of your image can be used for your actual container command, as its parameters will passed in parameters to the ENTRYPOINT
command:
entrypoint: wait-for-it.sh host:port -- cmd: mycmd myargs
This should work... except for docker-compose
issue 3140 (mentioned by the OP Derek Mahar in the comments)
entrypoint defined in
docker-compose.yml
wipes outCMD
defined in Dockerfile
That issue suggests (Jan. 2021)
If you have a custom image you can add a startscript to the build and call it inside the dockerfile and in the
docker-compose
you can call it again.
Thats a way to avoid duplicate for more complicated entrypoints.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With