深色模式
Docker Compose 安装 PostgreSQL
概述
本地开发装 PostgreSQL,主线建议直接用 docker compose。数据库不是一次性命令,后面还会碰到数据卷、初始化脚本、自定义配置、认证规则、连接地址这些事。docker run 适合临时试一下,真要长期维护,还是把配置写进文件里更稳。
这一篇只讲一套能长期用的做法:compose.yaml 负责容器和挂载,postgresql.conf 控制实例行为,pg_hba.conf 决定谁能连,initdb 目录放第一次初始化要执行的脚本。这样整理完,后面排错和迁移都会轻松很多。
目录结构
项目里建议至少有这几个文件和目录:
text
.
├── compose.yaml
├── .env
├── conf
│ ├── postgresql.conf
│ └── pg_hba.conf
└── initdb
└── 001-init.sql它们分别负责:
compose.yaml:定义容器、端口、挂载和健康检查.env:放镜像版本、数据库名、密码这类环境变量conf/postgresql.conf:服务端配置conf/pg_hba.conf:客户端认证规则initdb/*.sql:数据目录第一次初始化时执行的脚本
环境与编排
先准备一个 .env:
ini
POSTGRES_IMAGE=postgres:17
POSTGRES_DB=app
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432再写 compose.yaml:
yaml
services:
postgres:
image: ${POSTGRES_IMAGE}
container_name: blog-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "${POSTGRES_PORT}:5432"
command:
- postgres
- -c
- config_file=/etc/postgresql/postgresql.conf
- -c
- hba_file=/etc/postgresql/pg_hba.conf
volumes:
- pgdata:/var/lib/postgresql/data
- ./conf/postgresql.conf:/etc/postgresql/postgresql.conf:ro
- ./conf/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
- ./initdb:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:这份配置里最关键的是 4 个挂载:
pgdata:/var/lib/postgresql/data:持久化数据库文件./conf/postgresql.conf:/etc/postgresql/postgresql.conf:ro:挂载服务端配置文件./conf/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro:挂载认证规则文件./initdb:/docker-entrypoint-initdb.d:ro:挂载初始化脚本目录
这里不用 latest,而是把镜像版本钉在 .env 里。数据库升级不是小事,别让镜像标签替你偷偷做决定。
另外,POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD 这 3 个环境变量,主要作用在第一次初始化数据目录时:
POSTGRES_USER:初始化超级用户POSTGRES_PASSWORD:这个用户的密码POSTGRES_DB:第一次启动时自动创建的数据库
后面数据卷已经存在,再改这些值,数据库不会跟着重建。
实例配置
conf/postgresql.conf 可以先从最常用的配置开始:
ini
listen_addresses = '*'
port = 5432
password_encryption = scram-sha-256
timezone = 'UTC'
log_timezone = 'UTC'
log_min_duration_statement = 500这几项里最容易漏的是 listen_addresses。如果自定义了 postgresql.conf,又没把它改成 '*' 或合适的地址范围,容器虽然启动了,外部连接还是进不来。
log_min_duration_statement 不是必配,但本地排查慢 SQL 时很顺手。先留一个温和的阈值,比出了问题再翻日志目录高效。
认证配置
pg_hba.conf 比 postgresql.conf 更像门禁规则。一个适合本地开发的示例可以这样写:
ini
# 容器内本地连接
local all all trust
# 宿主机回环地址
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# Docker bridge / compose 网络
host all all 172.16.0.0/12 scram-sha-256这几行的意思分别是:
local:走 Unix socket 的本地连接,常见于docker compose exec postgres psql127.0.0.1/32、::1/128:宿主机本地连接172.16.0.0/12:给 Docker bridge 和常见compose网络一个可用范围
这里的 local ... trust 只是为了让容器内排障更省事,适合本地开发。团队如果希望开发环境也统一走密码认证,可以把它改成 scram-sha-256。
有两个边界要先记住:
pg_hba.conf是从上往下匹配,命中第一条就停止继续看- Docker 端口映射进来的连接,在容器里看到的来源地址不一定是
127.0.0.1
所以在 Docker 场景下,只写一条 127.0.0.1/32 往往不够。更稳的做法是先用 172.16.0.0/12 覆盖本地开发网络,等知道实际网段后再收紧。线上不要照抄这个范围,应该按实际子网单独配置。
初始化脚本
初始化脚本就放在 initdb 目录里,例如 initdb/001-init.sql:
sql
CREATE ROLE app_rw LOGIN PASSWORD 'app_rw_pass';
CREATE ROLE app_ro LOGIN PASSWORD 'app_ro_pass';
GRANT CONNECT ON DATABASE app TO app_rw, app_ro;
\connect app
CREATE EXTENSION IF NOT EXISTS pgcrypto;这个目录适合放:
- 创建业务角色
- 创建扩展
- 初始化基础 schema
- 导入一小段固定的种子数据
它有一个很重要的限制:只在数据目录为空时执行一次。
也就是说,下面这些操作单靠重启容器都不会重新生效:
- 改了
initdb/001-init.sql - 改了
POSTGRES_DB - 改了
POSTGRES_USER - 改了
POSTGRES_PASSWORD
因为数据库早就初始化完了。后面再改这些值,PostgreSQL 不会替你重建一遍。
重置初始化
如果只是改了 initdb 脚本,或者想让 POSTGRES_DB、POSTGRES_USER 这类初始化参数重新生效,光重启容器不够,得把旧数据卷一起清掉。
本地确认数据可以丢之后,再执行:
sh
docker compose down -v
docker compose up -d这一步会删除当前 compose 项目关联的数据卷。开发环境里很常见,线上环境当然不能这么处理。
启动检查
启动:
sh
docker compose up -d看状态和日志:
sh
docker compose ps
docker compose logs -f postgres检查数据库是否 ready:
sh
docker compose exec postgres pg_isready -U postgres -d app再确认实例实际加载的配置文件:
sh
docker compose exec postgres psql -U postgres -d app -c "SHOW config_file;"
docker compose exec postgres psql -U postgres -d app -c "SHOW hba_file;"这一步很值得做。很多“明明改了配置却没生效”的问题,最后都能追到加载的根本不是以为的那个文件。
连接方式
宿主机命令行连接
宿主机装了 psql 客户端后,可以直接连发布出来的端口:
sh
psql "postgresql://app_rw:app_rw_pass@127.0.0.1:5432/app?sslmode=disable"容器内连接
如果宿主机没装 psql,也可以直接进数据库容器:
sh
docker compose exec postgres psql -U postgres -d app同一个 compose 网络里的应用连接
如果应用服务也在同一个 compose.yaml 里,主机名要写服务名 postgres,不要写 localhost:
text
postgresql://app_rw:app_rw_pass@postgres:5432/app?sslmode=disable在容器里,localhost 指的是当前容器自己,不是数据库容器。这是 Docker 新手最常见的坑之一。
GUI 客户端连接
用 DBeaver、TablePlus、DataGrip 这类图形客户端时,填这 5 个字段就够了:
- Host:
127.0.0.1 - Port:
5432 - Database:
app - User:
app_rw - Password:
app_rw_pass
如果应用程序不是直接收连接串,而是分开配置字段,通常也就是这几项:
- Host
- Port
- Database
- User
- Password
- SSL Mode
配置生效
修改 pg_hba.conf、部分日志配置后,通常可以先重载:
sh
docker compose exec postgres psql -U postgres -d app -c "SELECT pg_reload_conf();"下面这些改动,通常要重启容器才算真正生效:
listen_addressesport- 需要重新初始化数据目录的环境变量
initdb目录里的首次初始化脚本
开发环境下最容易混淆的是“reload 了配置”和“重新初始化数据库”不是一回事。前者只是让实例重新读配置,后者涉及整套数据目录和初始化流程。
常见问题
改了初始化脚本,但是容器里什么都没变
先别怀疑 SQL 写错,先看数据卷是不是还在。/docker-entrypoint-initdb.d 只在第一次初始化时执行,已有数据卷时不会重复跑。
能看到容器在运行,但就是连不上
优先按这个顺序排查:
docker compose logs -f postgrespg_isreadySHOW config_file;、SHOW hba_file;- 检查
listen_addresses - 检查
pg_hba.conf里的地址范围和认证方式
应用连数据库时把主机写成了 localhost
如果应用也在容器里,这样写几乎一定会错。容器访问同网络里的 PostgreSQL,主机名应该是服务名 postgres。
