在Postgres中以单用户模式创建表

8

我正在尝试从postgres镜像创建Dockerfile。存储库表示,初始化应由将shell脚本放置在/docker-entrypoint-initdb.d/中处理。我按照在线示例编写了以下脚本:

#!/bin/bash
echo "******CREATING DOCKER DATABASE******"
gosu postgres postgres --single <<- EOSQL
   CREATE DATABASE orpheus;
   CREATE USER docker WITH ENCRYPTED PASSWORD 'pwd_docker';
   GRANT ALL PRIVILEGES ON DATABASE orpheus to docker;
   CREATE TABLE profiles ( \
     profile_id    SERIAL UNIQUE PRIMARY KEY, \
     user_id integer NOT NULL UNIQUE, \
     profile_photo_id integer NOT NULL UNIQUE, \
     age integer \
   );
   CREATE TABLE hidden_user ( \
     owner_id    integer NOT NULL PRIMARY KEY, \
     target_id integer NOT NULL \
   );
EOSQL
echo ""
echo "******DOCKER DATABASE CREATED******"

加上反斜杠是必要的,否则我会得到解析错误。该脚本运行无误,除了CREATE TABLE命令之外的所有命令似乎都产生了效果。

这是否意味着在单用户模式下不支持表格创建?如果是这样,有没有更好的方法让dockerfile在postgres中设置图像并创建表格?


2
单用户模式仅用于灾难恢复,不应用于其他任何目的:http://postgresql.nabble.com/BUG-12670-Can-t-create-postgis-extension-in-single-user-mode-tp5835585p5835592.html 问题是:你为什么要在单用户模式下启动Postgres? - user330315
1
在官方 docker postgres 存储库中建议使用:"如果您需要在初始化的一部分中执行 SQL 命令,则强烈建议使用 Postgres 单用户模式。" - https://registry.hub.docker.com/_/postgres/ - sdab
2
这很明显是个糟糕的建议。 - user330315
@a_horse_with_no_name 我认为他们试图避免在所需的初始化完成之前,数据库启动并对一般连接可用。最好使用listen_addresses = ''并使用unix套接字或listen_addresses ='127.0.0.1, ::1'(通过postgres-c参数)并使用回环psql`会话来启动它,执行所需操作,然后重新启动Pg以进行一般访问,但是Docker的人似乎更喜欢快速解决方案而不是健壮的解决方案... - Craig Ringer
3个回答

10

@a_horse_with_no_name对我的评论帮了我很大的忙。我决定丢弃单用户模式,即使它是“推荐”的。相反,我使用pg_ctl启动postgres,在其中加载一些包含表创建的SQL文件,并使用pg_ctl停止服务器。

我的Shell脚本看起来像这样:

#!/bin/bash
echo "******CREATING DOCKER DATABASE******"

echo "starting postgres"
gosu postgres pg_ctl -w start

echo "bootstrapping the postgres db"
gosu postgres psql -h localhost -p 5432 -U postgres -a -f /db/bootstrap.sql

echo "initializing tables"
gosu postgres psql -h localhost -p 5432 -U postgres -d orpheus -a -f /db/setup.sql

echo "stopping postgres"
gosu postgres pg_ctl stop

echo "stopped postgres"


echo ""
echo "******DOCKER DATABASE CREATED******"

1
这对我非常有效。感谢这个提示;我开始转圈找不到方向了。 - Jondlm
你节省了我好几个小时的时间... 非常感谢。 - Vitaly Isaev
几乎完美 - 不过我把“初始化表”中的用户切换成了在引导程序中创建的用户 :) - GlynD

5

如果您希望在完成必要的设置之前防止用户访问PostgreSQL,请仅使用回环访问或仅使用Unix套接字启动它,进行初始化,然后重新启动以进行一般访问。

我不懂Docker,但如果您在常规环境中执行此操作,可以采取以下步骤:

mkdir -p /db/temp_socket
chown -r postgres:postgres /db

PGHOST=/db/temp_socket pg_ctl -D /path/to/datadir -o "-c listen_addresses='' -c unix_socket_directories='/db/temp_socket'" -l "/db/dbsetup.log" -w start

# Do your work
PGHOST=/db/temp_socket psql -f some_script

PGHOST=/db/temp_socket pg_ctl -D /path/to/datadir -m fast -w stop

pg_ctl -D /path/to/datadir -w start ...normalstartupoptionsblah...

即不要让PostgreSQL在任何TCP/IP套接字上侦听,并使用非默认的unix_socket_directories进行设置。完成设置后,使用默认(或配置的)unix_socket_directorieslisten_addresses重新启动它,一旦准备好进行一般访问。

您还可以选择:

  • 修改pg_hba.conf只允许设置用户/仅限于环回地址等访问
  • 开始Pg
  • 完成设置
  • 用生产环境中的文件替换pg_hba.conf
  • pg_ctl reloadSELECT pg_reload_conf() 加载新的设置并允许一般访问

...但是这将允许应用程序连接,然后在设置阶段拒绝其认证;这可能不是您想要的,而且并非所有应用程序都能正确处理这种情况。


1
我喜欢这个答案。我们的应用容器使用一个小脚本来等待 postgres 容器接受 TCP 连接,然后再尝试连接它。让它短暂地上线以导入模式是一个问题,使用本地套接字很好地解决了这个问题。 - Louis Simoneau

0

我测试了你的脚本,它几乎可以正常工作。使用Postgresql 9.4,我成功让以下内容工作:

gosu postgres postgres --single -jE <<- EOSQL
   CREATE DATABASE orpheus;
EOSQL
echo    
gosu postgres postgres --single -jE orpheus <<- EOSQL
   CREATE USER docker WITH ENCRYPTED PASSWORD 'pwd_docker';
   GRANT ALL PRIVILEGES ON DATABASE orpheus to docker;
   CREATE TABLE profiles (
     profile_id    SERIAL UNIQUE PRIMARY KEY,
     user_id integer NOT NULL UNIQUE,
     profile_photo_id integer NOT NULL UNIQUE,
     age integer
   );
   CREATE TABLE hidden_user (
     owner_id    integer NOT NULL PRIMARY KEY,
     target_id integer NOT NULL
   );
EOSQL
echo

基本上我不得不将脚本分成两部分,因为PostgreSQL抱怨create database不能在多行脚本中使用。另一个部分只是在第二个命令中添加了数据库名称orpheus

就这样


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