说明
此方法适合以下环境,内网隔离,联网时需要安全防护,适合无数据库管理工具,希望通过非命令行环境管理pg数据库,pg数据库不可对外暴露端口。
PostgreSQL 18.3 + pgAdmin 部署说明
说明
本目录提供一个单一 Docker Compose 编排,用于部署 PostgreSQL 18.3 与 pgAdmin 4 9.17。
架构与安全边界如下:
- PostgreSQL 服务名为
postgresql,只暴露到内部 Docker 网络pg-net,不会向物理机发布5432端口。 - pgAdmin 服务名为
pgadmin,通过物理机80端口提供 Web 管理界面。 - PostgreSQL 与 pgAdmin 共同位于内部 Docker 网络
pg-net,并通过postgresql:5432连接;pgAdmin 另外连接pgadmin-net,用于接收物理机浏览器访问。 - PostgreSQL 业务账户为
hive,默认数据库为metastore_db。 - pgAdmin 管理员账户为
admin@example.com。 - PostgreSQL 与 pgAdmin 使用独立的 32 位随机密码,密码仅保存于
.env与pgpass,两者都被.gitignore排除。 servers.json与pgpass会预置 pgAdmin 的数据库连接。启动完成后,只需登录 pgAdmin,即可直接管理 PostgreSQL,无需在页面中新增服务器或填写数据库密码。
组件版本:
| 组件 | 镜像 | 用途 |
|---|---|---|
| PostgreSQL | postgres:18.3 |
关系型数据库 |
| pgAdmin | dpage/pgadmin4:9.17 |
PostgreSQL Web 管理界面 |
网络拓扑:
text
浏览器
|
| http://<服务器IP>:80
v
pgAdmin (80/tcp,物理机端口发布)
|
| postgresql:5432,仅 Docker 内部网络
v
PostgreSQL (5432/tcp,不发布到物理机)
准备环境
1. 前置条件
- Docker Engine 24+。
- Docker Compose Plugin v2+。
- 物理机
80/tcp未被 Nginx、Apache、IIS 或其他容器占用。 - 建议主机至少提供 4 CPU、8 GB 内存和足够的数据盘空间。Compose 对 PostgreSQL 设置 2 CPU / 4 GB 上限,对 pgAdmin 设置 1 CPU / 1 GB 上限。
在本目录执行以下命令检查环境:
bash
docker --version
docker compose version
docker ps --format "table {{.Names}}\t{{.Ports}}"
如果 80 端口被占用,先停止冲突服务,或将 compose.yaml 中 pgAdmin 的 "80:80" 修改为未占用端口,例如 "5050:80"。修改后访问地址也要改为 http://<服务器IP>:5050。
2. 保护密码文件
.env 与 pgpass 包含真实密码,不应提交到 Git、共享到聊天记录或复制到不受控位置。
Linux 服务器建议限制文件权限:
bash
chmod 600 .env pgpass
Windows 上应限制目录访问权限,仅允许部署账户读取本目录中的 .env 与 pgpass。
3. 首次部署前确认
postgresql-data 和 pgadmin-data 是命名卷。若本机此前已使用同名卷运行过其他 PostgreSQL 或 pgAdmin 环境,先检查卷是否存在:
bash
docker volume ls --filter name=postgresql-data
docker volume ls --filter name=pgadmin-data
不要在不确认数据归属的情况下删除已有卷。
配置文件
文件清单
| 文件 | 作用 | 是否包含密码 |
|---|---|---|
compose.yaml |
PostgreSQL 与 pgAdmin 的统一编排 | 否 |
.env |
镜像环境变量、账户名、密码和数据库参数 | 是 |
servers.json |
pgAdmin 自动注册的 PostgreSQL 连接 | 否 |
pgpass |
pgAdmin 自动连接 PostgreSQL 所需密码 | 是 |
.gitignore |
防止 .env 与 pgpass 进入 Git |
否 |
v2.yaml |
原始 PostgreSQL 单服务配置,仅作历史参考 | 是,含旧弱密码 |
compose.yaml
bash
name: pg-stack
services:
postgresql:
image: postgres:18.3
container_name: postgresql
restart: unless-stopped
environment:
TZ: ${TZ}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_INITDB_ARGS: ${POSTGRES_INITDB_ARGS}
volumes:
- postgresql-data:/var/lib/postgresql
networks:
- pg-net
expose:
- "5432"
command:
- postgres
- -c
- max_connections=${POSTGRES_MAX_CONNECTIONS}
- -c
- shared_buffers=${POSTGRES_SHARED_BUFFERS}
- -c
- work_mem=${POSTGRES_WORK_MEM}
- -c
- maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM}
- -c
- effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE}
- -c
- checkpoint_completion_target=${POSTGRES_CHECKPOINT_COMPLETION_TARGET}
- -c
- wal_buffers=${POSTGRES_WAL_BUFFERS}
- -c
- default_statistics_target=${POSTGRES_DEFAULT_STATISTICS_TARGET}
- -c
- log_min_messages=${POSTGRES_LOG_MIN_MESSAGES}
- -c
- log_rotation_size=${POSTGRES_LOG_ROTATION_SIZE}
- -c
- password_encryption=scram-sha-256
- -c
- listen_addresses=${POSTGRES_LISTEN_ADDRESSES}
deploy:
resources:
limits:
cpus: "2"
memory: 4g
healthcheck:
test:
- CMD-SHELL
- pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"
interval: 10s
timeout: 5s
retries: 6
start_period: 30s
pgadmin:
image: dpage/pgadmin4:9.17
container_name: pgadmin
restart: unless-stopped
depends_on:
postgresql:
condition: service_healthy
environment:
TZ: ${TZ}
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
PGADMIN_SERVER_JSON_FILE: /pgadmin4/servers.json
PGPASS_FILE: /pgadmin4/pgpass
PGADMIN_REPLACE_SERVERS_ON_STARTUP: "True"
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: "True"
ports:
- "80:80"
volumes:
- pgadmin-data:/var/lib/pgadmin
- ./servers.json:/pgadmin4/servers.json:ro
- ./pgpass:/pgadmin4/pgpass:ro
networks:
- pg-net
- pgadmin-net
deploy:
resources:
limits:
cpus: "1"
memory: 1g
healthcheck:
test:
- CMD-SHELL
- wget --no-verbose --tries=1 --spider http://127.0.0.1:80/login || exit 1
interval: 15s
timeout: 5s
retries: 6
start_period: 45s
volumes:
postgresql-data:
name: postgresql-data
pgadmin-data:
name: pgadmin-data
networks:
pg-net:
name: pg-net
driver: bridge
internal: true
pgadmin-net:
name: pgadmin-net
driver: bridge
compose.yaml 是唯一的运行编排文件。
PostgreSQL 配置要点:
- 未配置
ports,使用expose: "5432"仅声明容器网络内端口。 pg-net设置为internal: true,仅承载 PostgreSQL 与 pgAdmin 的数据库连接;pgAdmin 额外接入pgadmin-net并通过自身发布的80端口接收浏览器请求。- 数据目录使用 PostgreSQL 18 的父目录挂载路径
/var/lib/postgresql,由镜像根据主版本自动选择内部数据目录,避免将 PostgreSQL 18 数据写入不兼容的历史路径。 max_connections=200,适配 4 GB 容器内存的保守基线。高并发短连接场景应使用应用侧连接池,例如 PgBouncer,而不是直接提高连接数。password_encryption=scram-sha-256使后续新建或修改的角色密码使用 SCRAM-SHA-256 哈希保存。默认镜像初始化的本地认证不需要额外暴露密码认证规则;应用容器通过受控内部网络访问。shared_buffers=1GB、effective_cache_size=3GB、work_mem=8MB、maintenance_work_mem=256MB是 4 GB 上限下的通用基线;大型排序、哈希连接和维护任务会叠加使用内存,应结合实际负载调优。- 健康检查使用
pg_isready,只有数据库就绪后 pgAdmin 才会启动。
pgAdmin 配置要点:
ports: "80:80"将 Web 控制台发布到物理机80端口。- 使用命名卷
pgadmin-data持久化登录会话、用户配置和 pgAdmin 内置配置库。 PGADMIN_REPLACE_SERVERS_ON_STARTUP=True使servers.json在每次启动时按文件内容同步,便于声明式维护预置连接。PGPASS_FILE=/pgadmin4/pgpass将挂载的 pgpass 文件用于数据库认证。该文件格式为主机:端口:数据库:用户名:密码。- 设置
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION=True,加强会话 Cookie 防护。
.env
bash
TZ=Asia/Shanghai
POSTGRES_USER=hive
POSTGRES_PASSWORD=82iCQba@zkgyHA4K1_7MGDkg+qw+sTco
POSTGRES_DB=metastore_db
POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
POSTGRES_MAX_CONNECTIONS=200
POSTGRES_SHARED_BUFFERS=1GB
POSTGRES_WORK_MEM=8MB
POSTGRES_MAINTENANCE_WORK_MEM=256MB
POSTGRES_EFFECTIVE_CACHE_SIZE=3GB
POSTGRES_CHECKPOINT_COMPLETION_TARGET=0.9
POSTGRES_WAL_BUFFERS=16MB
POSTGRES_DEFAULT_STATISTICS_TARGET=100
POSTGRES_LOG_MIN_MESSAGES=warning
POSTGRES_LOG_ROTATION_SIZE=100MB
POSTGRES_LISTEN_ADDRESSES=*
PGADMIN_DEFAULT_EMAIL=admin@example.com
PGADMIN_DEFAULT_PASSWORD=LIPli9McIaYgHEQvDAeiLk+1J=JtepID
.env 保存运行参数和密码。当前已配置:
dotenv
POSTGRES_USER=hive
POSTGRES_DB=metastore_db
PGADMIN_DEFAULT_EMAIL=admin@example.com
POSTGRES_PASSWORD 是 PostgreSQL 中 hive 用户的密码;PGADMIN_DEFAULT_PASSWORD 是 pgAdmin Web 登录密码。这两个密码相互独立。PGADMIN_DEFAULT_EMAIL 必须使用有效、非保留域名格式的邮箱地址;example.local 会被 pgAdmin 9.17 拒绝,不能使用。
更换密码时必须同步修改:
.env中的POSTGRES_PASSWORD。pgpass中对应 PostgreSQL 连接的最后一列。- 已初始化数据库内
hive用户的实际密码。
首次初始化后,仅修改 .env 不会自动修改 PostgreSQL 已存在用户密码。请通过 pgAdmin 查询工具或容器命令执行:
bash
docker compose exec -T postgresql psql -U hive -d metastore_db -c "ALTER ROLE hive WITH PASSWORD '替换为新密码';"
随后更新 pgpass,并重启 pgAdmin:
bash
docker compose restart pgadmin
servers.json 与 pgpass
servers.json
bash
{
"Servers": {
"1": {
"Name": "PostgreSQL 18.3",
"Group": "Local Docker",
"Host": "postgresql",
"Port": 5432,
"MaintenanceDB": "metastore_db",
"Username": "hive",
"SSLMode": "prefer",
"PassFile": "/pgadmin4/pgpass"
}
}
}
pgpass
bash
postgresql:5432:metastore_db:hive:82iCQba@zkgyHA4K1_7MGDkg+qw+sTco
servers.json 预置以下连接:
| 项目 | 值 |
|---|---|
| 显示名称 | PostgreSQL 18.3 |
| 分组 | Local Docker |
| 主机 | postgresql |
| 端口 | 5432 |
| 数据库 | metastore_db |
| 用户 | hive |
| SSL 模式 | prefer |
这里的 postgresql 是 Compose 服务名,不是物理机 IP,也不是 localhost。容器内使用 localhost 会指向 pgAdmin 容器自身,无法连接数据库。
pgAdmin 的服务器清单在首次初始化时导入;本编排额外启用了 PGADMIN_REPLACE_SERVERS_ON_STARTUP=True,因此修改 servers.json 后重启 pgAdmin 即可同步服务器定义:
bash
docker compose restart pgadmin
pgpass 是密码文件,必须与 .env 的 POSTGRES_PASSWORD 保持一致。不要将其提交到仓库。
使用外部应用连接数据库
PostgreSQL 没有物理机端口映射,因此宿主机上的客户端和其他物理机不能直接连接 5432。需要连接数据库的其他容器应加入现有外部网络 pg-net,并配置以下连接信息:
text
host=postgresql
port=5432
database=metastore_db
username=hive
外部 Compose 项目加入网络的示例:
yaml
networks:
pg-net:
external: true
name: pg-net
services:
application:
networks:
- pg-net
只有受控容器应加入该网络。避免为了临时排障将 PostgreSQL 映射为 5432:5432。
部署
1. 校验配置
在 pg 目录执行:
bash
docker compose --env-file .env config
命令应成功输出渲染后的 Compose 配置。确认 postgresql 服务没有 ports 字段,pgadmin 服务存在 80:80 映射。
2. 启动服务
bash
docker compose up -d
查看服务状态:
bash
docker compose ps
docker compose logs --tail=100 postgresql
docker compose logs --tail=100 pgadmin
预期结果:
postgresql状态为healthy。pgadmin状态为healthy或running。docker compose ps中 PostgreSQL 不应显示0.0.0.0:5432->5432/tcp。docker compose ps中 pgAdmin 应显示0.0.0.0:80->80/tcp。
3. 登录 pgAdmin
浏览器访问:
text
http://<服务器IP>:80
使用 .env 中的以下凭据登录:
text
邮箱:PGADMIN_DEFAULT_EMAIL
密码:PGADMIN_DEFAULT_PASSWORD
登录后在左侧依次展开:
text
Servers > Local Docker > PostgreSQL 18.3
连接由 servers.json 和 pgpass 自动配置,应可直接展开 Databases > metastore_db。
pgAdmin 管理员邮箱校验失败
如果 docker logs pgadmin 出现以下错误:
text
'<邮箱地址>' does not appear to be a valid email address
The part after the @-sign is a special-use or reserved name
原因是 PGADMIN_DEFAULT_EMAIL 使用了 .local 等保留域名。pgAdmin 9.17 会校验管理员邮箱,即使不配置邮件发送功能也会拒绝此类域名。
修复步骤:
bash
sed -i 's/^PGADMIN_DEFAULT_EMAIL=.*/PGADMIN_DEFAULT_EMAIL=admin@example.com/' .env
docker compose up -d --force-recreate pgadmin
docker compose logs --tail=100 pgadmin
PGADMIN_DEFAULT_EMAIL 仅用于 pgAdmin Web 登录标识,不要求邮箱实际可收信;但域名必须不属于 .local、.localhost、.test、.invalid 等保留域。
访问验证
1. 验证数据库不向物理机发布
bash
docker compose ps
docker port postgresql
正确结果是 PostgreSQL 没有宿主机端口输出。docker compose ps 中仅 pgAdmin 显示物理机端口 80。
2. 验证数据库健康状态
bash
docker compose exec postgresql pg_isready -U hive -d metastore_db
docker compose exec postgresql psql -U hive -d metastore_db -c "SELECT current_user, current_database(), version();"
第一条命令应返回 accepting connections;第二条命令应显示当前用户为 hive、当前数据库为 metastore_db,并输出 PostgreSQL 版本。
3. 验证 pgAdmin 自动连接
登录 pgAdmin 后,在 PostgreSQL 18.3 上右键选择 Query Tool,执行:
sql
SELECT current_user, current_database(), inet_server_addr(), inet_server_port();
预期:
current_user为hive。current_database为metastore_db。inet_server_port为5432。- 服务器地址是 Docker 内部 IP,不是物理机 IP。
日常运维
停止和启动
bash
docker compose stop
docker compose start
停止不会删除命名卷和数据。
重启服务
bash
docker compose restart postgresql
docker compose restart pgadmin
修改 servers.json 或 pgpass 后,重启 pgAdmin;修改 PostgreSQL 运行参数后,重启 PostgreSQL。
查看日志
bash
docker compose logs -f postgresql
docker compose logs -f pgadmin
备份数据库
逻辑备份应输出到受控的宿主机备份目录,而不是容器临时目录:
bash
mkdir -p backups
docker compose exec -T postgresql pg_dump -U hive -d metastore_db -Fc > backups/metastore_db-$(date +%F).dump
验证备份文件:
bash
docker compose exec -T postgresql pg_restore -l < backups/metastore_db-$(date +%F).dump
生产环境应将备份目录同步到独立存储,并定期执行还原演练。
恢复数据库
恢复会覆盖目标数据库中的对象。执行前确认备份来源和目标环境:
bash
docker compose exec -T postgresql dropdb -U hive metastore_db
docker compose exec -T postgresql createdb -U hive metastore_db
docker compose exec -T postgresql pg_restore -U hive -d metastore_db --clean --if-exists < backups/metastore_db-YYYY-MM-DD.dump
安全注意事项
- 当前 pgAdmin 使用 HTTP 发布在
80端口。若需从不可信网络访问,必须在前置反向代理上配置 HTTPS、访问控制和可信证书,或限制物理机防火墙仅允许管理网段访问80/tcp。 - PostgreSQL 仅对 Docker 内部网络开放,不应额外添加
ports: "5432:5432"。 .env、pgpass和备份文件均可能包含敏感信息,应执行最小权限控制、定期轮换密码和异机备份。- 自动保存数据库密码是为了满足"启动后 pgAdmin 可直接使用"的要求。仅限受控管理环境;若需要更高安全等级,删除
pgpass挂载并改为首次连接手工输入密码。 POSTGRES_USER、POSTGRES_DB和POSTGRES_INITDB_ARGS只在postgresql-data为空时生效。不要在已有数据卷上随意变更这些初始化参数。- 不要使用
docker compose down -v清理环境,除非已完成备份且明确要永久删除 PostgreSQL 与 pgAdmin 数据。
访问使用
账户密码来自文件.env
