Spring Sidecar 如何与 Docker 一起工作
How Spring Sidecar Works with Docker
我们有一个 sidecar 应用程序,使我们能够向 Eureka 注册我们的 node.js 应用程序以启用服务发现。
我们的 sidecar 应用程序配置如下:
server:
port: 9000
spring:
application:
name: session-service
sidecar:
port: 3000
health-url: http://sessionServiceNode:${sidecar.port}/health.json
eureka:
client:
serviceUrl:
defaultZone: http://discoveryService:8761/eureka/
instance:
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
根据配置,我们的节点应用程序 运行ning 在由 sidecar.port
属性 定义的端口 3000
上,我们的 sidecar 应用程序应该 运行 根据 server.port
.
在端口 9000
我们已经向我们的节点应用程序添加了一个端点,以允许 sidecar 检查应用程序的运行状况 (sidecar.health-url
)。主机名 sessionServiceNode
是我们为节点应用程序的容器 运行 指定的别名。
我们的 Eureka 服务 运行 在一个单独的容器中,该容器也通过别名 discoveryService 链接到 sidecar 应用程序容器。
我们有一个单独的测试 spring 启动应用程序,它 运行 位于一个单独的容器中,该容器是会话服务的使用者。此容器仅链接到发现服务容器。
Sidecar 应用程序按预期向 Eureka 注册了自己
测试服务使用两种形式的会话服务查找。一个使用假客户端:
@FeignClient(value = "session-service") // name of our registered service in eureka
interface SessionServiceClient {
@RequestMapping(value = "/document/get/24324", method = GET)
String documentGetTest();
}
而另一种方法使用更具程序化的查找:
@Autowired
private DiscoveryClient discoveryClient;
...
discoveryClient.getInstances("session-service");
当我们向测试服务发出请求时,测试服务会查找会话服务,但是 eureka 返回给我们的实例信息的 URI 为:http://172.17.0.5:3000
,这是不正确的。 172.17.0.5
是 sidecar 应用容器的 IP 地址,但是 3000
是节点应用所在的端口 运行ning on?
应该期望看到eurekareturn我们session服务容器的位置和session服务端口(http://172.17.0.5:9000
),然后sidecar做'forwarding'到节点通过 zuul 代理为我们 (http://172.17.0.6:3000
) 申请?还是Eureka直接给我们节点应用的位置?
我在下面包含了来自 Eureka 的会话服务实例信息:
<?xml version="1.0" encoding="UTF-8"?>
<application>
<name>SESSION-SERVICE</name>
<instance>
<hostName>172.17.0.5</hostName>
<app>SESSION-SERVICE</app>
<ipAddr>172.17.0.5</ipAddr>
<status>UP</status>
<overriddenstatus>UNKNOWN</overriddenstatus>
<port enabled="true">3000</port>
<securePort enabled="false">443</securePort>
<countryId>1</countryId>
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
<name>MyOwn</name>
</dataCenterInfo>
<leaseInfo>
<renewalIntervalInSecs>5</renewalIntervalInSecs>
<durationInSecs>90</durationInSecs>
<registrationTimestamp>1461223810081</registrationTimestamp>
<lastRenewalTimestamp>1461224812429</lastRenewalTimestamp>
<evictionTimestamp>0</evictionTimestamp>
<serviceUpTimestamp>1461223810081</serviceUpTimestamp>
</leaseInfo>
<metadata class="java.util.Collections$EmptyMap" />
<homePageUrl>http://c892e0c03cf4:3000/</homePageUrl>
<statusPageUrl>http://c892e0c03cf4:9000/info</statusPageUrl>
<healthCheckUrl>http://c892e0c03cf4:9000/health</healthCheckUrl>
<vipAddress>session-service</vipAddress>
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
<lastUpdatedTimestamp>1461223810081</lastUpdatedTimestamp>
<lastDirtyTimestamp>1461223033045</lastDirtyTimestamp>
<actionType>ADDED</actionType>
</instance>
</application>
编辑:
看了代码后,Eureka使用return分别从InetAddress.getLocalHost().getHostAddress()
和InetAddress.getLocalHost().getHostName()
得到的主机信息来确定实例的地址。这就是我们获取 sidecar 容器 IP 地址的原因。有什么办法可以改变这种行为吗?
Spring Cloud 正在选取第一个非环回 IP 地址。您可以做几件事中的一件。
一:忽略你不想选择的网络接口。文档 here。
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
或二:显式设置eureka注册的ipaddress或主机名(如果你在任何地方都使用相同的docker别名。对于主机名设置eureka.instance.hostname=sessionServiceNode
并删除prefer-ip-address=true
选项。对于ip 地址,eureka.instance.ipAddress=<ipaddr>
.
因此,从外观上看,Sidecar 假设 spring 引导 sidecar 应用程序和非 jvm 应用程序 运行 在同一主机上运行。在我们的场景中,我们 运行 将所有内容都放在单独的容器中。一个容器用于我们的 sidecar jvm 应用程序,一个容器用于我们的 node.js 应用程序。理论上我们可以将两个应用程序 运行 放在同一个容器中,但这违反了 Docker 每个容器一个 unix 进程的最佳实践。
为了让它工作,我们覆盖了 EurekaInstanceConfigBean
,它允许我们控制为实例选择的主机名和 IP 地址。在这种情况下,我们委托给 inetUtils
class 并从主机名(这是通过 docker 链接的非 jvm 应用程序的别名)查找 IP 地址。我们利用 spring @ConfigurationProperties
来控制 application.yml
配置文件中的 hostname/port。
SessionServiceSidecar.java
@Component
@ConfigurationProperties
public class SessionServiceSidecarProperties {
@Value("${sidecar.hostname}")
private String hostname;
@Value("${sidecar.port}")
private Integer port;
public String getHostname() {
return hostname;
}
public Integer getPort() {
return port;
}
}
SessionServiceApp.java
@SpringBootApplication
@EnableSidecar
@EnableDiscoveryClient
@Configuration
@ComponentScan
@EnableConfigurationProperties
public class SessionServiceApp {
private @Autowired SessionServiceSidecarProperties properties;
public static void main(String[] args) {
SpringApplication.run(SessionServiceApp.class, args);
}
@Bean
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
final String sidecarHostname = properties.getHostname();
final Integer sidecarPort = properties.getPort();
try {
final EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setHostname(sidecarHostname);
instance.setIpAddress(inetUtils.convertAddress(InetAddress.getByName(sidecarHostname)).getIpAddress());
instance.setNonSecurePort(sidecarPort);
return instance;
} catch(UnknownHostException e) {
throw new IllegalStateException("Could not resolve IP address of sidecar application using hostname: " + sidecarHostname);
}
}
}
application.yml
spring:
application:
name: session-service
server:
port: 9000
sidecar:
hostname: sessionServiceNode
port: 3000
health-url: http://sessionServiceNode:${sidecar.port}/health.json
eureka:
client:
serviceUrl:
defaultZone: http://discoveryService:8761/eureka/
instance:
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
希望 Spring 的人将来允许我们通过 sidecar.hostname
属性 控制 Sidecar 应用程序的主机名和端口。
希望对您有所帮助!
我们有一个 sidecar 应用程序,使我们能够向 Eureka 注册我们的 node.js 应用程序以启用服务发现。
我们的 sidecar 应用程序配置如下:
server:
port: 9000
spring:
application:
name: session-service
sidecar:
port: 3000
health-url: http://sessionServiceNode:${sidecar.port}/health.json
eureka:
client:
serviceUrl:
defaultZone: http://discoveryService:8761/eureka/
instance:
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
根据配置,我们的节点应用程序 运行ning 在由 sidecar.port
属性 定义的端口 3000
上,我们的 sidecar 应用程序应该 运行 根据 server.port
.
9000
我们已经向我们的节点应用程序添加了一个端点,以允许 sidecar 检查应用程序的运行状况 (sidecar.health-url
)。主机名 sessionServiceNode
是我们为节点应用程序的容器 运行 指定的别名。
我们的 Eureka 服务 运行 在一个单独的容器中,该容器也通过别名 discoveryService 链接到 sidecar 应用程序容器。
我们有一个单独的测试 spring 启动应用程序,它 运行 位于一个单独的容器中,该容器是会话服务的使用者。此容器仅链接到发现服务容器。
Sidecar 应用程序按预期向 Eureka 注册了自己
测试服务使用两种形式的会话服务查找。一个使用假客户端:
@FeignClient(value = "session-service") // name of our registered service in eureka
interface SessionServiceClient {
@RequestMapping(value = "/document/get/24324", method = GET)
String documentGetTest();
}
而另一种方法使用更具程序化的查找:
@Autowired
private DiscoveryClient discoveryClient;
...
discoveryClient.getInstances("session-service");
当我们向测试服务发出请求时,测试服务会查找会话服务,但是 eureka 返回给我们的实例信息的 URI 为:http://172.17.0.5:3000
,这是不正确的。 172.17.0.5
是 sidecar 应用容器的 IP 地址,但是 3000
是节点应用所在的端口 运行ning on?
应该期望看到eurekareturn我们session服务容器的位置和session服务端口(http://172.17.0.5:9000
),然后sidecar做'forwarding'到节点通过 zuul 代理为我们 (http://172.17.0.6:3000
) 申请?还是Eureka直接给我们节点应用的位置?
我在下面包含了来自 Eureka 的会话服务实例信息:
<?xml version="1.0" encoding="UTF-8"?>
<application>
<name>SESSION-SERVICE</name>
<instance>
<hostName>172.17.0.5</hostName>
<app>SESSION-SERVICE</app>
<ipAddr>172.17.0.5</ipAddr>
<status>UP</status>
<overriddenstatus>UNKNOWN</overriddenstatus>
<port enabled="true">3000</port>
<securePort enabled="false">443</securePort>
<countryId>1</countryId>
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
<name>MyOwn</name>
</dataCenterInfo>
<leaseInfo>
<renewalIntervalInSecs>5</renewalIntervalInSecs>
<durationInSecs>90</durationInSecs>
<registrationTimestamp>1461223810081</registrationTimestamp>
<lastRenewalTimestamp>1461224812429</lastRenewalTimestamp>
<evictionTimestamp>0</evictionTimestamp>
<serviceUpTimestamp>1461223810081</serviceUpTimestamp>
</leaseInfo>
<metadata class="java.util.Collections$EmptyMap" />
<homePageUrl>http://c892e0c03cf4:3000/</homePageUrl>
<statusPageUrl>http://c892e0c03cf4:9000/info</statusPageUrl>
<healthCheckUrl>http://c892e0c03cf4:9000/health</healthCheckUrl>
<vipAddress>session-service</vipAddress>
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
<lastUpdatedTimestamp>1461223810081</lastUpdatedTimestamp>
<lastDirtyTimestamp>1461223033045</lastDirtyTimestamp>
<actionType>ADDED</actionType>
</instance>
</application>
编辑:
看了代码后,Eureka使用return分别从InetAddress.getLocalHost().getHostAddress()
和InetAddress.getLocalHost().getHostName()
得到的主机信息来确定实例的地址。这就是我们获取 sidecar 容器 IP 地址的原因。有什么办法可以改变这种行为吗?
Spring Cloud 正在选取第一个非环回 IP 地址。您可以做几件事中的一件。
一:忽略你不想选择的网络接口。文档 here。
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
或二:显式设置eureka注册的ipaddress或主机名(如果你在任何地方都使用相同的docker别名。对于主机名设置eureka.instance.hostname=sessionServiceNode
并删除prefer-ip-address=true
选项。对于ip 地址,eureka.instance.ipAddress=<ipaddr>
.
因此,从外观上看,Sidecar 假设 spring 引导 sidecar 应用程序和非 jvm 应用程序 运行 在同一主机上运行。在我们的场景中,我们 运行 将所有内容都放在单独的容器中。一个容器用于我们的 sidecar jvm 应用程序,一个容器用于我们的 node.js 应用程序。理论上我们可以将两个应用程序 运行 放在同一个容器中,但这违反了 Docker 每个容器一个 unix 进程的最佳实践。
为了让它工作,我们覆盖了 EurekaInstanceConfigBean
,它允许我们控制为实例选择的主机名和 IP 地址。在这种情况下,我们委托给 inetUtils
class 并从主机名(这是通过 docker 链接的非 jvm 应用程序的别名)查找 IP 地址。我们利用 spring @ConfigurationProperties
来控制 application.yml
配置文件中的 hostname/port。
SessionServiceSidecar.java
@Component
@ConfigurationProperties
public class SessionServiceSidecarProperties {
@Value("${sidecar.hostname}")
private String hostname;
@Value("${sidecar.port}")
private Integer port;
public String getHostname() {
return hostname;
}
public Integer getPort() {
return port;
}
}
SessionServiceApp.java
@SpringBootApplication
@EnableSidecar
@EnableDiscoveryClient
@Configuration
@ComponentScan
@EnableConfigurationProperties
public class SessionServiceApp {
private @Autowired SessionServiceSidecarProperties properties;
public static void main(String[] args) {
SpringApplication.run(SessionServiceApp.class, args);
}
@Bean
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
final String sidecarHostname = properties.getHostname();
final Integer sidecarPort = properties.getPort();
try {
final EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setHostname(sidecarHostname);
instance.setIpAddress(inetUtils.convertAddress(InetAddress.getByName(sidecarHostname)).getIpAddress());
instance.setNonSecurePort(sidecarPort);
return instance;
} catch(UnknownHostException e) {
throw new IllegalStateException("Could not resolve IP address of sidecar application using hostname: " + sidecarHostname);
}
}
}
application.yml
spring:
application:
name: session-service
server:
port: 9000
sidecar:
hostname: sessionServiceNode
port: 3000
health-url: http://sessionServiceNode:${sidecar.port}/health.json
eureka:
client:
serviceUrl:
defaultZone: http://discoveryService:8761/eureka/
instance:
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
希望 Spring 的人将来允许我们通过 sidecar.hostname
属性 控制 Sidecar 应用程序的主机名和端口。
希望对您有所帮助!