SpringCloud以及Nacos服务注册IP选择问题
作者:mmseoamin日期:2023-12-18

一、SpringCloud以及Nacos服务注册IP选择问题

场景:

微服务部署后,需要相互调用,其中服务A调用服务B时发现无法调用。其中服务注册和发现以及配置中心使用Nacos

分析:

检查了多遍代码后,没有发现调用方式有问题,所以只能是网络问题。通过postman直接调用服务B,发现可以调通,但是使用服务A不行,于是检查服务A在注册中心注册的IP,发现和并不是服务B启动机器的IP。这就是问题所在了。

为什么注册的IP和真实IP不符合呢?原因是Nacos客户端在注册服务时会从机器网卡中选择一个IP来注册,当机器存在多个网卡(例如存在虚拟网卡)时,所选则的IP可能不是真是的物理机的IP,所以,当注册了的是非真实IP后,另一台机器调用时是不可能调通的。

解决:

知道问题后,就要解决,查了一下SpringCloud的官方文档,发现有一项配置如下:

Sometimes, it is useful to ignore certain named network interfaces so that they can be excluded from Service Discovery registration (for example, when running in a Docker container). A list of regular expressions can be set to cause the desired network interfaces to be ignored.

You can also force the use of only specified network addresses by using a list of regular expressions.

spring:
  cloud:
	inetutils:
	  preferredNetworks:
		- 192.168
		- 10.0

该项配置用于指定首选IP,当有多个网卡时,指定该IP地址后(支持正则),客户端在选择IP时就会选择符合preferredNetworks配置的IP地址进行注册。

同样的,Nacos也可以配置自己的首选IP以及网卡选择:

spring.cloud.nacos.discovery.ip:
spring.cloud.nacos.discovery.networkInterface

我们选择其中一个配置就可以,都能达到相同的效果。

扩展:

虽然问题解决了,但是还是要更深入的了解一下这个IP选择的逻辑。翻了一通源码发现,其大致逻辑如下:

SpringCloud以及Nacos服务注册IP选择问题,img,第1张

Nacos首先检查有没有ip及networkInterface配置,如果有则使用配置的IP,否则检查networkInterface,并获取IP,如果两者都为空,则使用inetUtils.findFirstNonLoopbackHostInfo().getIpAddress()来获取IP:

SpringCloud以及Nacos服务注册IP选择问题,img,第2张

而findFirstNonLoopbackHostInfo()的部分逻辑如下:

SpringCloud以及Nacos服务注册IP选择问题,img,第3张

它最终会返回一个匹配的IPV4地址,并且排除本机回环网络(127.0.0.0-127.255.255.255),并且匹配是否是首选网络(如果配置了preferredNetworks)。

二、注册为指定IP和端口

如果选择固定Ip注册可以配置

spring.cloud.nacos.discovery.ip = 192.168.0.118
spring.cloud.nacos.discovery.port = 9000

如果选择固定网卡配置项

spring.cloud.nacos.discovery.networkInterface = eth0

