运维笔记

PostgreSQL 连接被拒绝?从内核参数到防火墙,手把手教你排查修复

Developer Tools 技术可视化

症状:你在客户端看到了什么?

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 包被发回来。

说白了,就三种情况:

  1. PostgreSQL 没在跑(或者跑了但没监听你指定的端口/IP)
  2. PostgreSQL 只监听 localhost,但你在远程连接
  3. 防火墙在中间截胡了

别急着改 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条hostallall127.0.0.1/32scram-sha-256最高
第2条hostallall192.168.1.0/24md5中等
第3条hostallall0.0.0.0/0reject最低

改完后不用重启,执行:

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 reloadSELECT pg_reload_conf() 即可。只有改 postgresql.conf 里的 listen_addressesport 才需要重启。

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 没改。

如果你在生产环境遇到这个问题,建议先切到备份节点,再慢慢排查主节点。别让用户等你修数据库。