Reputation: 2478
I have 3 types of components in my docker app:
When trying to run all of these services in docker compose I have a problem of ordering. I need database to be available, then migrations applied, then seed - in that exact order.
Simple docker compose up
brings all services at once, so they fail because they cannot connect to database. On the other hand using docker compose up
instead of calling docker run
automatically brings services down.
How I can start my services, so they:
Upvotes: 5
Views: 6282
Reputation: 10784
docker-compose
use depends_on....
services:
db:
...
migrations:
depends_on:
- db
...
seeding:
depends_on:
- db
db
service may not be available for some time after start.To solve that:
db
service.depends_on
in the following manner in dependent on db
services:depends_on:
db:
condition: service_healthy
See working demo example here.
service_completed_successfully
with depends_on
in seeding service:depends_on:
migrations:
condition: service_completed_successfully
Thanks to @David Maze for suggestion.
command
. More information is here. Busy wait script has to check for some condition signifying migrations were complete.docker compose down
Upvotes: 6
Reputation: 1043
I find the way Big Data Europe have packed their Apache images useful, using a service precondition script. For example, see this repo, although it would mean modifying/extending the docker images.
You could create an ENTRYPOINT file that contains the following:
function wait_for_it()
{
local serviceport=$1
local service=${serviceport%%:*}
local port=${serviceport#*:}
local retry_seconds=5
local max_try=100
let i=1
nc -z $service $port
result=$?
until [ $result -eq 0 ]; do
echo "[$i/$max_try] check for ${service}:${port}..."
echo "[$i/$max_try] ${service}:${port} is not available yet"
if (( $i == $max_try )); then
echo "[$i/$max_try] ${service}:${port} is still not available; giving up after ${max_try} tries. :/"
exit 1
fi
echo "[$i/$max_try] try in ${retry_seconds}s once again ..."
let "i++"
sleep $retry_seconds
nc -z $service $port
result=$?
done
echo "[$i/$max_try] $service:${port} is available."
}
for i in ${SERVICE_PRECONDITION[@]}
do
wait_for_it ${i}
done
Then in the Dockerfile at the end:
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD ---NORMAL STARTUP SCRIPT---
Then in your docker-compose.yml, add environmental variables to the containers that need to wait on another server going live. For example:
db:
...
migrations:
...
environment:
SERVICE_PRECONDITION: "db:5432 ... ..."
Once the required server is responding on that host name and port (i.e. the database is running), the container will continue to the normal command.
Upvotes: 0
Reputation: 31654
You could use depends on, e.g.
version: "3.9"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
But:
depends_on does not wait for db and redis to be “ready” before starting web - only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.
For controlling startup order
, you need explicitly write script to wait other container starts to work, e.g. if you want to make sure a db container really starts, you could let the app container connect to the db container to have try. If link successful, means the db container starts, otherwise, loop to try again.
E.g. example extract from above documention:
- Use a tool such as wait-for-it, dockerize, sh-compatible wait-for, or RelayAndContainers template. These are small wrapper scripts which you can include in your application’s image to poll a given host and port until it’s accepting TCP connections.
For example, to use wait-for-it.sh or wait-for to wrap your service’s command:
version: "2" services: web: build: . ports: - "80:8000" depends_on: - "db" command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"] db: image: postgres
- Alternatively, write your own wrapper script to perform a more application-specific health check. For example, you might want to wait until Postgres is ready to accept commands:
#!/bin/sh # wait-for-postgres.sh set -e host="$1" shift until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "postgres" -c '\q'; do >&2 echo "Postgres is unavailable - sleeping" sleep 1 done >&2 echo "Postgres is up - executing command" exec "$@"
You can use this as a wrapper script as in the previous example, by setting:
command: ["./wait-for-postgres.sh", "db", "python", "app.py"]
Finally,
Can easily be composed down with simple ctrl+c?
I think docker-compose down
is not so complex, isn't it?
Upvotes: 1
Reputation: 11040
You should use the depends_on
keywork as per the following example:
version: '3.5'
services:
db:
image: mysql:latest
container_name: db
ports:
- "3306:3306"
volumes:
- ${SOURCES_PATH}/../volumes/mysql:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: wordpress
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
phpmyadmin:
depends_on:
- db
image: phpmyadmin/phpmyadmin:latest
container_name: phpmyadmin
ports:
- "8001:80"
restart: always
environment:
- PMA_HOST=db
- PMA_USER=${DB_USER}
- PMA_PASSWORD=${DB_PASSWORD}
- PMA_PORT=3306
volumes:
- phpmyadmin:/sessions
The dependency relation forces docker compose to start services in the correct order.
Upvotes: 0