Docker 自定义网络中容器无法通过宿主机 IP 访问服务的完整排障记录

背景

在一次 Spring Boot 应用对接 Nacos 配置中心和 MySQL 数据库的过程中,遇到了一个典型的 Docker 网络问题:应用容器和 Nacos、MySQL 容器都运行在同一台宿主机的同一个自定义 Docker 网络中,但应用始终无法通过宿主机的物理 IP(10.2.4.4)连接 Nacos 和 MySQL。宿主机本身通过 curl 却能正常访问 Nacos 容器的映射端口(返回 404,证明网络通)。

现象总结

  • 宿主机 curl 10.2.4.4:8848 能连接 Nacos(返回 404,说明服务在)

  • 宿主机 curl 127.0.0.1:8848 也能连接

  • Docker 容器 ontostar 通过 10.2.4.4:8848 无法连接 Nacos

  • Docker 容器 ontostar 通过 10.2.4.4:3306 无法连接 MySQL

  • 三个容器(ontostarnacosmysql)都在同一个自定义网络 docker-services_default

根本原因分析

1. 容器访问宿主机物理 IP 的特殊性

当容器试图通过宿主机的物理 IP(如 10.2.4.4)访问另一个容器时,流量路径为:

text

复制代码
容器A → 宿主机eth0(物理网卡) → 宿主机iptables DNAT规则 → 目标容器

这条路径依赖 Linux 内核的 NAT 回流(Hairpin NAT) 功能,而 Docker 默认的桥接网络实现对此支持不完整,导致从容器发出的包经过宿主机后,无法正确返回到源容器。

2. 自定义网络的本应行为

在同一个用户自定义的桥接网络中,容器之间理应可以通过 IP 地址直接通信,而不需要经过宿主机物理网卡。但是在实际环境中,可能因为以下因素导致 IP 互通失败:

  • 宿主机 iptables FORWARD 链被阻断(例如默认策略为 DROP,或没有 Docker 插入的允许规则)

  • 容器内服务监听地址错误(例如监听 127.0.0.1 而非 0.0.0.0)

  • Docker 守护进程设置了 --icc=false(本例中未设置)

  • 防火墙规则清除了 Docker 自动创建的规则

3. 为什么通过宿主机 IP 访问不行,但直接用容器 IP 访问可以?

理论上,在同一个自定义网络中使用容器 IP 或容器名访问是最直接、最可靠的方式,因为流量完全在 Docker 虚拟网桥内部转发,不经过宿主机物理网卡和 NAT 规则。本例中由于未知原因(可能是 iptables 规则残留),导致容器间通过 IP 访问也失败,但宿主机却能访问映射端口 -- 这强烈暗示 FORWARD 链或 Docker 网络隔离策略存在异常。

排查步骤及命令

以下是按照执行顺序整理的排查操作:

1. 确认容器处于同一网络

bash

复制代码
docker inspect ontostar --format='{{.HostConfig.NetworkMode}}'
docker inspect nacos --format='{{.HostConfig.NetworkMode}}'
docker inspect mysql --format='{{.HostConfig.NetworkMode}}'

输出均为 docker-services_default,确认网络一致。

2. 验证宿主机端口映射的 DNAT 规则

bash

复制代码
sudo iptables -t nat -L -n | grep 8848

结果存在:

text

复制代码
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8848 to:172.19.0.3:8848

说明端口映射正确。

3. 在宿主机上测试连通性

bash

复制代码
curl -v 10.2.4.4:8848   # 成功返回404,服务可达
curl -v 127.0.0.1:8848     # 也成功

4. 检查 Docker 守护进程的 icc 设置

bash

复制代码
docker system info | grep -i icc

输出只有 cgroup v1 的警告,没有 --icc=false,说明容器间通信未被全局禁用。

5. 从应用容器内部测试网络(关键)

bash

复制代码
# 进入应用容器
docker exec -it ontostar /bin/sh

# 尝试 ping Nacos 容器的 IP(假设为 172.19.0.3)
ping 172.19.0.3

如果 ping 不通,说明容器间的 IP 层路由或 iptables FORWARD 链有问题。

若 ping 通但端口不通,则需要检查 Nacos 容器内服务监听地址。

6. 检查容器内服务的监听地址

bash

复制代码
# Nacos 容器
docker exec nacos netstat -tulnp | grep 8848
# MySQL 容器
docker exec mysql netstat -tulnp | grep 3306

确保监听地址为 0.0.0.0 而非 127.0.0.1

7. 检查宿主机 iptables FORWARD 链

bash

复制代码
sudo iptables -L FORWARD -n -v

