web-dev-qa-db-fra.com

Démarrer et remplir un conteneur Postgres dans Docker

J'ai un conteneur Docker qui contient ma base de données Postgres. Il utilise la version officielle image Postgres qui a une entrée CMD qui démarre le serveur sur le thread principal.

Je veux remplir la base de données en exécutant RUN psql –U postgres postgres < /dump/dump.sql avant de commencer à écouter les requêtes.

Je ne comprends pas comment cela est possible avec Docker. Si je place la commande RUN après CMD, elle ne sera bien sûr jamais atteinte car Docker a fini de lire le fichier Docker. Mais si je le place avant le CMD, il sera exécuté avant même que psql n’existe en tant que processus.

Comment puis-je pré-remplir une base de données Postgres dans Docker?

40
Miguel

Après beaucoup de combats, j'ai trouvé une solution ;-)

Pour moi était très utile un commentaire posté ici: https://registry.hub.docker.com/_/postgres/ de "justfalter"

Quoi qu'il en soit, j'ai fait de cette manière:

# Dockerfile
FROM postgres:9.4

RUN mkdir -p /tmp/psql_data/

COPY db/structure.sql /tmp/psql_data/
COPY scripts/init_docker_postgres.sh /docker-entrypoint-initdb.d/

db/structure.sql est un dump SQL, utile pour initialiser le premier espace de table.

Puis le init_docker_postgres.sh

#!/bin/bash

# this script is run when the docker container is built
# it imports the base database structure and create the database for the tests

DATABASE_NAME="db_name"
DB_DUMP_LOCATION="/tmp/psql_data/structure.sql"

echo "*** CREATING DATABASE ***"

# create default database
gosu postgres postgres --single <<EOSQL
  CREATE DATABASE "$DATABASE_NAME";
  GRANT ALL PRIVILEGES ON DATABASE "$DATABASE_NAME" TO postgres;
EOSQL

# clean sql_dump - because I want to have a one-line command

# remove indentation
sed "s/^[ \t]*//" -i "$DB_DUMP_LOCATION"

# remove comments
sed '/^--/ d' -i "$DB_DUMP_LOCATION"

# remove new lines
sed ':a;N;$!ba;s/\n/ /g' -i "$DB_DUMP_LOCATION"

# remove other spaces
sed 's/  */ /g' -i "$DB_DUMP_LOCATION"

# remove firsts line spaces
sed 's/^ *//' -i "$DB_DUMP_LOCATION"

# append new line at the end (suggested by @Nicola Ferraro)
sed -e '$a\' -i "$DB_DUMP_LOCATION"

# import sql_dump
gosu postgres postgres --single "$DATABASE_NAME" < "$DB_DUMP_LOCATION";


echo "*** DATABASE CREATED! ***"

Alors finalement:

# no postgres is running
[myserver]# psql -h 127.0.0.1 -U postgres
psql: could not connect to server: Connection refused
    Is the server running on Host "127.0.0.1" and accepting
    TCP/IP connections on port 5432?

[myserver]# docker build -t custom_psql .
[myserver]# docker run -d --name custom_psql_running -p 5432:5432 custom_psql

[myserver]# docker ps -a
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS              PORTS                    NAMES
ce4212697372        custom_psql:latest   "/docker-entrypoint.   9 minutes ago       Up 9 minutes        0.0.0.0:5432->5432/tcp   custom_psql_running

[myserver]# psql -h 127.0.0.1 -U postgres
psql (9.2.10, server 9.4.1)
WARNING: psql version 9.2, server version 9.4.
         Some psql features might not work.
Type "help" for help.

postgres=# 

# postgres is now initialized with the dump

J'espère que ça aide!

45
damoiser

Sinon, vous pouvez simplement monter un volume sur /docker-entrypoint-initdb.d/ qui contient tous vos scripts DDL. Vous pouvez mettre dans *. Sh, * .sql ou * .sql.gz et il se chargera de les exécuter au démarrage.

par exemple. (en supposant que vos scripts soient dans/tmp/my_scripts)

docker run -v /tmp/my_scripts:/docker-entrypoint-initdb.d postgres
27
darthbinamira

Pour ceux qui veulent initialiser la base de données postgres avec des millions d’enregistrements lors de la première exécution.

Importation à l'aide d'un fichier de vidage * .sql

