3

I'd like to use Docker to load a .sql file into a PostgreSQL database and serve it.

The following command returns quickly (before the database has been fully populated - which appears to happen asynchronously in the background).

docker run \
    --name myContainer \
    -p 5432:5432 \
    -v mySqlFile.sql:/docker-entrypoint-initdb.d/dump.sql \
    -d postgres:alpine

Since this command will be part of a script, I'd like the command to block until the database has fully initialised with mySqlFile.sql

How would I go about getting the command to block, or some other means of pausing while mySqlFile.sql is loaded?

For clarity: I run the above command, and then the following command:

echo "select count(*) as \"Posts imported\" from posts;" | docker exec -i myContainer psql -U postgres
 Posts imported
----------------
              0
(1 row)

Waiting a few seconds, and I get this:

echo "select count(*) as \"Posts imported\" from posts;" | docker exec -i myContainer psql -U postgres
 Posts imported
----------------
          51103
(1 row)

This is a problem, because I am looking to build an automated solution. I want the docker run command to block until all records have been imported into Postgres. Then I have the database in a state which is ready for the next command.

Chris Beach
  • 4,302
  • 2
  • 31
  • 50
  • Your docker will run when mount is complete. Then PostgreSQL service will automatically block until all script in `/docker-entrypoint-initdb.d` has been executed – Shahriar Feb 06 '18 at 05:19
  • what about running a bash scrip instead of psql ... inside sleep for 15 seconds then execute the psql command? – Urban48 Feb 06 '18 at 13:13
  • Thanks @Urban48 but I'd prefer not to use sleep statements as I cannot predict how large the sql dump will be. 15 secs could be way too long (which is kinda okay)... but it could be too short (then my script will break) – Chris Beach Feb 06 '18 at 13:22

2 Answers2

4

Idea: creation of a custom wrapper waiting a specific record inserted in a table for docker at the end of the populate script.

Using docker-compose you can use a custom wrapper script:

In a docker-compose.yml

version: "2"
services:
  app:
     build: app/
     depends_on:
       - "db"
     command: ["./wait-for-postgres.sh", "db", "yourcommand", "with", "parameters"]
  db:
    build: db/

In wait-for-postgres.sh

#!/bin/bash
# wait-for-postgres.sh

set -e

host="$1"
shift
cmd="$@"
#wait postgres up
until psql -h "$host" -U "postgres" -c '\q'; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

#wait populate script
while ! psql -h "$host" -U "postgres" -t -c "select * from docker_ready" | egrep .
do
  sleep 1
done
>&2 echo "Postgres is populated - executing command"
exec $cmd

You can find some tips on https://docs.docker.com/compose/startup-order/, https://hub.docker.com/_/postgres/

MoiioM
  • 1,914
  • 1
  • 10
  • 16
  • Thanks for this answer. Unfortunately the database comes up (and down) during the initdb phase, so this check isn't able to distinguish the end of the initdb process – Chris Beach Feb 07 '18 at 00:40
2

I have found a solution. I think you can use this way.

When you run your postgres image, ENTRYPOINT is a shell.

In that time if you run ps aux, you will see that PID 1 is bash.

But when all initialization is completed, postgres becomes the main process for exec "$@". That time PID 1 is postgres.

You can use this hack. You don't know when your initialization will be completed. But you are sure that, when your initialization is completed, your PID 1 will be postgres.

$ docker exec -i myContainer ps -p 1 -o comm=

This will return bash when initialization is running. And will be changed to postgres when everything is done.

Of course, you need loop. But in this way you will be sure when initialization is totally completed without any guess.

UPDATE

As you are using alpine image, it has cut version of ps. You need to install uncut ps in your alpine image. (stack overflow question)

Run following command first.

$ docker exec -i myContainer apk add --no-cache procps

Now ps -p 1 -o comm= will work

Hope this will help

Shahriar
  • 13,460
  • 8
  • 78
  • 95