When/If moving from Docker Compose files to Stack files in Docker Swarm, you might have problems solving dependencies and especially starting order as depends_on is not supported when deploying a stack in swarm mode. One solution is to arrange for your services to implement a network server and wait for this port to be opened and respond before going on with running a given service.
Let’s run roughly through the example of running grafana in a scalable way, for example using Redis and Postgres for, respectively, runtime and persistent data. To make this more complex, let’s suppose that the TOML configuration file of grafana is the result of another service. A regular Grafana Docker installation neither has an entrypoing, nor a run command as it is mostly configured using a combination of environment variables and the TOML configuration file. To arrange for the other services to be ready, you could have the following (snippet) entrypoint:
entrypoint: >-
wait-for.sh pg_grafana:5432 -t 120 -v --
wait-for.sh redis:6379 -t 120 -v --
wait-for.sh grafana-init:8080 -t 120 -v --
/run.sh
In that example, pg_grafana, redis and grafana-init are the name of the
services that implement (respectively) the postgres database, Redis and the
initialisation of the TOML configuration. Creating them is (almost) out of the
scope of this post… The implementation for
wait-for.sh
is available as a gist. It deviates slightly from the
original as it prefers nc for trying to
establish connection to remote servers, but is also able to use pure bash
constructs whenever bash is
available.
grafana-init is a bit special as it typically generates a configuration file
depending on a number of parameters. In a regular installation, such a service
would “die” once it had created the configuration file. However, instead it can
wait forever once it has performed its initialisation job. I have experimented
with relaying another external service with socat, as this is available in
most distributions. I typically call the TOML generator with a commane-line
option similar to -r 8080:icanhazip.com:80, you will notice that 8080 is the
port that the grafana service was waiting for. The implementation is as follows,
provided the content of the -r switch is contained in the variable RELAY.
Even if the remote service was not available, initialisation would still work as
socat would still respond on 8080 on incoming client requests.
if [ -n "$RELAY" ]; then
lport=$(echo "${RELAY}"|cut -d: -f1)
remote=$(echo "${RELAY}"|cut -d: -f2)
rport=$(echo "${RELAY}"|cut -d: -f3)
socat TCP4-LISTEN:${lport},su=nobody,fork,reuseaddr TCP4:${remote}:${rport}
fi