如何将 PostgreSQL 安装部署为 Docker 容器

PostgreSQL,也称为 Postgres,是领先的对象关系数据库系统。 它之所以受欢迎,是因为它高度符合 SQL 标准,并且包含可简化大规模处理复杂数据集的附加功能。

PostgreSQL 使用传统的客户端-服务器架构,因此您需要独立于应用程序代码运行它。 在本篇文章中,我们将部署 PostgreSQL 服务器实例,将其作为 Docker 容器。 这避免了将包添加到您的主机,并有助于将数据库与堆栈的其他部分隔离开来。 在继续之前,请确保已经安装了 Docker。

PostgreSQL 在 Docker Hub 上有一个官方镜像,它有几种不同的变体。 标签可让我们在从 v9 到 v14 的主要 PostgreSQL 版本之间进行选择,并选择用作基础映像的操作系统。 提供了 Alpine 、Debian Stretch 和 Debian Bullseye 。

出于本教程的目的,我们将使用 postgres:14 标签,它在 Bullseye 上提供 PostgreSQL 14。 我们也可以自由选择不同的版本以满足自己的要求。

使用 docker run 命令启动 PostgreSQL 容器:

$ docker run -d \
    --name postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD=<password> \
    -v postgres:/var/lib/postgresql/data \
    postgres:14

我们必须为 POSTGRES_PASSWORD 环境变量提供一个值。 这定义了将分配给 Postgres 的默认超级用户帐户的密码。 用户名默认为 postgres,但可以通过设置 POSTGRES_USER 环境变量来更改。

-v 选项用于将 Docker 卷挂载到 PostgreSQL 容器的数据目录。 引用了一个名为 postgres 的命名卷; Docker 将创建它或重新附加该卷(如果它已经存在)。 我们应该使用卷将数据库存储在容器之外。 如果没有,将在容器停止时使用我们数据。

PostgreSQL 默认监听 5432 端口。 容器端口通过 -p 选项绑定到 Docker 主机上的端口 5432。 -d 标志用于在分离模式下启动容器,有效地使其成为一个后台服务,一直运行直到被 docker stop 停止。

以文件形式提供密码

如果对以纯文本 CLI 标志的形式提供超级用户密码感到不舒服,我们可以通过卷将其作为文件注入。 然后,应该设置 POSTGRES_PASSWORD_FILE 环境变量,为 Postgres 指明该文件的路径:

$ docker run -d \
    --name postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password \
    -v ./postgres-password.txt:/run/secrets/postgres-password
    -v postgres:/var/lib/postgresql/data \
    postgres:14

此方式也适用于 POSTGRES_USER 和其他受支持的环境变量。

连接到您的数据库

由于 PostgreSQL 绑定到上面的端口 5432,我们可以从任何兼容的客户端连接到 localhost:5432 上的数据库。 使用在启动容器时分配的凭据作为环境变量。

Docker 映像还包括我们可以使用 docker exec 调用的 psql 二进制文件。 使用它可以从容器内的 PostgreSQL shell 快速与数据库交互。

$ docker exec -it postgres psql -U postgres

从其他 Docker 容器连接

创建 Docker 网络是从同一主机上的其他容器访问 PostgreSQL 的首选方式。 这避免了绑定 Postgres 服务器的端口并可能将服务暴露给主机更广泛的网络。

创建一个 Docker 网络:

$ docker network create my-app

使用带有 –network 选项的 docker run 启动 Postgres 容器并连接到网络:

$ docker run -d \
    --name postgres \
    --network my-app \
    -e POSTGRES_PASSWORD=<password> \
    -v postgres:/var/lib/postgresql/data \
    postgres:14

现在将您的应用程序容器加入同一个网络:

$ docker run -d
    --name api
    --network my-app
    my-api:latest

网络中的容器可以使用 postgres 主机名访问 Postgres,因为这是分配给 Postgres 容器的名称。 使用端口 5432 完成连接。

配置 PostgreSQL

我们可以在 docker run 命令中的映像名称后使用 -c 选项传递 PostgreSQL 服务器选项:

$ docker run -d \
    --name postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD=<password> \
    -v postgres:/var/lib/postgresql/data \
    postgres:14 -c max_connections=100

