docker swarm 中 dubbo服务注册IP问题

发布于:2025-06-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

问题

springboot + dubbo 微服务部署在docker swarm 中,升级dubbo 版本到 2.7.14 后出现注册服务的IP不对,导致 dubbo 服务调用异常。
因为 docker swarm 环境下部署服务没法指定注册到注册中心的服务IP地址,此时dubbo注册服务的IP地址使用的是 org.apache.dubbo.common.utils.NetUtils#findNetworkInterface 方法获取的,该方法的源码如下:

    /**
     * Get the suitable {@link NetworkInterface}
     *
     * @return If no {@link NetworkInterface} is available , return <code>null</code>
     * @since 2.7.6
     */
    public static NetworkInterface findNetworkInterface() {

        List<NetworkInterface> validNetworkInterfaces = emptyList();
        try {
            validNetworkInterfaces = getValidNetworkInterfaces();
        } catch (Throwable e) {
            logger.warn(e);
        }

        NetworkInterface result = null;

        // Try to find the preferred one
        for (NetworkInterface networkInterface : validNetworkInterfaces) {
            if (isPreferredNetworkInterface(networkInterface)) {
                result = networkInterface;
                break;
            }
        }

        if (result == null) { // If not found, try to get the first one
            for (NetworkInterface networkInterface : validNetworkInterfaces) {
                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
                    if (addressOp.isPresent()) {
                        try {
                            if (addressOp.get().isReachable(100)) {
                               	// <==== 2.7.14 前的版本
                                // result = networkInterface;
                                // break;
                                // 2.7.14 前的版本 ====> 
                                return networkInterface;
                            }
                        } catch (IOException e) {
                            // ignore
                        }
                    }
                }
            }
        }

        if (result == null) {
            result = first(validNetworkInterfaces);
        }

        return result;
    }

该方法用于查找合适的网络接口(NetworkInterface),逻辑如下:

  • 获取所有有效的网络接口;
  • 优先匹配系统指定的首选网络接口;
  • 若未找到首选接口,则遍历接口地址,返回第一个可到达的接口;2.7.14 前的版本会一直遍历,然后返回最后一个可到达的接口
  • 若仍未找到,则返回第一个有效接口。

我们看看 docker swarm 中容器的网络信息:

/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:05:CD  
          inet addr:10.0.5.205  Bcast:10.0.5.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:319384 errors:0 dropped:0 overruns:0 frame:0
          TX packets:337887 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:53199330 (50.7 MiB)  TX bytes:44167534 (42.1 MiB)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:13:00:0E  
          inet addr:172.19.0.14  Bcast:172.19.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:46473 errors:0 dropped:0 overruns:0 frame:0
          TX packets:48581 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:7550252 (7.2 MiB)  TX bytes:4590693 (4.3 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:10034 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10034 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1318656 (1.2 MiB)  TX bytes:1318656 (1.2 MiB)

/ # 

从2.7.14版本注册服务的IP获取的是 eth1,也就是虚拟网桥段(docker_gwbridge)的地址,这个地址只能够在宿主机上访问,跨主机是不能访问的,正确的地址应该是eth0(Overlay网络)。

解决方案

如果容器是多网卡的情况,在不手动指定注册服务的ip时都是不太稳定的,也就是说只能通过人为手动指定IP。但是在docker swarm中容器的ip是动态分配的,没办法提前指定。

使用服务名

在docker swarm 中 Service 提供内置的 DNS 解析和负载均衡能力,注册时使用docker swarm 中的服务名注册,这样也就能实现dubbo服务的正常调用了。

version: "3.7"
networks:
  api-net: 
    external: true
services:      
  ucs:
    image: xxx
    networks:
      - api-net
    deploy:
      replicas: 1
    environment:
      - DUBBO_IP_TO_REGISTRY=ucs

以上通过配置环境变量DUBBO_IP_TO_REGISTRY设置了注册服务的ip为docker swarm 中的服务名。

但是这个方案在服务更新时,dubbo consumer 会出现短暂的消费异常:

org.apache.dubbo.rpc.RpcException: Failed to invoke the method getUser in the service com.mbzj.ucs.api.service.UserService. 
No provider available for the service com.mbzj.ucs.api.service.UserService from registry RegistryDirectory(registry: zookeeper:2181)
-Directory(invokers: 1[ucs:20880], validInvokers: 0[], invokersToReconnect: 1[ucs:20880]) on the consumer 172.19.0.14 using the dubbo version 3.2.15. Please check if the providers have been started and registered.

大致的原因是新服务注册后,旧服务后注销导致,因为新旧服务的ip都是使用的域名 ucs。

指定网卡偏好

dubbo 支持 dubbo.network.interface.ignoreddubbo.network.interface.preferred 指定网络接口

version: "3.7"
networks:
  api-net: 
    external: true
services:      
  ucs:
    image: xxx
    networks:
      - api-net
    deploy:
      replicas: 1
    environment:
      - JAVA_TOOL_OPTIONS="-Ddubbo.network.interface.preferred=eth0"

参考

issue#8676
issue#8679
issue#9617
issue#9737