原生的 PostgreSQL 镜像的制作还是比较合理的,推荐大家深入研究这个镜像的使用操作,能熟悉构建脚本最好不过。
原文链接:https://hub.docker.com/_/postgres
如何使用这个镜像
启动一个 postgres 实例
$ docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
入口点(entrypoint)会使用 initdb
命令创建默认的 postgres
用户和数据库。
postgres 数据库是供用户、实用工具程序和第三方应用程序使用的默认数据库。
postgresql.org/docs
… 或者通过psql
$ docker run -it --rm --network some-network postgres psql -h some-postgres -U postgres
psql (14.3)
Type "help" for help.
postgres=# SELECT 1;
?column?
----------
1
(1 row)
… … 通过docker.com/engine/reference/commandline/stack_deploy/">docker stack deploy
或 docker-compose
例子用于 postgres
的 stack.yml
:
# Use postgres/example user/password credentials
version: '3.1'
services:
db:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: example
adminer:
image: adminer
restart: always
ports:
- 8080:8080
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-30Sx9bu8-1677600497745)(null)]
运行docker stack deploy -c stack.yml postgres
(或 docker-compose -f stack.yml up
等待它初始化完成,然后访问 http://swarm-ip:8080
, http://localhost:8080
, 或 http://host-ip:8080
(视情况而定)
如何扩展这个镜像
有许多方法可以扩展postgres
镜像。在不尝试支持每种可能的用例的情况下,以下是我们发现有用的一些方法。
环境变量
PostgreSQL 镜像使用了几个环境变量,这些变量很容易被忽略。唯一必需的变量是POSTGRES_PASSWORD
,其余变量是可选的。
警告:仅当你使用一个空的数据目录启动容器时,这些 Docker 特定的变量才会生效;任何现有的数据库都将在容器启动时保持不变。
POSTGRES_PASSWORD
这个环境变量是使用 PostgreSQL 镜像所必需的。它不能是空的或未定义的。这个环境变量为 PostgreSQL 设置超级用户密码。默认的超级用户是由环境变量POSTGRES_USER
定义的。
注意1:PostgreSQL 镜像在本地设置了trust
身份验证,因此您可能会注意到从localhost
(在同一个容器内)连接时不需要密码。但是,从不同的主机或容器连接时需要密码。
注意2:此变量定义了 PostgreSQL 实例中的超级用户密码,在最初容器启动期间由initdb
脚本设置。在运行容器时,它对可能会被psql
客户端使用到的 PGPASSWORD
环境变量没有影响,如 https://www.postgresql.org/docs/14/libpq-envars.html 所述。如果使用了PGPASSWORD
,则会将其指定为单独的一个环境变量。
POSTGRES_USER
这个可选的环境变量与POSTGRES_PASSWORD
结合使用来设置用户及其密码。该变量将创建它指定的具有超级用户权限用户和一个同名数据库。如果未指定,则将使用默认用户postgres
。
注意,如果指定了该参数,PostgreSQL 仍然会显示属于该数据库系统的文件将在初始化过程中由用户“postgres”所有
。这是指 postgres 守护进程以 Linux 系统用户(来自镜像中的/etc/passwd
)运行的,因此与这个POSTGRES_USER
选项无关。更多细节请参见“任意 --user
注意事项”部分。
POSTGRES_DB
这个可选环境变量可用于为镜像首次启动时所创建的默认数据库定义不同的名称。如果未指定,则使用POSTGRES_USER
的值。
POSTGRES_INITDB_ARGS
这个可选的环境变量可用于向 postgres initdb
发送参数。它的值是由空格分隔的参数组成的一个字符串,跟 postgres initdb
所要求的一样。这对于添加数据页校验和这样的功能非常有用:-e POSTGRES_INITDB_ARGS="--data-checksums"
。
POSTGRES_INITDB_WALDIR
这个可选的环境变量可用于定义另一个位置来存储 Postgres 的事务日志。默认情况下,事务日志存储在主 Postgres 数据文件夹(PGDATA
)的子目录中。有时候,将事务日志存储在由具有不同性能或可靠性特征的存储器支持的不同目录中是有必要的。
注意:在 PostgreSQL 9.x 中,这个变量是 POSTGRES_INITDB_XLOGDIR
(联想到在 PostgreSQL 10+ 中标志名--xlogdir
变更为--waldir
)。
POSTGRES_HOST_AUTH_METHOD
此可选变量可用于控制所有数据库
、所有用户
和所有地址
的主机连接的认证方法
(auth-method)。如果未指定,则使用scram-sha-256
密码身份验证(这是在 14+ 版本中;而早期版本中则使用MD5
)。在未初始化的数据库上,这将通过以下近似行填充 pg_hba.conf:
echo "host all all all $POSTGRES_HOST_AUTH_METHOD" >> pg_hba.conf
有关可能值及其含义的更多信息,请参阅 PostgreSQL 文档中的pg_hba.conf
。
注意1:不建议使用trust
,因为它允许任何人在没有密码的情况下连接,即使设置了密码(例如通过POSTGRES_PASSWORD
)。有关详细信息,请参阅 PostgreSQL 文档中的 Trust 身份认证。
注意2:如果将POSTGRES_HOST_AUTH_METHOD
设置为trust
,则不需要POSTGRES_PASSWORD
。
注意3:如果将其设置为另一个值(例如 scram-sha-256
),则你可能需要额外的POSTGRES_INITDB_ARGS
来正确初始化数据库(例如 POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256
)。
PGDATA
该可选变量可用于定义另一个位置(例如一个子目录)来存储数据库文件。默认情况下为/var/lib/postgresql/data
。如果你使用的数据卷是文件系统挂载点(例如 GCE 持久磁盘)或无法修改所有权(chowned)为postgres
用户的远程文件夹(例如某些 NFS 挂载),则 Postgres 的 initdb
建议创建一个子目录来包含数据。
例如:
$ docker run -d \
--name some-postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-v /custom/mount:/var/lib/postgresql/data \
postgres
这不是一个特定于 Docker 使用的环境变量。由于此变量由postgres
服务器的二进制文件使用(请参阅 PostgreSQL 文档),因此入口点(entrypoint)脚本会对它加以重视。
Docker 私密信息
作为传递敏感信息的替代方法,可以在一些先前列出的环境变量后添加_FILE
,从容器中存在的文件中加载这些变量的值,从而使初始化脚本从文件中读取密码。特别地,这可用于从存储在/run/secrets/<secret_name>
文件中的 Docker 私密消息中加载密码。例如:
$ docker run --name some-postgres -e POSTGRES_PASSWORD_FILE=/run/secrets/postgres-passwd -d postgres
目前,这仅适用于POSTGRES_INITDB_ARGS
、POSTGRES_PASSWORD
、POSTGRES_USER
、和POSTGRES_DB
。
初始化脚本
如果你想要在派生自此镜像的镜像中进行额外的初始化,请在 /docker-entrypoint-initdb.d
目录下添加一个或多个 *.sql
、*.sql.gz
或 *.sh
脚本(如果必要,创建该目录)。在 entrypoint 调用 initdb
创建默认的 postgres
用户和数据库之后,它将运行任何 *.sql
文件、运行任何可执行的 *.sh
脚本,并在该目录中 source 任何非可执行的 *.sh
脚本以在启动服务之前进行进一步的初始化。
警告:仅在使用空数据目录启动容器时,/docker-entrypoint-initdb.d
中的脚本才会被运行;任何现存的数据库都将在容器启动时保持不变。一个常见的问题是,如果你的 /docker-entrypoint-initdb.d
中的脚本失败(这将导致 entrypoint 脚本退出),并且你的编排器使用已初始化的数据目录重新启动容器,它将不会继续运行您的脚本。
例如,要添加一个额外的用户和数据库,请将以下内容添加到 /docker-entrypoint-initdb.d/init-user-db.sh
:
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
这些初始化文件将按照当前语言环境定义的排序名称顺序执行,其默认值为 en_US.utf8
。任何 *.sql
文件将由 POSTGRES_USER
执行,默认为 postgres
超级用户。建议在 *.sh
脚本中运行的任何 psql
命令都使用 --username "$POSTGRES_USER"
标志以 POSTGRES_USER
身份执行。由于容器内 Unix 套接字连接的 trust
身份验证存在,该用户将能够连接而无需密码。
此外,截至 docker-library/postgres#253,这些初始化脚本是以 postgres
用户的身份运行的(或使用 --user
标志指定给 docker run
的“半随意用户”;有关更多详细信息,请参见“任意 --user
注意事项”一节)。另外,自 docker-library/postgres#440 起,为这些初始化脚本启动的临时守护程序仅在 Unix 套接字上侦听,因此任何 psql
的使用都应该删除主机名(hostname)部分(请参见 docker-library/postgres#474(注释)的示例)。
数据库配置
有很多设置 PostgreSQL 服务器配置的方法。有关可配置项的信息,请查看你正在运行的 PostgreSQL 版本的PostgreSQL文档。以下是几种设置配置的选项:
-
使用自定义配置文件。创建一个配置文件并将其放入容器中。如果你需要配置文件的起点,可以使用 PostgreSQL 提供的示例,该示例位于容器中的
/usr/share/postgresql/postgresql.conf.sample
(在Alpine版本中为/usr/local/share/postgresql/postgresql.conf.sample
)。- 重要提示:你必须设置
listen_addresses = '*'
,以便其他容器可以访问 postgres。
$ # 获取默认配置 $ docker run -i --rm postgres cat /usr/share/postgresql/postgresql.conf.sample > my-postgres.conf $ # 自定义这个配置(即:my-postgres.conf) $ # 使用自定义配置运行 postgres $ docker run -d --name some-postgres -v "$PWD/my-postgres.conf":/etc/postgresql/postgresql.conf -e POSTGRES_PASSWORD=mysecretpassword postgres -c 'config_file=/etc/postgresql/postgresql.conf'
- 重要提示:你必须设置
-
在运行命令中直接设置选项。入口点脚本会将传递给 docker 命令的任何选项传递给
postgres
服务器守护程序。从PostgreSQL文档中我们可以看到,任何在.conf
文件中可用的选项都可以通过-c
来设置。$ docker run -d --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword postgres -c shared_buffers=256MB -c max_connections=200
语言环境定制
你可以使用一个简单的Dockerfile
扩展基于 Debian 的镜像,以设置不同的语言环境。以下示例将默认语言环境设置为de_DE.utf8
:
dockerfile">FROM postgres:14.3
RUN localedef -i de_DE -c -f UTF-8 -A /usr/share/locale/locale.alias de_DE.UTF-8
ENV LANG de_DE.utf8
由于数据库初始化只发生在容器启动时,因此这允许我们在创建之前设置语言环境。
另外值得注意的是,从 Postgres 15 开始,基于 Alpine 的变体支持 ICU 语言环境。以前基于 alpine 的 Postgres 版本不支持语言环境;有关详细信息,请参见 musl 文档中的“字符集和语言环境”。
你可以使用POSTGRES_INITDB_ARGS
在基于 alpine 的镜像中设置语言环境,来设置一个不同的语言环境。下面的例子将一个新初始化的数据库的默认区域设置为de_DE.utf8
:
$ docker run -d -e LANG=de_DE.utf8 -e POSTGRES_INITDB_ARGS="--locale-provider=icu --icu-locale=de-DE" -e POSTGRES_PASSWORD=mysecretpassword postgres:15-alpine
附加扩展
当使用默认的(基于Debian的)变体时,安装其他扩展(如:PostGIS)应该很简单,只需安装相关的软件包即可(请参见github.com/postgis/docker-postgis以获得一个具体的示例)。
当使用Alpine变体时,任何未列在postgres-contrib中的 postgres 扩展都需要在您自己的镜像中编译(同样,请参见github.com/postgis/docker-postgis以获得一个具体的示例)。
任意 --user
说明
从docker-library/postgres#253开始,该镜像支持通过 docker run
上的 --user
以(大多数)任意用户身份运行。从docker-library/postgres#1018开始,这也适用于Alpine变体。
要注意的主要注意事项是 postgres
不关心它作为什么 UID 运行(只要 /var/lib/postgresql/data
的所有者匹配即可),但是 initdb
确实关心(并需要用户存在于 /etc/passwd
中):
$ docker run -it --rm --user www-data -e POSTGRES_PASSWORD=mysecretpassword postgres
这个数据库系统的文件将属于用户“www-data”。
...
$ docker run -it --rm --user 1000:1000 -e POSTGRES_PASSWORD=mysecretpassword postgres
initdb: could not look up effective user ID 1000: user does not exist
解决此问题的三种最简单方法:
-
允许镜像使用 nss_wrapper 库来“伪造”
/etc/passwd
内容(有关详细信息,请参见docker-library/postgres#448) -
从宿主机上以只读方式绑定挂载
/etc/passwd
如果你需要的 UID 是宿主机上的有效用户):$ docker run -it --rm --user "$(id -u):$(id -g)" -v /etc/passwd:/etc/passwd:ro -e POSTGRES_PASSWORD=mysecretpassword postgres 这个数据库系统的文件将属于用户“jsmith”。 ...
-
与最终运行分开,单独先初始化目标目录(在两者中间插入一个
chown
操作):$ docker volume create pgdata $ docker run -it --rm -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=mysecretpassword postgres 属于该数据库系统的文件将由用户“postgres”拥有。 ... (如果 PostgreSQL 成功初始化并等待连接,那么可以停止它) $ docker run -it --rm -v pgdata:/var/lib/postgresql/data bash chown -R 1000:1000 /var/lib/postgresql/data $ docker run -it --rm --user 1000:1000 -v pgdata:/var/lib/postgresql/data postgres LOG: database system was shut down at 2017-01-20 00:03:23 UTC LOG: MultiXact member wraparound protections are now enabled LOG: autovacuum launcher started LOG: database system is ready to accept connections
注意事项
如果在容器启动postgres
时没有数据库,那么postgres
将为你创建此默认数据库。虽然这是postgres
的预期行为,但这意味着在此期间它将不接受传入连接。当使用自动化工具(如:docker-compose
)同时启动多个容器时,这可能会引起问题。
还请注意,容器的默认/dev/shm
大小为 64MB。如果共享内存用尽,您将遇到ERROR:could not resize shared memory segment ...: No space left on device
的错误。你需要向docker run
传递--shm-size=256MB
之类的参数,或者在docker-compose
中指定。
数据存储位置
重要提示:有多种方法可以存储应用程序在 Docker 容器中运行时使用的数据。我们鼓励postgres
镜像的用户熟悉可用的选项,包括:
- 让 Docker 使用自己的内部卷管理将数据库文件写入宿主机系统上的磁盘,以管理数据库数据的存储。这是默认的方式,对用户而言易于使用而且相当透明。缺点是,对于直接运行在宿主机系统上的工具和应用程序(即容器外部),文件可能难以定位。
- 在宿主机系统(容器外部)上创建一个数据目录,并将其挂载到容器内部可见的目录。这将在宿主机系统上的一个已知位置放置数据库文件,并使宿主机系统上的工具和应用程序很容易访问这些文件。缺点是用户需要确保目录存在,并且在宿主机系统上正确设置了目录权限和其他安全机制。
Docker文档是理解不同存储选项和变化的一个很好的起点,有许多博客和论坛帖子讨论并给出了这方面的建议。在这里我们将简单地展示上述后一个选项的基本过程:
-
在宿主机系统上适当的卷上创建一个数据目录,例如:
/my/own/datadir
。 -
以以下方式启动你的
postgres
容器:$ docker run --name some-postgres -v /my/own/datadir:/var/lib/postgresql/data -e POSTGRES_PASSWORD=mysecretpassword -d postgres:tag
该命令的-v /my/own/datadir:/var/lib/postgresql/data
部分将底层宿主机系统中的/my/own/datadir
目录挂载为容器中的/var/lib/postgresql/data
,默认情况下 PostgreSQL 将在这里写入它的数据文件。