Запуск и заполнение контейнера Postgres в Docker
У меня есть контейнер Docker, содержащий мою базу данных Postgres. Он использует официальное изображение Postgres, в котором есть запись CMD, которая запускает сервер в основном потоке.
Я хочу заполнить базу данных, запустив RUN psql –U postgres postgres < /dump/dump.sql
, прежде чем она начнет прослушивать запросы.
Я не понимаю, как это возможно с Docker. Если я поместил команду RUN
после CMD, она, конечно, никогда не будет достигнута, потому что Docker закончил чтение файла Docker. Но если я поместил его перед CMD
, он будет работать до того, как psql будет существовать как процесс.
Как я могу предварительно заполнить базу данных Postgres в Docker?
Ответы
Ответ 1
После долгих боев я нашел решение ;-)
Для меня был очень полезен комментарий, размещенный здесь: https://registry.hub.docker.com/_/postgres/ от "justfalter"
Во всяком случае, я сделал так:
# 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
- это дамп sql, полезный для инициализации первого табличного пространства.
Затем 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! ***"
Итак, наконец:
# 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
Надеюсь, поможет!
Ответ 2
Кроме того, вы можете просто подключить том к /docker -entrypoint-initdb.d/, который содержит все ваши DDL-скрипты. Вы можете поместить файлы *. Sh, *.sql или *.sql.gz, и он позаботится об их запуске.
например. (предполагая, что у вас есть ваши скрипты в /tmp/my _scripts)
docker run -v /tmp/my_scripts:/docker-entrypoint-initdb.d postgres
Ответ 3
Для тех, кто хочет инициализировать базу данных postgres с миллионами записей во время первого запуска.
Импорт с использованием *.sql dump
Вы можете сделать простой SQL дамп и скопировать dump.sql
файл в /docker-entrypoint-initdb.d/
. Проблема в скорости. Мой сценарий dump.sql
занимает около 17 МБ (небольшая БД - 10 таблиц, по 100 000 строк только в одной из них), и инициализация занимает более минуты (!). Это неприемлемо для локальной разработки/модульного тестирования и т.д.
Импорт с использованием двоичного дампа
Решение состоит в том, чтобы сделать двоичный дамп postgres и использовать поддержку инициализации сценариев оболочки. Затем эта же БД инициализируется как 500 мс вместо 1 минуты :)
1. Создайте двоичный дамп dump.pgdata
для БД с именем "my-db".
Непосредственно изнутри контейнера или вашей локальной БД
pg_dump -U postgres --format custom my-db > "dump.pgdata"
Или с хоста из запущенного контейнера (postgres-container)
docker exec postgres-container pg_dump -U postgres --format custom my-db > "dump.pgdata"
2. Создайте образ докера с заданным скриптом дампа и инициализации
$ 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. Создайте образ и запустите его
$ docker build -t db-test-img .
$ docker run -it --rm --name db-test db-test-img
Ответ 4
Есть еще один доступный вариант, который использует Flocker:
Flocker - это диспетчер томов контейнерных данных, который позволяет легко создавать базы данных, такие как PostgreSQL, в контейнерах на производстве. При запуске базы данных на производстве вы должны думать о таких вещах, как восстановление после сбоя хоста. Flocker предоставляет инструменты для управления томами данных в кластере машин, как в рабочей среде. Например, поскольку контейнер Postgres запланирован между хостами в ответ на сбой сервера, Flocker может автоматически перемещать свой связанный объем данных между хостами одновременно. Это означает, что когда ваш Postgres-контейнер запускается на новом хосте, он имеет свои данные. Эту операцию можно выполнить вручную с помощью Flocker API или CLI или автоматически с помощью инструмента для сопоставления контейнеров, с которым Flocker интегрируется, например, Docker Swarm, Kubernetes или Mesos.
Ответ 5
Мне удалось загрузить данные, предварительно запустив команду run в файле докеров с помощью /etc/init.d/postgresql. Мой файл докеров имеет следующую строку, которая работает для меня:
RUN /etc/init.d/postgresql start && /usr/bin/psql -a < /tmp/dump.sql
Ответ 6
Я следовал тому же решению, что и @damoiser. Единственная другая ситуация - я хотел импортировать все данные дампа.
Пожалуйста, следуйте приведенному ниже решению. (Я не делал никаких проверок)
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/
затем скрипт 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! ***"
и тогда вы можете построить свой образ как
docker build -t abhije***/postgres-data .
docker run -d abhije***/postgres-data