如何在 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_USER
、POSTGRES_PASSWORD
和 POSTGRES_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 < /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 服务器