我有一个包含Postgres数据库的Docker容器。它使用的官方Postgres image有一个CMD条目,可以在主线程上启动服务器。
我希望在开始收听查询之前运行RUN psql –U postgres postgres < /dump/dump.sql
来填充数据库。
我不明白Docker如何做到这一点。如果我在CMD之后放置RUN
命令,它当然永远不会被访问,因为Docker已经完成了对Dockerfile的读取。但是如果我将它放在CMD
之前,它将在psql甚至作为一个进程存在之前运行。
如何在Docker中预填充Postgres数据库?
答案 0 :(得分:44)
经过大量的战斗,我找到了解决方案; - )
对我来说,这里发表的评论非常有用:https://registry.hub.docker.com/_/postgres/来自&#34; justfalter&#34;
无论如何,我这样做了:
# 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
希望它有所帮助!
答案 1 :(得分:25)
或者,您可以将卷安装到包含所有DDL脚本的/docker-entrypoint-initdb.d/。您可以输入 *。sh,* .sql或* .sql.gz 文件,它将在启动时执行这些文件。
e.g。 (假设你的脚本在/ tmp / my_scripts中)
docker run -v /tmp/my_scripts:/docker-entrypoint-initdb.d postgres
答案 2 :(得分:8)
您可以执行简单的SQL转储并将dump.sql
文件复制到/docker-entrypoint-initdb.d/
中。问题是速度。我的dump.sql
脚本大约为17MB(小型DB-10个表,其中只有1个行有10万行),初始化需要一分钟(!)。这对于本地开发/单元测试等是不可接受的。
解决方案是进行二进制postgres转储并使用shell scripts initialization support。 然后,相同的DB会在500毫秒之内初始化,而不是1分钟:)
dump.pgdata
二进制转储直接从容器或本地数据库中
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"
$ 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
答案 3 :(得分:1)
utilises Flocker还有另一种选择:
Flocker是一个容器数据卷管理器,旨在允许像PostgreSQL这样的数据库在生产中的容器中轻松运行。在生产中运行数据库时,您必须考虑从主机故障中恢复等问题。 Flocker提供了一种工具,用于管理生产环境中的机器群集中的数据量。例如,由于Postgres容器是在主机之间调度以响应服务器故障,因此Flocker可以同时在主机之间自动移动其关联的数据卷。这意味着当您的Postgres容器在新主机上启动时,它会拥有其数据。此操作可以使用Flocker API或CLI手动完成,也可以由Flocker集成的容器编排工具自动完成,例如Docker Swarm,Kubernetes或Mesos。
答案 4 :(得分:0)
我能够通过/etc/init.d/postgresql预先挂起docker文件中的run命令来加载数据。我的docker文件有以下行,它对我有用:
RUN /etc/init.d/postgresql start && /usr/bin/psql -a < /tmp/dump.sql
答案 5 :(得分:0)
我遵循与@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
答案 6 :(得分:0)
对于E2E测试,我们需要一个数据库,该数据库的结构和数据已保存在Docker映像中,我们已执行以下操作:
Dockerfile:
FROM postgres:9.4.24-alpine
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD postgres
ENV PGDATA /pgdata
COPY database.backup /tmp/
COPY database_restore.sh /docker-entrypoint-initdb.d/
RUN /docker-entrypoint.sh --help
RUN rm -rf /docker-entrypoint-initdb.d/database_restore.sh
RUN rm -rf /tmp/database.backup
database_restore.sh:
#!/bin/sh
set -e
pg_restore -C -d postgres /tmp/database.backup
要创建图像,请执行以下操作:
docker build .
要启动容器:
docker run --name docker-postgres -d -p 5432:5432 <Id-docker-image>
这不会在每次启动容器时不还原数据库。数据库的结构和数据已经包含在创建的Docker映像中。
我们基于本文,但消除了多阶段操作: Creating Fast, Lightweight Testing Databases in Docker
编辑:在9.4版中,alpine现在无法正常运行,因为它无法运行 运行database_restore.sh脚本。使用9.4.24-alpine版本
答案 7 :(得分:0)
我的解决方案受到亚历克斯·德格斯(Alex Dguez)的回答的启发,很不幸,该回答对我不起作用,因为:
RUN /docker-entrypoint.sh --help
从来没有遇到过,而我总是抱怨The command '/bin/sh -c /docker-entrypoint.sh -' returned a non-zero code: 1
/docker-entrypoint-initdb.d
目录以下答案最初来自我在另一篇文章中的回复:https://stackoverflow.com/a/59303962/4440427。应该注意的是,该解决方案是从二进制转储还原,而不是从OP要求的普通SQL还原。但是可以稍加修改以适应普通的SQL情况
Dockerfile:
FROM postgres:9.6.16-alpine
LABEL maintainer="lu@cobrainer.com"
LABEL org="Cobrainer GmbH"
ARG PG_POSTGRES_PWD=postgres
ARG DBUSER=someuser
ARG DBUSER_PWD=P@ssw0rd
ARG DBNAME=sampledb
ARG DB_DUMP_FILE=example.pg
ENV POSTGRES_DB launchpad
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD ${PG_POSTGRES_PWD}
ENV PGDATA /pgdata
COPY wait-for-pg-isready.sh /tmp/wait-for-pg-isready.sh
COPY ${DB_DUMP_FILE} /tmp/pgdump.pg
RUN set -e && \
nohup bash -c "docker-entrypoint.sh postgres &" && \
/tmp/wait-for-pg-isready.sh && \
psql -U postgres -c "CREATE USER ${DBUSER} WITH SUPERUSER CREATEDB CREATEROLE ENCRYPTED PASSWORD '${DBUSER_PWD}';" && \
psql -U ${DBUSER} -d ${POSTGRES_DB} -c "CREATE DATABASE ${DBNAME} TEMPLATE template0;" && \
pg_restore -v --no-owner --role=${DBUSER} --exit-on-error -U ${DBUSER} -d ${DBNAME} /tmp/pgdump.pg && \
psql -U postgres -c "ALTER USER ${DBUSER} WITH NOSUPERUSER;" && \
rm -rf /tmp/pgdump.pg
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD pg_isready -U postgres -d launchpad
其中wait-for-pg-isready.sh
是:
#!/bin/bash
set -e
get_non_lo_ip() {
local _ip _non_lo_ip _line _nl=$'\n'
while IFS=$': \t' read -a _line ;do
[ -z "${_line%inet}" ] &&
_ip=${_line[${#_line[1]}>4?1:2]} &&
[ "${_ip#127.0.0.1}" ] && _non_lo_ip=$_ip
done< <(LANG=C /sbin/ifconfig)
printf ${1+-v} $1 "%s${_nl:0:$[${#1}>0?0:1]}" $_non_lo_ip
}
get_non_lo_ip NON_LO_IP
until pg_isready -h $NON_LO_IP -U "postgres" -d "launchpad"; do
>&2 echo "Postgres is not ready - sleeping..."
sleep 4
done
>&2 echo "Postgres is up - you can execute commands now"
获得上述脚本以及更详细的自述文件。
答案 8 :(得分:0)
我的目标是拥有一个包含数据库的图像-i。 e。每次我docker run
或docker-compose up
时都可以节省重建时间。
我们只需要从exec "$@"
中取出docker-entrypoint.sh
行即可。所以我加入了Dockerfile
:
#Copy my ssql scripts into the image to /docker-entrypoint-initdb.d:
COPY ./init_db /docker-entrypoint-initdb.d
#init db
RUN grep -v 'exec "$@"' /usr/local/bin/docker-entrypoint.sh > /tmp/docker-entrypoint-without-serverstart.sh && \
chmod a+x /tmp/docker-entrypoint-without-serverstart.sh && \
/tmp/docker-entrypoint-without-serverstart.sh postgres && \
rm -rf /docker-entrypoint-initdb.d/* /tmp/docker-entrypoint-without-serverstart.sh