快速上手:使用 Docker Compose 轻松运行 PostgreSQL
在现代软件开发中,数据库是几乎所有应用程序不可或缺的核心组件。作为开源关系型数据库领域的佼佼者,PostgreSQL 以其强大的功能、稳定性和可靠性赢得了广泛的认可。然而,传统的数据库安装和配置过程往往繁琐且容易遇到各种依赖问题。这时,容器化技术,尤其是 Docker,就显得尤为重要。
Docker 提供了轻量级、可移植且自给自足的环境,使得应用程序及其所有依赖项可以打包在一起,并在任何地方一致地运行。而对于需要管理多个容器协同工作的复杂应用(比如一个 Web 应用、一个数据库和一个缓存服务),Docker Compose 应运而生。它允许你使用一个 YAML 文件来定义和管理多容器 Docker 应用,极大地简化了应用的部署和管理流程。
本文将带你快速上手,学习如何使用 Docker Compose 来运行 PostgreSQL 数据库,并详细讲解其核心配置和最佳实践,让你轻松告别繁琐的数据库安装,专注于应用的开发。
为什么选择使用 Docker Compose 运行 PostgreSQL?
在使用 Docker Compose 运行 PostgreSQL 之前,让我们先理解这种方式带来的显著优势:
- 环境隔离与一致性: Docker Compose 为 PostgreSQL 创建了一个独立的环境。这意味着它不会干扰你主机上的其他软件或数据库安装,也不会受到它们的影响。无论在开发、测试还是生产环境,只要 Docker 和 Docker Compose 环境就绪,PostgreSQL 就能以完全相同的方式运行,消除了“在我的机器上可以跑”的问题。
- 简化安装与配置: 你不再需要手动下载安装包、配置环境变量、初始化数据库。只需编写一个简单的 YAML 文件,运行一个命令,Docker Compose 就会自动完成镜像的下载、容器的创建和配置。
- 易于管理: 使用一个文件管理数据库服务(启动、停止、重启、删除),比手动管理进程或服务要高效得多。
- 数据持久化: Docker Compose 提供了强大的卷(Volumes)管理机制,可以确保你的数据库数据在容器停止、删除甚至更新后依然安全地保留。
- 依赖管理与组网: 如果你的应用(如后端服务)需要连接到数据库,Docker Compose 可以轻松地将它们放在同一个网络中,并允许服务之间通过服务名称互相发现和通信,无需复杂的网络配置。
- 版本控制: 数据库配置以
docker-compose.yml
文件的形式存在,可以像代码一样被版本控制起来,方便团队协作和回溯。
前提条件
在开始之前,请确保你的系统上已经安装了 Docker 和 Docker Compose。
- 安装 Docker: 访问 Docker 官方网站,下载并安装适用于你操作系统的 Docker Desktop (macOS, Windows) 或 Docker Engine (Linux)。
- 安装 Docker Compose: 在 Docker Desktop 中,Docker Compose 通常是内置的。对于 Linux 用户,可能需要单独安装。请参考 Docker Compose 官方安装指南。较新版本的 Docker Compose 是作为一个 Docker CLI 插件提供的,使用
docker compose
命令(无-
)。旧版本则使用docker-compose
命令(有-
)。本文将主要使用较新的docker compose
命令格式。
你可以通过运行以下命令来验证 Docker 和 Docker Compose 是否已正确安装:
bash
docker --version
docker compose version
如果命令输出了版本信息,说明你已经准备就绪。
核心:docker-compose.yml
文件
Docker Compose 的魔力集中在一个名为 docker-compose.yml
的文本文件中。这个文件使用 YAML 格式编写,用于定义你的服务、网络和卷等。
创建一个新的目录来存放你的项目文件,并在该目录中创建一个 docker-compose.yml
文件。
bash
mkdir my-postgres-app
cd my-postgres-app
touch docker-compose.yml
现在,打开 docker-compose.yml
文件,我们将逐步构建一个用于运行 PostgreSQL 的配置。
1. 基本的 PostgreSQL 服务配置
最简单的情况,我们只需要指定要使用的 PostgreSQL 镜像和一些必要的环境变量。
“`yaml
version: ‘3.8’ # 指定 Docker Compose 文件格式版本
services:
db: # 定义一个名为 ‘db’ 的服务,你可以起任何名字,比如 ‘postgres’
image: postgres:15 # 指定使用的 PostgreSQL 镜像,这里使用官方的 15 版本
restart: always # 设置容器退出时总是重启,保证服务高可用
environment: # 设置环境变量,用于配置 PostgreSQL
POSTGRES_PASSWORD: your_strong_password # !! 重要:设置 PostgreSQL 超级用户的密码
# POSTGRES_USER: myuser # 可选:设置超级用户的用户名,默认是 ‘postgres’
# POSTGRES_DB: mydatabase # 可选:设置默认创建的数据库名称,默认是和用户名一样
# ports: # 可选:将容器内部的 PostgreSQL 端口映射到主机上
# - "5432:5432" # 格式是 主机端口:容器端口
# volumes: # 数据持久化配置,非常重要!后面会详细讲解
# - db_data:/var/lib/postgresql/data # 将容器内数据目录映射到具名卷 'db_data'
volumes: # 如果使用了具名卷,需要在这里定义
db_data: # 定义一个名为 ‘db_data’ 的具名卷
“`
解释:
version: '3.8'
: 指定 Docker Compose 文件的版本。不同版本支持不同的特性。3.8
是一个比较常用的版本。services:
: 在这里定义你的所有服务(容器)。每个服务都有一个名称(例如db
)。db:
: 这是我们给 PostgreSQL 服务起的名字。在 Docker Compose 网络中,其他服务可以通过这个名字来访问 PostgreSQL。image: postgres:15
: 指定构建此服务所使用的 Docker 镜像。postgres
是官方 PostgreSQL 镜像的名称,:15
指定了使用 15 版本。你也可以使用latest
,但推荐使用特定版本以确保环境稳定。restart: always
: 设置容器的重启策略。always
表示无论容器如何退出(正常停止、错误退出等),Docker 都会尝试重启它。这有助于提高服务的可用性。environment:
: 用于设置传递给容器的环境变量。这是配置 PostgreSQL 的主要方式。POSTGRES_PASSWORD
: 这是最重要且必须设置的环境变量! 它指定了 PostgreSQL 超级用户(默认为postgres
)的密码。请务必将其替换为一个强密码! 不要使用示例中的your_strong_password
。POSTGRES_USER
(可选): 指定超级用户的用户名。如果未设置,默认为postgres
。POSTGRES_DB
(可选): 指定在容器启动时自动创建的数据库名称。如果未设置,默认数据库的名称将与POSTGRES_USER
相同。
ports:
(可选): 用于将容器内部的端口映射到主机上。PostgreSQL 默认监听 5432 端口。将5432:5432
映射出来后,你就可以直接从主机上使用数据库客户端(如psql
或 pgAdmin)连接到这个数据库。注意:在生产环境中,通常不建议将数据库端口直接暴露到公网,而是在 Docker Compose 网络内部供其他应用容器访问。volumes:
(重要): 用于数据持久化。这是确保数据库数据不会随着容器的删除而丢失的关键配置。在上面的基本配置中,我们注释掉了这一行,后面会详细讲解如何正确配置。volumes:
(在服务定义之外): 用于定义具名卷(Named Volumes)。如果服务中使用了具名卷,需要在文件的顶层volumes
部分进行定义。
2. 运行你的 PostgreSQL 服务
保存 docker-compose.yml
文件。现在,在包含这个文件的目录下,打开终端,运行以下命令来启动你的 PostgreSQL 服务:
bash
docker compose up -d
解释:
docker compose
: 调用 Docker Compose CLI 工具。up
: 构建、(如果需要)创建、启动和连接服务。-d
: 以“分离”(detached)模式运行,这意味着容器将在后台运行,不会阻塞你的终端。
第一次运行 docker compose up -d
时,Docker Compose 会:
- 查找
docker-compose.yml
文件。 - 根据
image: postgres:15
下载 PostgreSQL 镜像(如果本地不存在)。 - 创建一个名为
my-postgres-app_db_1
(或类似名称,格式通常是目录名_服务名_序号
) 的容器。 - 设置你在
environment
中指定的环境变量。 - (如果配置了 ports)在主机上打开指定的端口并将其转发到容器内部。
- (如果配置了 volumes)创建或查找指定的卷,并将其挂载到容器内 PostgreSQL 数据目录 (
/var/lib/postgresql/data
)。 - 启动 PostgreSQL 服务。
你可以使用以下命令检查服务是否正在运行:
bash
docker compose ps
输出应该类似于:
NAME COMMAND SERVICE STATUS PORTS
my-postgres-app_db_1 "docker-entrypoint.s…" db running (healthy) 0.0.0.0:5432->5432/tcp # 如果你映射了端口
STATUS
列显示 running
,表示容器已成功启动。如果配置了健康检查(后面会讲),可能会显示 running (healthy)
。
要查看服务的日志,以便进行调试或查看启动过程:
bash
docker compose logs db
要停止服务:
bash
docker compose down
这将停止并删除由 docker compose up
创建的容器和默认网络。注意:docker compose down
默认不会删除卷!这是为了保护你的数据。 如果你想同时删除卷(慎用!会导致数据丢失),可以使用 -v
标志:docker compose down -v
。
关键:数据持久化 (Volumes)
上面的基本配置有一个严重的问题:如果你停止并删除了容器 (docker compose down
),所有存储在数据库中的数据都会丢失,因为数据是存储在容器的可写层中的,而可写层会随着容器的删除而消失。
为了解决这个问题,我们需要使用 Docker 的卷(Volumes)功能将容器内部的数据目录 (/var/lib/postgresql/data
是 PostgreSQL 存储数据文件的默认位置) 映射到 Docker 管理的持久化存储区域。
有两种主要的卷类型:
- 具名卷 (Named Volumes): 这是推荐用于数据库数据的方法。具名卷由 Docker 管理,它们在主机文件系统的特定位置存储数据,但具体位置通常不重要,你只需要知道卷的名称。Docker 负责卷的创建、管理和挂载。它们生命周期独立于容器。
- 绑定挂载 (Bind Mounts): 允许你将主机文件系统上的一个目录直接挂载到容器内的某个路径。这更常用于将源代码或配置文件挂载到容器中进行开发,或者让容器访问主机上的特定文件或目录。虽然也可以用于数据持久化,但不如具名卷灵活,尤其是在权限管理和跨平台方面。
推荐使用具名卷来持久化 PostgreSQL 数据。
修改你的 docker-compose.yml
文件,添加卷配置:
“`yaml
version: ‘3.8’
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: your_strong_password # !! 务必修改为你的强密码
# POSTGRES_USER: myuser
# POSTGRES_DB: mydatabase
ports:
- "5432:5432" # 暂时保留端口映射方便测试连接
volumes:
- db_data:/var/lib/postgresql/data # 将具名卷 'db_data' 挂载到容器的 /var/lib/postgresql/data 目录
volumes: # 在 services 之外定义具名卷
db_data: # 定义一个名为 ‘db_data’ 的具名卷
# 可选的配置,例如指定驱动或标签
# driver: local
# driver_opts:
# o: bind
# type: none
# device: /path/on/your/host # 如果想指定主机路径,这实际上变成了 Bind Mount 的高级用法
“`
解释:
- 在
services -> db
下增加了volumes:
配置。- db_data:/var/lib/postgresql/data
: 这一行表示将名为db_data
的具名卷挂载到容器内部的/var/lib/postgresql/data
路径。 Docker 会确保/var/lib/postgresql/data
目录的内容被存储在db_data
这个卷中。
- 在文件的最底部增加了顶级的
volumes:
部分。db_data:
: 这是一个简单的定义,告诉 Docker Compose 有一个名为db_data
的卷需要管理。默认情况下,Docker 会创建一个本地卷。
现在,再次运行 docker compose up -d
。Docker Compose 会自动创建 db_data
卷(如果不存在),并将其挂载到容器。
你可以通过 docker volume ls
命令查看 Docker 管理的卷列表。
bash
docker volume ls
你会看到一个类似于 my-postgres-app_db_data
的卷(名称通常是 目录名_卷名
)。
即使你运行 docker compose down
停止并删除了容器,这个卷及其中的数据仍然保留。下次你运行 docker compose up -d
时,Docker Compose 会使用同一个 db_data
卷创建新的容器,你的数据将得以恢复。只有使用 docker compose down -v
或 docker volume rm
命令才会删除卷和数据。
访问你的 PostgreSQL 数据库
启动容器后,你可以通过几种方式访问数据库:
1. 使用 psql
连接容器内部
你可以直接进入运行中的 PostgreSQL 容器,使用其内部的 psql
命令行客户端进行操作。
首先,找到容器的名称或 ID(docker compose ps
)。然后执行:
bash
docker compose exec db psql -U postgres
解释:
docker compose exec db
: 在名为db
的服务容器中执行命令。psql
: PostgreSQL 的命令行客户端。-U postgres
: 使用postgres
用户连接。当提示输入密码时,输入你在docker-compose.yml
中为POSTGRES_PASSWORD
设置的值。
连接成功后,你就可以像在本地安装的 PostgreSQL 中一样执行 SQL 命令了。
2. 使用主机上的 psql
或 GUI 工具 (如果端口已映射)
如果你在 docker-compose.yml
中配置了 ports: - "5432:5432"
,你可以直接从主机上使用任何 PostgreSQL 客户端工具连接到 localhost
或 127.0.0.1
的 5432 端口。
-
使用主机上的
psql
: 如果你的主机上也安装了psql
,可以这样连接:bash
psql -h localhost -U postgres -p 5432
输入密码即可。 -
使用 GUI 工具 (如 pgAdmin, DBeaver): 打开你喜欢的数据库 GUI 工具,创建新的连接,配置如下:
- Host/Server Address:
localhost
或127.0.0.1
- Port:
5432
- Database:
postgres
(或你在POSTGRES_DB
中设置的名称) - Username:
postgres
(或你在POSTGRES_USER
中设置的名称) - Password: 你在
POSTGRES_PASSWORD
中设置的密码
- Host/Server Address:
连接成功后,你就可以通过图形界面管理你的数据库了。
进阶配置与最佳实践
为了构建更健壮、更灵活的 Docker Compose 应用,我们需要了解一些进阶配置。
1. 自定义用户和数据库
在上面的示例中,我们使用了默认的 postgres
用户和数据库。在实际应用中,通常需要创建特定的用户和数据库。你可以通过环境变量来实现:
“`yaml
version: ‘3.8’
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: your_strong_password # 超级用户密码
POSTGRES_USER: myuser # 指定新的超级用户用户名
POSTGRES_DB: myapp_db # 指定启动时创建的数据库名
ports:
- "5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
“`
使用此配置启动容器后,PostgreSQL 将创建一个名为 myuser
的超级用户,并创建一个名为 myapp_db
的数据库。连接时,你就需要使用 myuser
和对应的密码,连接到 myapp_db
数据库。
2. 连接应用服务到数据库
Docker Compose 的强大之处在于管理多个相互依赖的服务。假设你有一个 Web 应用服务(例如,使用 Node.js, Python, Java 等),它需要连接到 PostgreSQL 数据库。
在同一个 docker-compose.yml
文件中,你可以定义你的应用服务,并将其连接到 db
服务所在的网络。默认情况下,Docker Compose 会为你的所有服务创建一个桥接网络,服务可以通过其服务名称互相访问。
“`yaml
version: ‘3.8’
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: your_strong_password
POSTGRES_USER: myuser
POSTGRES_DB: myapp_db
volumes:
– db_data:/var/lib/postgresql/data
# 注意:如果你的应用服务和数据库在同一个 Docker Compose 网络中,通常无需将数据库端口映射到主机上。
# 只有当你需要从主机外部(非容器)访问数据库时才需要 ports 映射。
# ports:
# – “5432:5432”
myapp: # 定义你的应用服务
build: ./app # 假设你的应用代码在 ‘./app’ 目录,并且有 Dockerfile
# 或 image: your-app-image # 如果你已经构建了应用镜像
ports:
– “8080:80” # 将应用端口映射到主机
environment: # 配置应用连接数据库所需的环境变量
DATABASE_URL: postgres://myuser:your_strong_password@db:5432/myapp_db # 注意这里的主机名是 ‘db’ (服务名)
# 或者分开设置:
# DB_HOST: db
# DB_PORT: 5432
# DB_USER: myuser
# DB_PASSWORD: your_strong_password
# DB_NAME: myapp_db
depends_on: # 定义服务之间的依赖关系
– db # myapp 依赖于 db 服务
volumes:
db_data:
“`
解释:
- 新增了一个
myapp
服务。 build: ./app
(或image:
) 指定如何构建或使用应用镜像。ports:
映射应用服务的端口到主机,以便从浏览器或客户端访问应用。environment:
配置应用连接数据库所需的参数。关键在于数据库主机名不再是localhost
,而是数据库服务在 Docker Compose 网络中的名称db
。Docker Compose 的内部 DNS 会将db
解析到数据库容器的 IP 地址。depends_on: - db
: 告诉 Docker Compose,myapp
服务依赖于db
服务。Docker Compose 会尝试在启动myapp
之前先启动db
。然而,depends_on
只保证启动顺序,不保证依赖服务已经“准备就绪”(例如,数据库已经完全启动并接受连接)。 这就需要健康检查。
3. 添加健康检查 (Healthcheck)
depends_on
并不能保证数据库已经完全启动并可以接受连接。应用服务可能会启动得更快,导致连接数据库失败。为了解决这个问题,我们可以为数据库服务添加健康检查。Docker Compose 会定期运行一个命令来检查服务的健康状态,并且 depends_on
支持等待依赖服务达到健康状态后再启动。
修改 db
服务的配置,添加 healthcheck
:
“`yaml
version: ‘3.8’
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: your_strong_password
POSTGRES_USER: myuser
POSTGRES_DB: myapp_db
volumes:
– db_data:/var/lib/postgresql/data
healthcheck: # 添加健康检查配置
test: [“CMD-SHELL”, “pg_isready -U ${POSTGRES_USER:-postgres}”] # 测试命令
interval: 5s # 检查间隔
timeout: 5s # 超时时间
retries: 5 # 重试次数
start_period: 10s # 启动期间的等待时间,在此期间失败不计入 retries
# ports:
# – “5432:5432”
myapp:
build: ./app
ports:
– “8080:80”
environment:
DATABASE_URL: postgres://myuser:your_strong_password@db:5432/myapp_db
depends_on:
db: # 修改 depends_on 语法,指定依赖服务的健康状态
condition: service_healthy # myapp 依赖于 db 服务达到健康状态
volumes:
db_data:
“`
解释:
- 在
db
服务下增加了healthcheck:
配置。test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
: 这是执行健康检查的命令。pg_isready
是 PostgreSQL 提供的一个小工具,用于检查服务器是否接受连接。-U ${POSTGRES_USER:-postgres}
表示使用POSTGRES_USER
环境变量指定的用户(如果未设置,则使用默认的postgres
用户)进行检查。CMD-SHELL
表示在容器的 shell 中执行此命令。interval: 5s
: 每隔 5 秒检查一次。timeout: 5s
: 如果命令在 5 秒内没有完成,则视为失败。retries: 5
: 如果健康检查连续失败 5 次,则容器被标记为 unhealthy。start_period: 10s
: 在容器启动后的前 10 秒内,健康检查失败不会导致容器被标记为 unhealthy,这给容器启动和初始化数据库足够的时间。10 秒后,如果健康检查失败,才开始计算重试次数。
- 在
myapp
服务的depends_on
中,将- db
改为更详细的db: condition: service_healthy
。这明确告诉 Docker Compose,只有当db
服务通过健康检查被标记为healthy
时,才启动myapp
服务。
现在,当你运行 docker compose up -d
时,Docker Compose 会等待 db
服务显示为 healthy
(可以通过 docker compose ps
或 docker compose logs db
查看状态) 后,才会启动 myapp
服务,从而避免了连接时数据库尚未就绪的问题。
4. 使用 .env
文件管理敏感信息
直接在 docker-compose.yml
中硬编码密码是非常不安全的做法,尤其是在将文件提交到版本控制系统时。更好的方法是使用环境变量,并将这些变量存储在 .env
文件中,然后将 .env
文件排除在版本控制之外(例如,添加到 .gitignore
)。
创建一个名为 .env
的文件在与 docker-compose.yml
相同的目录下:
“`ini
.env 文件
POSTGRES_PASSWORD=your_very_secure_password_from_env
POSTGRES_USER=myuser_from_env
POSTGRES_DB=myapp_db_from_env
“`
然后修改 docker-compose.yml
,使用 ${VAR_NAME}
语法引用 .env
文件中的变量:
“`yaml
version: ‘3.8’
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # 从 .env 文件中读取密码
POSTGRES_USER: ${POSTGRES_USER:-postgres} # 从 .env 中读取用户,如果未设置则使用 postgres
POSTGRES_DB: ${POSTGRES_DB:-postgres} # 从 .env 中读取数据库名,如果未设置则使用 postgres
volumes:
– db_data:/var/lib/postgresql/data
healthcheck:
test: [“CMD-SHELL”, “pg_isready -U ${POSTGRES_USER:-postgres}”]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
# myapp 服务也同样从 .env 读取配置
# myapp:
# …
# environment:
# DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
# depends_on:
# db:
# condition: service_healthy
volumes:
db_data:
“`
现在,当你运行 docker compose up
时,Docker Compose 会自动读取 .env
文件中的变量,并将它们注入到容器的环境中。.env
文件可以添加到 .gitignore
中,保护你的敏感信息。
注意: 对于生产环境,更安全的敏感信息管理方式是使用 Docker Secrets 或外部密钥管理系统。.env
文件适用于开发和测试环境。
总结
通过本文的学习,你已经掌握了使用 Docker Compose 快速、便捷地运行 PostgreSQL 数据库的方法。我们从最基本的配置开始,逐步深入,了解了数据持久化(卷)、服务间通信、健康检查以及敏感信息管理(.env 文件)等重要概念和实践。
使用 Docker Compose 运行 PostgreSQL 不仅简化了环境搭建过程,更重要的是提供了一致性、隔离性、可移植性的优势,无论你是在开发、测试还是部署应用,都能享受到容器化带来的便利。
现在,你可以尝试:
- 在你的开发项目中使用 Docker Compose 启动 PostgreSQL。
- 将你的应用服务添加到
docker-compose.yml
文件中,并配置它连接到数据库。 - 探索 PostgreSQL 官方镜像的其他配置选项(可以查看 Docker Hub 上的 PostgreSQL 页面 获取详细信息)。
- 学习如何进行数据库备份和恢复(通常可以通过
docker compose exec
运行pg_dump
和pg_restore
命令实现)。
Docker Compose 是构建和管理多容器应用的强大工具。熟练掌握它,将极大地提升你的开发效率和部署可靠性。开始你的容器化数据库之旅吧!
希望这篇详细的文章能够帮助你快速上手使用 Docker Compose 运行 PostgreSQL。祝你编码愉快!