使用初始模式构建Postgres Docker容器

53

我希望构建代表已经存在的公司数据库的Dockerfile。同样,我想创建一个Dockerfile,它通过恢复psql转储文件来启动。

我的psql_dump.sql文件在.目录中。

FROM postgres
ADD . /init_data
run "createdb" "--template=template0" "my_database"
run  "psql" "-d" "my_database"  --command="create role my_admin superuser"
run  "psql" "my_database" "<" "init_data/psql_dump.sql"

我原以为这样做已经足够好了。我想避免使用.sh脚本的解决方案,就像这个解决方案那样。

我使用template0,因为psql文档中说需要创建与原始数据库相同的用户,并且在恢复之前需要使用template0创建数据库。

然而,它给了我一个错误:

createdb: could not connect to database template1: could not connect to server: No such file or directory
        Is the server running locally and accepting

我还在使用Docker Compose来管理整个应用程序,如果在Docker Compose中解决这个问题更好,我很乐意使用基础的psql镜像并使用Docker Compose来实现。


可能是在Docker中启动并填充Postgres容器的重复问题。 - damoiser
3个回答

77

根据官方PostgreSQL Docker镜像的使用指南,您所需的只有:

Dockerfile

FROM postgres
ENV POSTGRES_DB my_database
COPY psql_dump.sql /docker-entrypoint-initdb.d/

POSTGRES_DB环境变量将指示容器在第一次运行时创建my_database模式。

并且容器中任何位于/docker-entrypoint-initdb.d/目录下的.sql文件都将被执行。

如果您想要执行.sh脚本,也可以将它们提供在/docker-entrypoint-initdb.d/目录下。


太棒了,谢谢!我也没有意识到需要使用psql --username=my_user --dbname=my_database输入psql才能使它按我预期的方式工作。 - Jono
10
主题要求“使用初始模式构建postgres docker容器”,但您建议在启动时创建模式,而不是构建已经具有该模式的容器。如果模式需要5分钟才能创建,则每次必须创建卷都会受到5分钟的惩罚,而不是将其放在实际容器内部。 - dsvensson
1
将数据放在镜像内部(以便在容器创建时拥有它)是一个不好的主意,因为Docker镜像联合文件系统很慢。您的数据应该在卷上,因此不在容器文件系统/镜像文件系统中。 - Thomasleveil
1
@Thomasleveil,你可以分为三步来完成:首先像你之前做的那样创建一个容器,然后将其数据复制到本地主机目录中,每次需要一个新的PostgreSQL时,启动一个PostgreSQL容器,它的卷与此本地主机目录的副本一起工作。 - yishaiz

9

正如评论中所说,如果您的模式重建速度很快,则@Thomasleveil答案非常棒且简单。

但在我的情况下,它很慢,我想使用Docker卷,所以我做了以下操作:

  1. 首先使用@Thomasleveil答案中提到的Docker镜像创建一个包含所有模式初始化的Postgres容器

Dockerfile:

FROM postgres
WORKDIR /docker-entrypoint-initdb.d
ADD psql_dump.sql /docker-entrypoint-initdb.d
EXPOSE 5432
  1. 运行该命令并创建一个本地目录,其中包含从“psql_dump.sql”文件中填充的Postgres数据:docker cp mypg:/var/lib/postgresql/data ./postgres-data

  2. 将数据复制到临时数据文件夹,并启动新的Postgres docker-compose容器,其卷位于新的临时数据文件夹中:

startPostgres.sh:

rm -r ./temp-postgres-data/data
mkdir -p ./temp-postgres-data/data
cp -r ./postgres-data/data ./temp-postgres-data/
docker-compose -p mini-postgres-project up

docker-compose.yml文件如下:

version: '3'
services:
  postgres:
    container_name: mini-postgres
    image: postgres:9.5
    ports:
    - "5432:5432"
    volumes:
      - ./temp-postgres-data/data:/var/lib/postgresql/data

现在您可以在新机器上运行步骤#1和#2,或者如果您的psql_dump.sql发生更改。每次您想要一个新的干净(但已初始化)的数据库时,只需从步骤#3运行startPostgres.sh即可。它仍然使用Docker卷。

6
"ADD psql_dump.sql /docker-entrypoint-initdb.d" 可以简化为 "ADD psql_dump.sql .",因为你已经在那个 WORKDIR 文件夹中了。 - Lee Benson
为什么不直接使用卷将SQL添加到docker-entrypoint-initdb.d中呢? - Juan Carrey

6

@Thomasleveil的回答将在运行时重新创建数据库模式,这对大多数情况都可以。

如果您想在构建时重新创建数据库模式(即如果您的模式初始化非常慢),则可以从Dockerfile中调用stock docker_entrypoint.sh

但是,由于docker_entrypoint.sh旨在启动长时间运行的数据库服务器,因此您必须添加额外的脚本以在数据库初始化之后但在启动长时间运行的服务器之前退出进程。

Dockerfile(具有构建时间数据库初始化)

# STAGE 1 - Equivalent to @Thomasleveil
FROM postgres AS runtime_init
ENV POSTGRES_DB my_database
COPY 1-psql_dump.sql /docker-entrypoint-initdb.d/

# STAGE 2 - Initialize the database during the build
FROM runtime_init AS buildtime_init_builder
RUN echo "exit 0" > /docker-entrypoint-initdb.d/100-exit_before_boot.sh
ENV PGDATA=/pgdata
RUN docker-entrypoint.sh postgres

# STAGE 3 - Copy the initialized db to a new image to reduce size.
FROM postgres AS buildtime_init
ENV PGDATA=/pgdata
COPY --chown=postgres:postgres --from=buildtime_init_builder /pgdata /pgdata

重要提示

  • 库存postgres映像将按字母顺序运行初始化脚本in alphabetical order,因此请确保您的数据库恢复脚本出现在Dockerfile中创建的exit_before_boot.sh脚本之前。
    • 这由上面显示的1100前缀所示。根据您的喜好进行修改。
  • 对此映像的正在运行的实例进行的数据库更新不会在重新启动时保留,因为存储数据库文件的PGDATA路径不再映射到从主机机器挂载的卷。

进一步阅读


这非常有帮助。顺便提一下,我用来排序文件的一个模式是001、100、999等。这可以防止字母排序出现问题。例如,100会排在2之前。另外,PGDATA对我没有起作用,所以我不得不使用默认的/var/lib/postgresql/data/ - cole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接