镜像名称之后的所有内容都传递给在容器中启动的命令。 对于 Postgres 镜像,此命令将是 PostgreSQL 服务器二进制文件。

当您设置多个选项的值时,您可以使用自定义配置文件。 需要使用另一个 Docker 卷将文件挂载到容器中,然后提供一个 -c 选项来指示 Postgres 在哪里查找:

$ docker run -d \
    --name postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD=<password> \
    -v ./postgres.conf:/etc/postgresql/postgresql.conf \
    -v postgres:/var/lib/postgresql/data \
    postgres:14 -c config_file=/etc/postgresql/postgresql.conf

此示例使用 Docker 绑定挂载将工作目录中的 postgres.conf 文件挂载到容器的 /etc/postgresql 目录中。

初始化数据库

Docker 镜像支持放置在 /docker-entrypoint-initdb.d 目录中的种子文件。 将执行任何 .sql 或 .sql.gz 文件来初始化数据库。 这发生在创建默认用户帐户和 postgres 数据库之后。 还可以添加 .sh 文件来运行任意 shell 脚本。 所有脚本都按字母顺序执行。

这种机制意味着您只需要一组按正确顺序命名的 SQL 或 shell 脚本来为数据库初始化。 使用带有 -v 选项的 docker run 命令将它们安装到新容器中:

$ docker run -d \
    --name postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD=<password> \
    -v ./db-seed-files/:/etc/docker-entrypoint-initdb.d \
    -v postgres:/var/lib/postgresql/data \
    postgres:14

只有当 Postgres 数据目录为空时,才会使用初始化脚本。 出于实际目的,这意味着它们将在容器第一次启动时运行,并附加一个新的空卷。

创建自定义数据库镜像

可以选择将配置文件和初始化脚本封装在自己的 Docker 镜像中。 这将让任何有权访问该镜像的人启动一个为您的应用程序预先配置的新 PostgreSQL 实例。 这是一个简单的 Dockerfile,可以使用它:

Dockerfile

FROM postgres:14
COPY postgres.conf /etc/postgresql/postgresql.conf
COPY db-seed-files/ /etc/docker-entrypoint-initdb.d/
CMD ["-c", "config_file=/etc/postgresql/postgresql.conf"]

构建自定义镜像:

$ docker build -t custom-postgres:latest .

Dockerfile 中的构建指令将从您的工作目录复制 PostgreSQL 配置文件和初始化脚本,并将它们嵌入到容器镜像中。 现在您可以在不手动提供资源的情况下启动数据库容器:

$ docker run -d \
    --name custom-postgres \
    -p 5432:5432
    -e POSTGRES_PASSWORD=<password> \
    -v postgres:/var/lib/postgresql/data \
    custom-postgres:latest

你应该容器化你的生产数据库吗?

决定是否在 Docker 中运行数据库可能很困难。 容器化 PostgreSQL 使设置体验更容易,但有时维护起来更具挑战性。 在管理容器时需要小心,以免将来丢失数据。 Docker 还增加了适度的性能开销,当预计数据库将处理非常大的数据量时,这是值得考虑的。

Docker 的好处是增加了可移植性、易于扩展和开发人员效率。 将数据库容器化让任何人都可以使用 Docker 启动一个新实例,而无需先手动安装和配置 PostgreSQL。 因此,为您的 PostgreSQL 数据库编写一个 Dockerfile 来添加您的配置文件和 SQL 种子脚本是帮助开发人员快速启动新环境的好方法。

总结

PostgreSQL 是一种基于 SQL 的高级数据库引擎,它增加了对象关系功能。 虽然您可以选择在生产环境中运行传统部署,但使用容器化实例可以简化设置并帮助开发人员快速启动自己的基础架构。

Dockerized 部署最关键的方面是确保您使用卷来存储数据。 这将允许您停止、替换和更新您的容器到更高版本的镜像,而不会丢失您的数据库。 除了存储之外,还应该评估您将如何连接到 Postgres 并避免将端口绑定到您的主机,除非必要。 从另一个容器连接时,最好使用共享的 Docker 网络以方便访问。