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?
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!
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
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.
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 :)
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"
$ 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 ... ;
$ docker build -t db-test-img .
$ docker run -it --rm --name db-test db-test-img
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.
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
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