如果看到 Chain FORWARD (policy DROP) 且没有任何 ACCEPT 规则,则所有跨容器流量都会被丢弃。Docker 通常会在 FORWARD 链中插入自己的规则允许容器间通信,但若规则被清除,就会出现问题。

8. 尝试重启 Docker 服务恢复默认规则

bash

复制代码
sudo systemctl restart docker
docker restart nacos mysql ontostar

9. 验证网络本身可用性(创建临时网络测试)

bash

复制代码
docker network create test-net
docker network connect test-net nacos
docker run -it --rm --network test-net alpine ping nacos

如果这个测试成功,说明 Docker 网络功能正常,问题可能局限在旧网络的 iptables 残留规则上。

最终解决方案

经过排查,根本原因是在同一个自定义网络中,容器之间本应 IP 互通,但由于某种原因(可能是之前手动修改过 iptables 或安全策略干扰)导致通过 IP 访问失败。而通过宿主机物理 IP 访问的方式又因为 Docker NAT 回流的限制不可靠。

因此,最佳解决方案是放弃使用宿主机 IP,转而使用 Docker 自定义网络内置的 DNS 解析功能,直接通过容器名(或服务名)进行通信

具体修改步骤

  1. 修改应用配置

    将原本配置中的 Nacos 地址从 10.2.4.4:8848 改为 nacos:8848

    将 MySQL 地址从 10.2.4.4:3306 改为 mysql:3306

  2. 确保 Nacos 注册的地址为宿主机 IP(可选,如需外部访问)

    在启动 Nacos 容器时添加环境变量:

    bash

    复制代码
    -e PREFER_HOST_MODE=hostname
    -e NACOS_SERVER_IP=10.2.4.4

    这样其他需要从宿主机外部访问的服务仍能通过 10.2.4.4 调用 Nacos。

  3. 授权 MySQL 允许来自任意主机的连接

    sql

    复制代码
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your_password' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
  4. 修改 MySQL 监听地址为 0.0.0.0

    在容器内的 MySQL 配置文件(如 /etc/mysql/my.cnf)中设置:

    ini

    复制代码
    bind-address = 0.0.0.0

    然后重启 MySQL 容器。

  5. 重启所有服务

    bash

    复制代码
    docker-compose down   # 如果使用 compose
    docker-compose up -d

验证

bash

复制代码
# 从应用容器内测试容器名连通性
docker exec ontostar curl -v nacos:8848/nacos/
docker exec ontostar mysql -h mysql -u root -p -e "SELECT 1"

经验与总结

  1. 永远不要依赖宿主机物理 IP 来让同一台机器上的 Docker 容器之间通信。这种"迂回"方式既低效又容易踩坑(NAT 回流问题)。

  2. 对于自定义桥接网络,直接使用容器名/服务名访问是最佳实践。Docker 内置的 DNS 解析稳定可靠。

  3. 如果出现容器间 IP 不通的情况,优先检查 iptables FORWARD 链和 Docker 守护进程的 icc 配置。重启 Docker 服务通常可以恢复默认规则。

  4. 对于生产环境,应避免将数据库(如 MySQL)的访问权限开放给 %,而应指定具体的 Docker 子网段或使用网络别名进行精细化控制。

  5. 故障排查时,分层测试:先测 IP 层(ping),再测传输层(nc/telnet),最后测应用层(curl)。这样能快速定位问题环节。

相关推荐
ClouGence1 小时前
CloudDM 3.1.0 发布:初始化、驱动管理与升级体验全面优化
docker·开源·数据库管理·企业开发·数据库工具·数据库运维
utf8mb4安全女神1 小时前
子网划分【概念+实操+理解】
运维·服务器·网络
码语智行1 小时前
拦截器、接口限流、过滤器、防重发/幂等性功能说明
开发语言·网络·python
志栋智能2 小时前
超自动化安全:构建智能安全运营的神经系统
大数据·运维·网络·人工智能·安全·自动化
华普微HOPERF2 小时前
LoRa模块,如何通过卫星通信补齐地面网络的覆盖盲区?
网络·嵌入式硬件·模块·卫星通信
zandy10112 小时前
Hermes Agent 安装与配置全流程(2026年6月最新版)
docker·agent·安装教程·kimi
取经蜗牛2 小时前
Docker 常用命令全面总结
运维·docker·容器
翔云1234562 小时前
Kubernetes 与 Docker Compose:异同详解
docker
键盘上的猫头鹰3 小时前
【Linux 基础教程(一)】概述、安装与网络配置:VMware + CentOS + NAT + XShell 远程连接
linux·网络·centos