如何在 Docker Postgres 的脚本中创建用户/数据库

Posted

技术标签:

【中文标题】如何在 Docker Postgres 的脚本中创建用户/数据库【英文标题】:How to create User/Database in script for Docker Postgres 【发布时间】:2014-12-23 06:59:32 【问题描述】:

我一直在尝试通过创建自定义用户和数据库来为开发 postgres 实例设置容器。我正在使用official postgres docker image。在文档中,它指示您在 /docker-entrypoint-initdb.d/ 文件夹中插入一个 bash 脚本,以使用任何自定义参数设置数据库。

我的 bash 脚本:make_db.sh

su postgres -c "createuser -w -d -r -s docker"
su postgres -c "createdb -O docker docker"

Dockerfile

FROM library/postgres

RUN ["mkdir", "/docker-entrypoint-initdb.d"]
ADD make_db.sh /docker-entrypoint-initdb.d/

我从docker logs -f db(db 是我的容器名称)得到的错误是:

createuser:无法连接到数据库 postgres:无法连接到服务器:没有这样的文件或目录

似乎/docker-entrypoint-initdb.d/ 文件夹内的命令在 postgres 启动之前正在执行。我的问题是,如何使用官方的 postgres 容器以编程方式设置用户/数据库?有没有办法用脚本做到这一点?

【问题讨论】:

【参考方案1】:

编辑 - 自 2015 年 7 月 23 日起

official postgres docker image 将运行在/docker-entrypoint-initdb.d/ 文件夹中找到的.sql 脚本。

所以你只需要创建以下 sql 脚本:

init.sql

CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;

并将其添加到您的 Dockerfile 中:

Dockerfile

FROM library/postgres
COPY init.sql /docker-entrypoint-initdb.d/

但自 2015 年 7 月 8 日起,如果您只需要创建一个用户和数据库,使用 POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB 环境会更容易变量:

docker run -e POSTGRES_USER=docker -e POSTGRES_PASSWORD=docker -e POSTGRES_DB=docker library/postgres

或使用 Dockerfile:

FROM library/postgres
ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker
ENV POSTGRES_DB docker

适用于 2015 年 7 月 23 日之前的图片

来自the documentation of the postgres Docker image,据说

[...] 它将获取在该目录 [/docker-entrypoint-initdb.d] 中找到的任何 *.sh 脚本,以便在启动服务之前进行进一步的初始化

这里重要的是“在启动服务之前”。这意味着您的脚本 make_db.sh 将在 postgres 服务启动之前执行,因此错误消息 “无法连接到数据库 postgres”

之后还有一条有用的信息:

如果您需要在初始化过程中执行 SQL 命令,强烈建议使用 Postgres 单用户模式。

同意这乍一看可能有点神秘。它说的是您的初始化脚本应该在执行其操作之前以单一模式启动 postgres 服务。所以你可以改变你的 make_db.ksh 脚本如下,它应该让你更接近你想要的:

注意,最近in the following commit 发生了变化。这将适用于最新更改:

export PGUSER=postgres
psql <<- EOSQL
    CREATE USER docker;
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL

以前需要使用--single 模式:

gosu postgres postgres --single <<- EOSQL
    CREATE USER docker;
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL

【讨论】:

您可以使用:gosu postgres postgres --single &lt; /tmp/somefile.sql 如何运行psql -U myDb -d myDb -f myDb.sql 注意,任何 postgres Dockerfile 都不再支持 --single。 虽然这被标记为正确答案,但这里有一个没有人谈论的细微差别:POSTGRES_USER/PASSWORD 将在您的数据库中设置超级用户。对于您的主数据库用户来说,这可能并不总是一个好主意。而是考虑创建一个单独的应用程序用户和数据库。 截至 2021 年 9 月,这可以在 docker 的 PostgreSQL 官方页面上看到:警告/docker-entrypoint-initdb.d 中的脚本只有在使用数据目录启动容器时才会运行那是空的【参考方案2】:

通过使用docker-compose

假设您有以下目录布局:

$MYAPP_ROOT/docker-compose.yml
           /Docker/init.sql
           /Docker/db.Dockerfile

文件:docker-compose.yml

version: "3.3"
services:
  db:
    build:
      context: ./Docker
      dockerfile: db.Dockerfile
    volumes:
      - ./var/pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

文件:Docker/init.sql

CREATE USER myUser;

CREATE DATABASE myApp_dev;
GRANT ALL PRIVILEGES ON DATABASE myApp_dev TO myUser;

CREATE DATABASE myApp_test;
GRANT ALL PRIVILEGES ON DATABASE myApp_test TO myUser;

文件:Docker/db.Dockerfile

FROM postgres:11.5-alpine
COPY init.sql /docker-entrypoint-initdb.d/

组合和启动服务:

docker-compose -f docker-compose.yml up --no-start
docker-compose -f docker-compose.yml start

