Docker Desktop 环境下 Eureka 注册问题修复指引
一、问题描述
本地 IDEA 启动服务后,Eureka 控制台(Status 列)显示:
```
host.docker.internal:skyer-goods:8310
host.docker.internal:skyer-stock:8350
```
而不是期望的本机真实 IP:
```
192.168.0.233:skyer-goods:8310
192.168.0.233:skyer-stock:8350
```
**影响**:其他服务通过 Feign 调用时,请求会发到 `host.docker.internal`,导致连接失败或调用到错误的地址。
二、根因分析
2.1 直接原因
Spring Cloud Finchley.SR2 的 `EurekaInstanceConfigBean` 有一个 `@PostConstruct init()` 方法,它会调用 `InetAddress.getLocalHost().getHostName()` 获取本机 hostname。
当本机安装了 **Docker Desktop** 时,Docker 会修改系统的 hosts 文件,将本机 hostname 解析为 `host.docker.internal`,导致 `InetAddress.getLocalHost()` 返回错误地址。
2.2 为什么 YAML 配置不生效?
```yaml
eureka:
instance:
hostname: 192.168.0.233
prefer-ip-address: true
ip-address: 192.168.0.233
```
上述配置在 `init()` 执行**之前**绑定,但 `init()` 方法内部会**无条件**用 `InetUtils.findFirstNonLoopbackAddress()` 重新获取地址并覆盖 `hostname` 和 `ipAddress`,所以 YAML 配置无效。
2.3 影响的模块
所有注册到 Eureka 的服务都会受影响,包括但不限于:
-
`skyer-goods`
-
`skyer-stock`
-
其他本地启动的 business-service 模块
三、修复方案
3.1 方案原理
通过 `BeanPostProcessor` 在 `EurekaInstanceConfigBean` 的 `init()` 执行完毕后,强制将 `hostname`、`ipAddress`、`instanceId` 改写为本机真实 IP。
IP 获取策略:**遍历本机网卡**,取第一个非回环、非 Docker 虚拟网桥的 IPv4 地址。
-
✅ 完全自动,DHCP 分配 IP 变化后无需修改任何代码
-
✅ 不写死任何 IP 地址
-
✅ 通过 `META-INF/spring.factories` 注册到 Spring Cloud bootstrap context,确保在 `EurekaInstanceConfigBean` 创建前就生效
3.2 文件清单
每个需要修复的模块,需要添加以下两个文件:
| 文件 | 作用 |
|------|------|
| `src/main/java/.../config/EurekaHostnameFixConfig.java` | 核心修复逻辑:网卡遍历获取 IP + BeanPostProcessor 强制修正 |
| `src/main/resources/META-INF/spring.factories` | 注册到 bootstrap context(如已存在则追加一行) |
3.3 核心代码
`EurekaHostnameFixConfig.java`
```java
package 你的包名.config;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
/**
* 修复 Docker Desktop 环境下 Eureka 注册地址为 host.docker.internal 的问题。
*
* <p>原理:通过 BeanPostProcessor 在 EurekaInstanceConfigBean 初始化后强制修正注册地址。
*
* <p>IP 获取策略:遍历本机网卡,取第一个非回环、非 Docker 虚拟网桥的 IPv4 地址。
* 该策略完全自动,DHCP 分配 IP 变化后无需任何修改。
*
* @author yuelinsoft
*/
@Configuration
public class EurekaHostnameFixConfig {
/**
* 遍历本机网卡,获取第一个非回环、非 Docker 虚拟网桥的 IPv4 地址。
*/
private static String detectLocalIp() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) {
continue;
}
// 跳过 Docker 虚拟网桥
String name = ni.getName();
if (name.startsWith("docker") || name.startsWith("br-")) {
continue;
}
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
continue;
}
String ip = addr.getHostAddress();
if (ip.contains(".")) {
System.out.println("=== EurekaFixed 检测到本机 IP(网卡:" + name + "): " + ip + " ===");
return ip;
}
}
}
} catch (Exception e) {
System.err.println("=== EurekaFixed 遍历网卡失败: " + e.getMessage() + " ===");
}
return "127.0.0.1";
}
private static final String TARGET_IP = detectLocalIp();
static {
System.out.println("=== EurekaFixed 本机 IP 检测完成: " + TARGET_IP + " ===");
}
@Bean
public org.springframework.beans.factory.config.BeanPostProcessor eurekaInstanceConfigPostProcessor() {
return new org.springframework.beans.factory.config.BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof EurekaInstanceConfigBean)) {
return bean;
}
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) bean;
config.setHostname(TARGET_IP);
config.setIpAddress(TARGET_IP);
config.setPreferIpAddress(true);
String appName = config.getAppname();
int port = config.getNonSecurePort() > 0
? config.getNonSecurePort()
: config.getSecurePort();
if (appName != null && !appName.isEmpty() && port > 0) {
String newInstanceId = TARGET_IP + ":" + appName + ":" + port;
config.setInstanceId(newInstanceId);
}
System.out.println("=== EurekaFixed 修正完成: hostname="
-
config.getHostname()
-
", instanceId=" + config.getInstanceId() + " ===");
return bean;
}
};
}
}
```
`META-INF/spring.factories`
```properties
修复 Docker Desktop 环境下 Eureka 注册地址为 host.docker.internal 的问题
通过 BootstrapConfiguration 注册,确保在 bootstrap context 阶段生效
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
你的包名.config.EurekaHostnameFixConfig
```
> **注意**:如果 `spring.factories` 已存在,在 `BootstrapConfiguration=` 后面用逗号追加新类名即可,例如:
> ```
> org.springframework.cloud.bootstrap.BootstrapConfiguration=已有的类,新增的类
> ```
四、如何为新模块添加修复
假设要为 `skyer-order` 模块添加修复:
**第一步**:在 `skyer-order/src/main/java/org/skyer/order/config/` 下新建 `EurekaHostnameFixConfig.java`
-
包名改为 `org.skyer.order.config`
-
日志里的 `(skyer-goods)` 改为 `(skyer-order)`
**第二步**:在 `skyer-order/src/main/resources/META-INF/` 下新建或更新 `spring.factories`
-
如果文件已存在,在 `BootstrapConfiguration=` 后追加类名
-
如果文件不存在,按上述格式新建
**第三步**:编译验证
```bash
cd D:\github\dev\erp_backend\business-service\skyer-order
mvn clean compile -DskipTests
```
启动后搜索控制台日志:
```
=== EurekaFixed 检测到本机 IP(网卡:...): 192.168.0.XXX ===
=== EurekaFixed 修正完成: hostname=192.168.0.XXX, instanceId=192.168.0.XXX:skyer-order:端口 ===
```
五、验证步骤
-
启动服务,查看控制台日志,确认 `EurekaFixed` 日志出现且 IP 正确
-
打开 Eureka 控制台(`http://192.168.0.252:8000`)
-
检查 Status 列,确认显示 `本机IP:服务名:端口` 格式(而不是 `host.docker.internal`)
-
通过 Feign 调用该服务,确认调用正常
六、常见问题
Q1:DHCP 分配 IP 变了,需要改代码吗?
不需要。`detectLocalIp()` 在每次启动时自动遍历网卡获取当前 IP,完全自动。
Q2:没有安装 Docker Desktop,还需要这个修复吗?
不需要。该问题只在安装了 Docker Desktop 的机器上出现。
Q3:生产环境需要这个修复吗?
不需要。生产环境通常不安装 Docker Desktop,不会出现 `host.docker.internal` 问题。建议通过 Maven profile 或条件注解控制该配置类只在本地 dev 环境生效。
Q4:启动后 Eureka 控制台还是显示 host.docker.internal?
检查以下几点:
-
`spring.factories` 是否在 `META-INF/` 目录下(注意是 `META-INF`,不是 `meta-inf`)
-
编译后 `target/classes/META-INF/spring.factories` 是否正确
-
控制台是否有 `EurekaFixed` 日志,若无则说明 `BootstrapConfiguration` 注册未生效
七、已修复模块
| 模块 | 路径 |
|------|------|
| skyer-goods | `business-service/skyer-goods/src/main/java/org/skyer/goods/config/EurekaHostnameFixConfig.java` |
| skyer-stock | `business-service/skyer-stock/src/main/java/org/skyer/stock/config/EurekaHostnameFixConfig.java` |
*文档创建时间:2026-06-19*
*作者:郭龙*