在Docker中启动并填充Postgres容器

时间:2015-04-13 07:52:21

标签: database postgresql docker

我有一个包含Postgres数据库的Docker容器。它使用的官方Postgres image有一个CMD条目,可以在主线程上启动服务器。

我希望在开始收听查询之前运行RUN psql –U postgres postgres < /dump/dump.sql来填充数据库。

我不明白Docker如何做到这一点。如果我在CMD之后放置RUN命令,它当然永远不会被访问,因为Docker已经完成了对Dockerfile的读取。但是如果我将它放在CMD之前,它将在psql甚至作为一个进程存在之前运行。

如何在Docker中预填充Postgres数据库?

9 个答案:

答案 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)

对于那些希望在首次运行时使用数百万条记录初始化postgres DB的人。

使用* .sql dump导入

您可以执行简单的SQL转储并将dump.sql文件复制到/docker-entrypoint-initdb.d/中。问题是速度。我的dump.sql脚本大约为17MB(小型DB-10个表,其中只有1个行有10万行),初始化需要一分钟(!)。这对于本地开发/单元测试等是不可接受的。

使用二进制转储导入

解决方案是进行二进制postgres转储并使用shell scripts initialization support。 然后,相同的DB会在500毫秒之内初始化,而不是1分钟:)

1。创建名为“ my-db”的数据库的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"

2。使用给定的转储和初始化脚本创建docker镜像

$ 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

答案 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)的回答的启发,很不幸,该回答对我不起作用,因为:

  1. 我使用了pg-9.6基本图像,RUN /docker-entrypoint.sh --help从来没有遇到过,而我总是抱怨The command '/bin/sh -c /docker-entrypoint.sh -' returned a non-zero code: 1
  2. 我不想污染/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"

可以在https://github.com/cobrainer/pg-docker-with-restored-db

获得上述脚本以及更详细的自述文件。

答案 8 :(得分:0)

我的目标是拥有一个包含数据库的图像-i。 e。每次我docker rundocker-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