在 Linux 主机上运行 Docker 时,明明已经通过 -p 8080:80 发布端口,却仍然出现连接超时、Connection refused,甚至重启 Docker 后端口时好时坏。此类问题通常不只是“容器没启动”,还可能与宿主机端口占用、应用监听地址、IP 转发、iptables 链顺序以及 firewalld、ufw 等防火墙工具的规则冲突有关。🔍 本文提供一套由内到外的排查流程,帮助快速定位故障层级,避免一开始就清空防火墙规则。
一、先确认端口映射是否真正生效
首先执行 docker ps,查看 PORTS 一栏是否存在类似 0.0.0.0:8080->80/tcp 的记录。也可以运行 docker port 容器名,直接检查容器端口对应的宿主机地址。如果只看到 80/tcp,说明镜像虽然声明了端口,但创建容器时并没有通过 -p 或 Compose 的 ports 配置将其发布。
需要特别区分 EXPOSE 与端口发布:EXPOSE 主要用于描述镜像预期使用的端口,并不会自动允许外部访问;真正建立宿主机到容器转发关系的是 -p 主机端口:容器端口。Docker 会借助防火墙规则实现 NAT、端口转换和地址伪装,具体机制可参考 Docker 端口发布文档。
二、检查宿主机端口是否被占用
执行 ss -lntup | grep :8080,确认目标宿主机端口是否已被其他进程监听。如果创建容器时出现“port is already allocated”或“address already in use”,应停止冲突服务、修改映射端口,或删除占用该端口的旧容器。不要只检查正在运行的业务进程,还要留意反向代理、遗留容器和 systemd 服务。
端口绑定地址也会影响访问范围。例如 -p 127.0.0.1:8080:80 通常只允许从宿主机本地访问;若希望通过宿主机网卡地址接收连接,可按实际安全需求绑定指定网卡 IP,或使用默认的全接口发布方式。⚠️ 默认发布到所有接口可能扩大暴露面,数据库和管理端口不宜直接面向公网。
三、确认容器内应用正在正确监听
进入容器执行 docker exec -it 容器名 sh,再使用 ss -lntp 检查应用是否监听目标容器端口。若镜像缺少 ss,也可以从容器内部使用 curl 或 wget 请求本地服务。例如映射为 8080:80 时,应用必须实际监听容器内的 80 端口,而不是 8080。
另一个常见问题是应用只绑定在 127.0.0.1。容器端口转发进入的是容器网络接口,而不是应用进程的回环接口,因此服务通常应监听 0.0.0.0 或容器内对应网卡地址。若容器内部访问失败,应优先修复应用配置,而不是修改 iptables。
四、按照访问路径分层测试
- 在容器内访问应用地址,确认服务进程正常。
- 在宿主机执行 curl 来源链接,验证本机端口映射。
- 使用宿主机实际网卡 IP 测试,排除绑定地址问题。
- 从另一台机器访问,检查入站防火墙、安全组及上游网络设备。
如果容器内正常、宿主机本地失败,重点检查 Docker NAT 规则和端口占用;如果宿主机本地正常、远程失败,则更可能是 INPUT、FORWARD、DOCKER-USER 链、云安全组或路由策略拦截。分层测试能够显著缩小排查范围。🧭
五、检查 Docker 创建的 iptables 规则
可依次执行 iptables -t nat -L DOCKER -n -v、iptables -L FORWARD -n -v 和 iptables -L DOCKER-USER -n -v --line-numbers。正常情况下,Docker 会在 nat 表中创建 DOCKER 链并写入端口映射规则,同时在 FORWARD 路径中调用 Docker 自定义链。相关链及处理顺序可查看 Docker 与 iptables 官方说明。
排查时要观察规则是否存在、计数器是否增长,以及前方是否有 DROP 或 REJECT 规则。若请求到达主机后计数器完全不变,可能是流量未进入预期网卡或已被更早的规则拦截;如果 DOCKER-USER 链中的丢弃规则持续命中,则应核对其来源地址、目标端口和连接状态条件。
为什么自定义规则应优先放在 DOCKER-USER 链
Docker 官方建议使用 DOCKER-USER 链添加针对容器转发流量的用户策略,因为该链会在后续 Docker 转发链之前处理。直接把限制规则追加到 FORWARD 链末尾,可能因顺序靠后而无法产生预期效果;反之,在链首加入过宽的 DROP,也可能让所有已发布端口失效。
修改规则前应先备份:执行 iptables-save > /root/iptables-backup.txt。添加测试规则时尽量限定来源 IP、协议和端口,并使用 --line-numbers 确认插入位置。不要在远程 SSH 会话中直接执行 iptables -F,否则可能同时中断管理连接并破坏 Docker 网络。🛡️
六、排查 IP 转发与 FORWARD 默认策略
执行 sysctl net.ipv4.ip_forward,确认返回值为 1。Docker 的默认桥接网络依赖 IP 转发;如果安全加固脚本或系统升级将其关闭,容器转发流量可能失败。还应检查 iptables -S FORWARD,确认默认 DROP 策略之前是否存在允许已建立连接和 Docker 网络流量的跳转规则。
若必须启用转发,可通过 sysctl -w net.ipv4.ip_forward=1临时修改,并在系统的 sysctl 配置中设置持久参数。不要为了恢复容器访问就把 FORWARD 默认策略永久改成 ACCEPT,更稳妥的方式是保留最小授权规则,并明确允许所需网络和端口。
七、处理 firewalld、ufw 与规则后端冲突
firewalld、ufw、Docker 和运维脚本可能同时管理 netfilter。常见现象包括防火墙 reload 后 Docker 规则丢失、重启 Docker 后自定义规则顺序变化,或系统在 iptables-nft 与 iptables-legacy 之间使用了不同后端。可使用 iptables --version 查看当前后端,并检查相应的 nftables 规则集,避免只看一套规则就得出结论。
如果问题恰好发生在防火墙重载之后,可先备份规则,再重启 Docker,使其重新建立所需链,并立即复测。随后应把长期访问策略写入防火墙的持久配置,而不是依赖临时命令。通常不建议在 Docker 配置中关闭 iptables 管理,因为这可能破坏桥接网络的端口发布和地址转换。
八、重建网络并核对 Docker 配置
当 DOCKER 链缺失、规则明显残缺或网络状态异常时,可先保存现场信息,包括 docker inspect、docker network inspect、iptables-save 输出和 Docker 日志,再重启 Docker 服务。若仅某个自定义网络异常,可以在确认无业务依赖后重建该网络及相关容器。
推荐排查顺序:容器状态 → 端口发布 → 宿主机端口占用 → 容器内监听 → 本地与远程分层测试 → DOCKER-USER 和 FORWARD → nat 表规则 → IP 转发 → 防火墙管理工具。✅
总结
Docker 端口映射失败并不等于 iptables 一定损坏。多数问题可以归入四类:映射参数错误、应用监听异常、宿主机端口冲突,以及防火墙规则顺序或管理工具冲突。正确做法是沿真实数据路径逐层验证,并结合规则计数器定位流量在哪一步被阻断。修复时应先备份、再进行最小范围调整,优先使用 DOCKER-USER 链承载自定义过滤策略,避免清空整套规则或长期关闭 Docker 的防火墙管理功能。