Spring-Cloud Docker 服务注册

微服务架构中的单个服务独立自治,互为依赖。犹如军队中的各个小队,所有的小队需要进行统一调配和管理,所以各服务需向注册中心报备信息,即 服务注册 。文章 Spring-Cloud 服务注册中心 中已经介绍了 注册中心 相关内容,续接前文,介绍如何进行 服务注册 以及如何使用 Docker 布署等。

服务依赖

Spring Cloud中的 注册中心 以及 服务注册 等相关内容均是依赖 netflix ,不同类型依赖于不同的 Jar 包,从而实现整体架构。相对于 注册中心 来说, 服务注册 区别并不大,而且依赖内容也基本相同,下面是其依赖文件  build.gradle

// Spring 依赖管理插件, 用于自动解决对应的依赖版本号
apply plugin: 'io.spring.dependency-management'

ext {
    // Spring Cloud 版本号
    set('springCloudVersion', "Hoxton.SR1")
}

dependencies {
    // 注册中心所需依赖包
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

属性配置

由于 注册中心服务注册 依赖的包都是 spring-cloud-starter-netflix-eureka-server ,所以区别它们的地方之一就在于配置文件。下面创建服务 spring-cloud-client ,而把注册中心的地址指定为 文章  Spring-Cloud 服务注册中心 所配置的路径  http://localhost:9000/eureka/ , 具体内容如下:

server:
  port: 9001 # 服务启动端口
  
spring:
  application:
    name: spring-cloud-client
    
eureka:
  instance:
    prefer-ip-address: true 
    ip-address: 127.0.0.1 

  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
    healthcheck:
      enabled: true

项目启动

服务注册与 注册中心 不同也在于启动项, 注册中心 采用的是  @EnableEurekaServer 提供服务,而 服务注册 则采用  @EnableDiscoveryClient@EnableDiscoveryClient 把该服务标记为客户端, Spring  启动时读取不同的配置参数。

@EnableDiscoveryClient
@SpringBootApplication
open class Application

仪表盘

启动 注册中心 与刚刚创建的 Client 服务 spring-cloud-client ,仪表盘上展示着如下信息:

名称 描述
spring-cloud-client 配置文件中指定的 spring.application.name
192.168.0.100:spring-cloud-client:9001 服务所在 IP : 服务名称 : 服务端口

在编译文件 build.gradle 中加入了 spring-boot-starter-actuator 的依赖,也在  application.yml  中配置了参数  eureka.client.healthcheck.enabled:true , 所以可以点击仪表盘中 Status 栏目从而查看服务健康数据,以便于我们监控服务状态。

Docker 注册

前面的 注册中心服务注册 已是把核心逻辑介绍完了,但是真正的生产环境肯定是无法这样直接使用的,不可能把所有的东西都放一台机器或一个网络当中,那样的话 微服务 架构就没有必要了。

Docker 我相信是当之无愧的容器一哥了,数之不尽的服务使用 Docker 进行布署。前面的 服务注册 逻辑已经没有任何问题了,但是如果你把该服务使用  Docker 进行布署的话,很好多麻烦就找上门了。

注册中心需要通过客户端服务提供的 实例数据 进行 健康检查 ,而 注册中心 最需要的是 IP 以及 PORT 。但是在容器内取到的  IP  是主机中子网的  IP ,外面的机器无法访问, 注册中心 就无法进行健康检查,从仪表盘上更无法定位服务位置等。

宿主IP

客户端服务实体信息是通过 eureka.instance.* 等一系列参数决定的,当不设置的时候,会取默认值,一般是  localhost  进行设置,所以我们在容器中必须手动指定对应的参数和端口。

eureka:
  instance:
    prefer-ip-address: true # 强制使用 IP
    ip-address: 127.0.0.1 # 指定 IP 地址

上面代码中指定了 IP ,但是那不是我们需要的,身处容器中我们需要的 IP 宿主机的  IP ,所以需要在容器启动时把宿主机 IP 带入到容器当中。 Docker 的参数中有  --env,-e 可以对容器设置环境变量,从而可以满足需求。而如何获取宿主机的 IP 地址也是一个问题,首先我们会想到 ifconfig 然后再经过一系列  Shell 得到需要的  IP 。当然,前一种方式相对复杂了些,还好 Linux 提供了一个命令  hostname 可以满足需求。

--env HOST_IP=`hostname -i` # hostname -i >> 192.168.0.100 我本机 IP

对于环境变量的读取当然有各种各样的方式, Spring 的操作比较简单,仅需在属性文件中使用  ${...} 就可取得对应环境变量。有时我们本地运行时不一定存在环境变量,所以我们可以使用  ${...:默认值} 的形式提供默认值。

eureka:
  instance:
    prefer-ip-address: true
    ip-address: ${HOST_IP:127.0.0.1}

到此完美解决以 Docker 容器为单元的客户端注册,但属性配置文件  application.yml 中的 IPPORT 仍是写死的,这也需要配置文件或者变量的方式进行解决。

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/

Appendix

若在实践过程中仍然无法注册或者定位不到服务时,那就进一步检查端口映射等是否正确。很多情况下 Docker  型应用会把端口通过  -p 进行映射,我们也可以通过它解决问题。若通常使用 DNS 进行定位的话,可以使用命令 Docker 命令进行域名映射。

--add-host HOST_IP:`hostname -i`