Vous pouvez faire un dump SQL simple et copier le dump.sql fichier dans /docker-entrypoint-initdb.d/. Le problème est la vitesse . Ma dump.sql le script fait environ 17 Mo (petites tables DB - 10 avec 100 000 lignes dans une seule d’entre elles) et l’initialisation dure plus d’une minute (!) . C'est inacceptable pour le développement local/test unitaire, etc.

Importer en utilisant un vidage binaire

La solution consiste à créer un dump postgres binaire et à utiliser prise en charge de l'initialisation des scripts Shell . Ensuite, le même DB est initialisé comme 500ms au lieu de 1 minute :)

1. Créez le dump.pgdata dump binaire de la base de données nommée "my-db"

Directement depuis le conteneur ou votre base de données locale

pg_dump -U postgres --format custom my-db > "dump.pgdata"

Ou de l'hôte à partir d'un conteneur en cours d'exécution ( postgres-container)

docker exec postgres-container pg_dump -U postgres --format custom my-db > "dump.pgdata"

2. Créer une image de menu fixe avec un script de sauvegarde et d'initialisation donné

$ tree
.
├── Dockerfile
└── docker-entrypoint-initdb.d
    ├── 01-restore.sh
    ├── 02-updates.sql
    └── dump.pgdata
$ cat Dockerfile
FROM postgres:11

COPY ./docker-entrypoint-initdb.d/ /docker-entrypoint-initdb.d/
$ cat docker-entrypoint-initdb.d/01-restore.sh
#!/bin/bash

file="/docker-entrypoint-initdb.d/dump.pgdata"
dbname=my-db

echo "Restoring DB using $file"
pg_restore -U postgres --dbname=$dbname --verbose --single-transaction < "$file" || exit 1
$ cat docker-entrypoint-initdb.d/02-updates.sql
-- some updates on your DB, for example for next application version
-- this file will be executed on DB during next release
UPDATE ... ;

3. Construire l'image et l'exécuter

$ docker build -t db-test-img .
$ docker run -it --rm --name db-test db-test-img
15
Petr Újezdský

Il y a encore une autre option disponible qui tilise Flocker :

Flocker est un gestionnaire de volume de données de conteneur conçu pour permettre à des bases de données telles que PostgreSQL de s'exécuter facilement dans des conteneurs en production. Lorsque vous exécutez une base de données en production, vous devez penser à des éléments tels que la récupération après une défaillance de l'hôte. Flocker fournit des outils de gestion des volumes de données sur un cluster de machines, comme dans un environnement de production. Par exemple, lorsqu'un conteneur Postgres est planifié entre les hôtes en réponse à une défaillance du serveur, Flocker peut automatiquement déplacer le volume de données associé entre les hôtes en même temps. Cela signifie que lorsque votre conteneur Postgres démarre sur un nouvel hôte, il a ses données. Cette opération peut être effectuée manuellement à l'aide de l'API ou de la CLI de Flocker, ou automatiquement à l'aide d'un outil d'orchestration de conteneur auquel Flocker est intégré, par exemple Docker Swarm, Kubernetes ou Mesos.

1
Max Desiatov

J'ai suivi la même solution que celle de @damoiser. La seule situation différente était que je voulais importer toutes les données de vidage.

Veuillez suivre la solution ci-dessous. (Je n’ai effectué aucune vérification)

Dockerfile

FROM postgres:9.5

RUN mkdir -p /tmp/psql_data/

COPY db/structure.sql /tmp/psql_data/
COPY scripts/init_docker_postgres.sh /docker-entrypoint-initdb.d/

puis le script doker-entrypoint-initdb.d

#!/bin/bash

DB_DUMP_LOCATION="/tmp/psql_data/structure.sql"

echo "*** CREATING DATABASE ***"

psql -U postgres < "$DB_DUMP_LOCATION";

echo "*** DATABASE CREATED! ***"

et alors vous pouvez construire votre image en tant que

docker build -t abhije***/postgres-data .

docker run -d abhije***/postgres-data 
0
Abhijeet Kamble

J'ai pu charger les données en pré-en attendant la commande d'exécution dans le fichier de menu fixe avec /etc/init.d/postgresql. Mon fichier docker a la ligne suivante qui fonctionne pour moi:

RUN /etc/init.d/postgresql start && /usr/bin/psql -a < /tmp/dump.sql
0
acknapp