如果想更丰富的选择,可以使用spring cloud 的工具 InetUtils进行配置

  • 具体说明可以自行检索: https://github.com/spring-cloud/spring-cloud-commons/blob/master/docs/src/main/asciidoc/spring-cloud-commons.adoc

    spring.cloud.inetutils.default-hostname

    spring.cloud.inetutils.default-ip-address

    spring.cloud.inetutils.ignored-interfaces[0]=eth0 # 忽略网卡,eth0

    spring.cloud.inetutils.ignored-interfaces=eth.* # 忽略网卡,eth.*,正则表达式

    spring.cloud.inetutils.preferred-networks=10.34.12 # 选择符合前缀的IP作为服务注册IP

    spring.cloud.inetutils.timeout-seconds

    spring.cloud.inetutils.use-only-site-local-interfaces

    更多配置

    spring.cloud.nacos.discovery.server-addr #Nacos Server 启动监听的ip地址和端口

    spring.cloud.nacos.discovery.service #给当前的服务命名

    spring.cloud.nacos.discovery.weight #取值范围 1 到 100,数值越大,权重越大

    spring.cloud.nacos.discovery.network-interface #当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址

    spring.cloud.nacos.discovery.ip # 优先级最高

    spring.cloud.nacos.discovery.port # 默认情况下不用配置,会自动探测

    spring.cloud.nacos.discovery.namespace # 常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 spring.cloud.nacos.discovery.access-key # 当要上阿里云时,阿里云上面的一个云账号名

    spring.cloud.nacos.discovery.secret-key # 当要上阿里云时,阿里云上面的一个云账号密码

    spring.cloud.nacos.discovery.metadata #使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息

    spring.cloud.nacos.discovery.log-name # 日志文件名

    spring.cloud.nacos.discovery.enpoint # 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址

    ribbon.nacos.enabled # 是否集成Ribbon 默认为true

    三、通过内网ip注册为指定外网ip

    Nacos注册中心是: https://github.com/alibaba/nacos

    各个服务通过Nacos客户端将服务信息注册到Nacos上,当Nacos服务注册的IP默认选择出问题时,可以通过查阅对应的客户端文档,来选择配置不同的IP。

    大多公司服务器环境会有两套ip,一套是对内网,一套是对外网。即同一台服务器,有对应两个ip(例:10.0.0.1,100.0.0.1)。

    在使用nacos做为注册中心,由于内网环境的因素,A服务注册上去的ip可能是100.0.0.1,B服务注册上去的ip可能是10.0.0.2,经验证10之间调用互通,100之间也互通。但10调用100不同,或者反之也不行。

    解决方案:

    配置文件加如下配置:

    nacos-ip-100.0.0.1 = 10.0.0.1
    spring.cloud.nacos.discovery.ip = ${nacos-ip-${spring.cloud.client.ip-address}}
    

    四、Nacos服务注册地址为内网IP的解决办法

    问题

    最近在使用Spring Cloud Alibaba这一套微服务解决方案,但是在服务注册的时候,网关死活找不到微服务地址,自己的微服务通过网关怎么也访问不到。

    查找原因

    仔细一查才发现,网关去访问了一个莫名其妙的IP地址,

    去Nacos服务详情去看,果然,我的微服务注册到Nacos的IP地址上也是这个地址,

    然后我去我电脑查找这个IP地址,还真有这么一个地址,那么问题来了,Nacos为什么会随意找个本机IP地址然后注册上去?

    Nacos服务注册的IP

    Nacos注册中心是: https://github.com/alibaba/nacos

    各个服务通过Nacos客户端将服务信息注册到Nacos上

    当Nacos服务注册的IP默认选择出问题时,可以通过查阅对应的客户端文档,来选择配置不同的网卡或者IP

    (参考org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties的配置)

    解决办法

    例如,使用了Spring cloud alibaba(官方文档)作为Nacos客户端,

    服务默认获取了内网IP 192.168.1.21,

    可以通过配置spring.cloud.inetutils.preferred-networks=10.34.12,使服务获取内网中前缀为10.34.12的IP

    如何配置

    # 如果选择固定Ip注册可以配置
    spring.cloud.nacos.discovery.ip = 10.2.11.11
    spring.cloud.nacos.discovery.port = 9090
    # 如果选择固定网卡配置项
    spring.cloud.nacos.discovery.networkInterface = eth0
    # 如果想更丰富的选择,可以使用spring cloud 的工具 InetUtils进行配置
    # 具体说明可以自行检索: https://github.com/spring-cloud/spring-cloud-commons/blob/master/docs/src/main/asciidoc/spring-cloud-commons.adoc
    spring.cloud.inetutils.default-hostname
    spring.cloud.inetutils.default-ip-address
    spring.cloud.inetutils.ignored-interfaces[0]=eth0   # 忽略网卡,eth0
    spring.cloud.inetutils.ignored-interfaces=eth.*     # 忽略网卡,eth.*,正则表达式
    spring.cloud.inetutils.preferred-networks=10.34.12  # 选择符合前缀的IP作为服务注册IP
    spring.cloud.inetutils.timeout-seconds
    spring.cloud.inetutils.use-only-site-local-interfaces
    

    更多配置

    spring.cloud.nacos.discovery.server-addr  #Nacos Server 启动监听的ip地址和端口
    spring.cloud.nacos.discovery.service  #给当前的服务命名
    spring.cloud.nacos.discovery.weight  #取值范围 1 到 100,数值越大,权重越大
    spring.cloud.nacos.discovery.network-interface #当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址
    spring.cloud.nacos.discovery.ip  # 优先级最高
    spring.cloud.nacos.discovery.port  # 默认情况下不用配置,会自动探测
    spring.cloud.nacos.discovery.namespace # 常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
    spring.cloud.nacos.discovery.access-key  # 当要上阿里云时,阿里云上面的一个云账号名
    spring.cloud.nacos.discovery.secret-key # 当要上阿里云时,阿里云上面的一个云账号密码
    spring.cloud.nacos.discovery.metadata    #使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息
    spring.cloud.nacos.discovery.log-name   # 日志文件名
    spring.cloud.nacos.discovery.enpoint   # 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
    ribbon.nacos.enabled  # 是否集成Ribbon 默认为true
    

    五、较合理的实现方式

    1. 使用docker进行部署

      • 项目打包的时候指定映射后的注册IP和端口或者ip前缀

      • 打包进镜像之后运行容器,设置上方指定的端口映射

      • 这样也能指定局域网的IP也可以通过docker将相关端口进行映射出来

        • 问题,假如需要映射到公网IP?
        • 也可以通过网关进行整改,所有服务都走网关,

          • 只需要将网关部署到连接内网服务的网卡上面,将网关的端口进行映射出去供外部调用
            • 问题,服务间的调用需要保证注册网络是统一网络
            • 通过自定义主机域名,或者本地dns服务,

              • 给每个服务指定域名
              • 服务注册时候需要将域名进行者注册上去,
                • 问题:可能会更麻烦但是对于网络较为复杂的情况会改善一点劳动量