在Docker中启动和填充Postgres容器

yzckvree  于 2023-01-25  发布在  Docker
关注(0)|答案(9)|浏览(131)

我有一个Docker容器包含我的Postgres数据库,它使用的是官方的Postgres image,它有一个CMD条目,在主线程上启动服务器。
我想在数据库开始侦听查询之前运行RUN psql –U postgres postgres < /dump/dump.sql来填充数据库。
我不明白Docker怎么可能做到这一点。如果我把RUN命令放在CMD之后,它当然永远不会被执行,因为Docker已经完成了对Docker文件的阅读。但是如果我把它放在CMD之前,它甚至会在psql作为一个进程存在之前运行。
如何在Docker中预填充Postgres数据库?

bq9c1y66

bq9c1y661#

经过大量的斗争,我已经找到了解决办法;-)
对我来说是非常有用的评论张贴在这里:来自"刚刚动摇"
不管怎样,我是这样做的:

# 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

希望能有所帮助!

wf82jlnq

wf82jlnq2#

对于那些希望在第一次运行时使用数百万条记录初始化PostgreSQL DB的用户。

使用 *. sql转储导入

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

使用二进制转储导入

解决方案是创建一个二进制PostgreSQL转储文件,并使用shell脚本初始化支持。然后相同的数据库在大约500ms内初始化,而不是1分钟。

    • 1.直接从容器或本地DB中创建名为"my-db"**的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映像**

一个一个二个一个一个一个三个一个一个一个一个一个一个四个一个一个一个一个一个五个一个

    • 3.构建映像并运行它**
$ docker build -t db-test-img .
$ docker run -it --rm --name db-test db-test-img
svmlkihl

svmlkihl3#

或者,您可以将一个卷装载到包含所有DDL脚本的/docker-entrypoint-initdb.d/中,您可以将其放入***.sh、*.sql或 *.sql.gz**文件中,它将负责在启动时执行这些文件。
例如(假设您的脚本位于/tmp/my_scripts中)

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

kiayqfof4#

utilises Flocker还有另一个可用选项:
Flocker是一个容器数据卷管理器,旨在允许PostgreSQL等数据库在生产环境中的容器中轻松运行。在生产环境中运行数据库时,您必须考虑从主机故障中恢复等问题。Flocker提供了跨计算机群集管理数据卷的工具,就像您在生产环境中一样。例如,当Postgres容器在主机之间调度以响应服务器故障时,Flocker可以同时在主机之间自动移动其关联的数据卷。2这意味着当您的Postgres容器在新主机上启动时,该操作可以使用Flocker API或CLI手动完成,也可以通过与Flocker集成的容器编排工具(例如Docker Swarm、Kubernetes或Mesos)自动完成。

e5nszbig

e5nszbig5#

我遵循相同的解决方案@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/

那么init_docker_postgres.sh脚本

#!/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
mnowg1ta

mnowg1ta6#

我的解决方案灵感来自亚历克斯·德格兹的答案,不幸的是不工作,因为我:
1.我使用pg-9.6基础映像,RUN /docker-entrypoint.sh --help对我来说从来没有跑过,这总是抱怨与The command '/bin/sh -c /docker-entrypoint.sh -' returned a non-zero code: 1
1.我不想污染/docker-entrypoint-initdb.d目录
下面的答案来自我在另一篇文章中的回复:https://stackoverflow.com/a/59303962/4440427。应该注意,该解决方案用于从二进制转储中恢复,而不是从OP要求的纯SQL中恢复。但是,可以稍微修改该解决方案以适应纯SQL情况
停靠文件:

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上获得

gzszwxb4

gzszwxb47#

我可以通过在docker文件中使用/etc/init.d/postgresql预挂起run命令来加载数据。我的docker文件包含以下行,这对我来说是有效的:

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

xbp102n08#

对于E2E测试,我们需要一个结构和数据已保存在Docker映像中的数据库,我们已执行以下操作:
停靠文件:

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

数据库_恢复. 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>

不会在每次启动container时恢复数据库。数据库的结构和数据已包含在创建的Docker映像中。
我们已经在此基础上进行了文章,但剔除了多阶段:Creating Fast, Lightweight Testing Databases in Docker

    • 编辑**:对于版本9.4-alpine现在不工作,因为它不运行database_restore. sh脚本。请使用版本9.4.24-alpine
yhuiod9q

yhuiod9q9#

我的目标是拥有一个包含数据库的映像--也就是说,节省每次执行docker run命令时重建数据库的时间。
我们只需要设法从docker-entrypoint.sh中取出exec "$@"行,所以我在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

相关问题