症状:你在客户端看到了什么?
psql: error: connection to server at "192.168.1.100", port 5432 failed: Connection refused
或者更直接的:
Is the server running on that host and accepting TCP/IP connections?
这玩意我见得太多了。上个月我们一个内部工具突然炸了,监控直接飘红,一看全是 connection refused。第一反应是服务挂了,但 systemctl status postgresql 显示它还在跑——这就有意思了。
根因分析:到底谁拒绝了谁?
“Connection refused” 不是 PostgreSQL 在耍脾气。这是 TCP 协议层面的拒绝——客户端发了个 SYN 包,但是目标机器上压根没有进程在监听那个端口。或者,有进程在监听,但被防火墙拦了,导致 RST 包被发回来。
说白了,就三种情况:
- PostgreSQL 没在跑(或者跑了但没监听你指定的端口/IP)
- PostgreSQL 只监听 localhost,但你在远程连接
- 防火墙在中间截胡了
别急着改 pg_hba.conf——很多人第一步就翻车在这里。pg_hba.conf 管的是"认证",但如果连 TCP 握手都没完成,认证配置压根不会被读取。
修复步骤:按顺序来,别跳
第一步:确认 PostgreSQL 进程真的在跑
# 看进程
ps aux | grep postgres
# 或者用 systemd
systemctl status postgresql
# 更直接——看端口
ss -tlnp | grep 5432
如果 ss 输出里没有 postgres 进程在 5432 上,那就是服务没起来。这时候去看日志:
journalctl -u postgresql -n 50 --no-pager
常见翻车原因:磁盘满了、共享内存不足、WAL 目录权限不对。
第二步:检查 PostgreSQL 监听了哪些地址
这是最大的坑。默认配置下,PostgreSQL 只监听 localhost。你要是想远程连,必须改 postgresql.conf。
# 找到配置文件位置
psql -U postgres -c "SHOW config_file;"
# 或者直接找
find /etc/postgresql -name "postgresql.conf"
找到后,改这个参数:
# 监听所有网卡
listen_addresses = '*'
# 或者只监听特定 IP
listen_addresses = 'localhost,192.168.1.100'
改完重启:
systemctl restart postgresql
再跑 ss -tlnp | grep 5432,你应该能看到类似:
LISTEN 0 244 0.0.0.0:5432 0.0.0.0:*
第三步:配置 pg_hba.conf——但别搞反了顺序
很多人一上来就把 pg_hba.conf 改成 trust,这是安全灾难。正确的做法是按需开放。
# 允许特定网段连接
host all all 192.168.1.0/24 md5
# 或者允许所有(不推荐)
host all all 0.0.0.0/0 md5
重要规则:PostgreSQL 读取 pg_hba.conf 是从上到下,匹配到第一条就停止。所以更具体的规则要放前面。
| 规则位置 | 连接类型 | 数据库 | 用户 | 来源地址 | 认证方式 | 匹配优先级 |
|---|---|---|---|---|---|---|
| 第1条 | host | all | all | 127.0.0.1/32 | scram-sha-256 | 最高 |
| 第2条 | host | all | all | 192.168.1.0/24 | md5 | 中等 |
| 第3条 | host | all | all | 0.0.0.0/0 | reject | 最低 |
改完后不用重启,执行:
pg_ctl reload
# 或者
SELECT pg_reload_conf();
第四步:防火墙——这玩意坑了我三次
你以为配置都对了,但防火墙在背后默默丢包。检查:
# iptables
iptables -L -n | grep 5432
# firewalld
firewall-cmd --list-all
# ufw
ufw status
开放端口:
# firewalld
firewall-cmd --permanent --add-port=5432/tcp
firewall-cmd --reload
# ufw
ufw allow 5432/tcp
# iptables
iptables -A INPUT -p tcp --dport 5432 -j ACCEPT
第五步:终极测试——从客户端验证
# 测试 TCP 连接
telnet 192.168.1.100 5432
# 或者用 nc
nc -zv 192.168.1.100 5432
如果 telnet 能连上但 psql 报错,那才是 pg_hba.conf 或认证的问题。
常见翻车现场
场景一:Docker 容器
# 容器内部监听没问题,但宿主机连不上?
# 检查端口映射
docker ps | grep postgres
# 正确的启动方式
docker run -d \
-p 5432:5432 \
-e POSTGRES_PASSWORD=mysecret \
postgres:15
场景二:云服务商的安全组
AWS、阿里云、腾讯云都有两层防火墙——实例自身的防火墙 + 云平台的安全组。很多人只配了一边。
场景三:IPv6 问题
PostgreSQL 默认可能监听 IPv6 的 ::1,但客户端用 IPv4 的 127.0.0.1 去连。检查:
ss -tlnp | grep 5432
如果只看到 [::]:5432 而没有 0.0.0.0:5432,那就是 IPv6 only。在 postgresql.conf 里加:
listen_addresses = '0.0.0.0'
FAQ
Q: 为什么 systemctl status postgresql 显示 running,但连接还是被拒绝?
A: 最常见的原因是 PostgreSQL 只监听了 127.0.0.1,但你从远程连接。用 ss -tlnp | grep 5432 确认监听地址。另一个可能是 postgresql.conf 里的 port 不是 5432。
Q: 改了 pg_hba.conf 后需要重启 PostgreSQL 吗?
A: 不需要重启,执行 pg_ctl reload 或 SELECT pg_reload_conf() 即可。只有改 postgresql.conf 里的 listen_addresses 或 port 才需要重启。
Q: 防火墙已经放行了 5432 端口,但还是连不上,为什么?
A: 检查 SELinux 或 AppArmor。在 CentOS/RHEL 上,SELinux 会阻止 PostgreSQL 监听非标准端口或连接远程数据库。用 ausearch -m avc 查看 SELinux 审计日志。
Q: 如何区分是防火墙拒绝还是 PostgreSQL 拒绝?
A: 用 telnet 测试。如果 telnet 返回 “Connection refused”,是防火墙或服务没在跑。如果 telnet 能连上但 psql 报 “no pg_hba.conf entry”,那是认证配置问题。
Q: 在 Kubernetes 中部署 PostgreSQL 时经常遇到连接拒绝,有什么特别要注意的?
A: 检查 Service 的 targetPort 是否匹配容器的端口,以及 Pod 的 readinessProbe 配置。另外,StatefulSet 的 Pod 重启后 IP 会变,要用 Headless Service 加 DNS 解析。
最后说两句
说实话,PostgreSQL 的连接问题 80% 是配置疏忽,15% 是防火墙,只有 5% 是真正的 bug。按上面的顺序排查,基本十分钟内能搞定。别一上来就重装——我见过有人重装了三次才发现是 listen_addresses 没改。
如果你在生产环境遇到这个问题,建议先切到备份节点,再慢慢排查主节点。别让用户等你修数据库。