【讨论】:

【参考方案3】:

使用 docker compose 有一个简单的替代方案(无需创建 Dockerfile)。只需创建一个 init-database.sh:

#!/bin/bash
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE USER docker;
    CREATE DATABASE my_project_development;
    GRANT ALL PRIVILEGES ON DATABASE my_project_development TO docker;
    CREATE DATABASE my_project_test;
    GRANT ALL PRIVILEGES ON DATABASE my_project_test TO docker;
EOSQL

并在卷部分引用它:

version: '3.4'

services:
  postgres:
    image: postgres
    restart: unless-stopped
    volumes:
      - postgres:/var/lib/postgresql/data
      - ./init-database.sh:/docker-entrypoint-initdb.d/init-database.sh
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - 5432:5432

volumes:
  postgres:

【讨论】:

这对我不起作用。似乎volumes指令在docker-entrypoint-initdb.d中创建了一个目录,而不是一个文件。错误是该文件是一个目录。【参考方案4】:

您可以使用以下命令:

docker exec -it yournamecontainer psql -U postgres -c "CREATE DATABASE mydatabase ENCODING 'LATIN1' TEMPLATE template0 LC_COLLATE 'C' LC_CTYPE 'C';"

docker exec -it yournamecontainer psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE postgres TO postgres;"

【讨论】:

ENCODING 'LATIN1' 很奇怪...你必须有非常特殊的需要避免使用utf8【参考方案5】:

您现在可以将 .sql 文件放入 init 目录中:

From the docs

如果您想在从该镜像派生的镜像中进行额外的初始化,请在 /docker-entrypoint-initdb.d 下添加一个或多个 *.sql 或 *.sh 脚本(必要时创建目录)。在入口点调用 initdb 创建默认的 postgres 用户和数据库后,它将运行任何 *.sql 文件并获取该目录中找到的任何 *.sh 脚本,以在启动服务之前进行进一步的初始化。

所以复制你的 .sql 文件就行了。

【讨论】:

用户/密码和数据库创建的环境变量仍然有效。 (9.5.3) 我有一个在本地工作的项目。但是,当我将它移动到 AWS EC2 时,它说找不到 DB。我将 .sql 文件复制到正确的目录中,但它仍然返回该错误。知道我可以在哪里看吗?我是 Docker 新手。 仅当有一个数据库实例绑定到单个卷时才适用,即如果卷文件夹中已有任何内容,它将不会运行任何脚本。这让我很头疼,因为我想为我的每个堆栈服务都有一个单独的数据库初始化程序。 来自docsWarning: scripts in /docker-entrypoint-initdb.d are only run if you start the container with a data directory that is empty; any pre-existing database will be left untouched on container startup. One common problem is that if one of your /docker-entrypoint-initdb.d scripts fails (which will cause the entrypoint script to exit) and your orchestrator restarts the container with the already initialized data directory, it will not continue on with your scripts.【参考方案6】:

我在启动服务后将自定义命令添加到在 CMD 中调用的环境中......我没有使用 postgres,但使用 Oracle:

#set up var with noop command
RUN export POST_START_CMDS=":"
RUN mkdir /scripts
ADD script.sql /scripts
CMD service oracle-xe start; $POST_START_CMDS; tail -f /var/log/dmesg

开始
docker run -d ... -e POST_START_CMDS="su - oracle -c 'sqlplus @/scripts/script' " <image>

.

【讨论】:

【参考方案7】:

您需要在创建用户之前运行数据库。为此,您需要多个进程。您可以在 shell 脚本的子 shell (&) 中启动 postgres,或者使用像 supervisord 这样的工具来运行 postgres,然后运行任何初始化脚本。

supervisord 和 docker 指南https://docs.docker.com/articles/using_supervisord/

【讨论】:

或者只是RUN service postgresql start ; su - postgres -c "psql -c \"CREATE USER test WITH PASSWORD 'test' CREATEDB\"" ; service postgresql stop【参考方案8】:

使用 Postgres 最新映像(ID:b262c8b2fb54)和 Docker 版本 20.10.6,docker-compose 看起来像,

version: '2'

services:
  app:
    # the detail of app
  db:
    image: 'postgres'
    container_name: book-shop-db-postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=bookshop-db

这将创建启动时提到的用户和数据库

【讨论】:

以上是关于如何在 Docker Postgres 的脚本中创建用户/数据库的主要内容,如果未能解决你的问题,请参考以下文章

flyway 无法连接到 docker-entrypoint-initdb.d 脚本中的 postgres 容器

在 docker 的 pgadmin 中添加新的 postgres 服务器

成功等待脚本后如何启动 docker 容器

postgres:从命令行在数据库中创建表

如何在不同的(依赖的)容器启动后对 docker 容器运行 .sql 脚本?

如何使用 Postgres 在 SQLAlchemy 中创建表?