SpringBoot @RestController 无法从 docker 容器外部访问

Posted

技术标签:

【中文标题】SpringBoot @RestController 无法从 docker 容器外部访问【英文标题】:SpringBoot @RestController not accessible from outside docker container 【发布时间】:2016-12-29 08:54:57 【问题描述】:

tl;dr 如果在 Docker 容器中运行,一个 RestController 可以正确回答,另一个则不能。

服务有两个APIalive

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
public class AliveController 
    @RequestMapping(value = "/alive", method = RequestMethod.GET)
    public ResponseEntity<?> alive() 
        return new ResponseEntity<>(HttpStatus.OK);
    

callcount

@CrossOrigin
@RestController
public class CallController 

    private static int callCount = 0;

    @RequestMapping(value = "/callcount", method = RequestMethod.GET)
    public ResponseEntity<?> callCount() 
        return new ResponseEntity<>(++callCount, HttpStatus.OK);
    

它们都通过 docker-compose 运行。

version: '2'

services:
  service:
    image: my/service
    ports:
      - "4000:4000"

docker-machine ip 返回192.168.99.100

alive 返回一个空的 200 响应。正如预期的那样。

$ curl -i http://192.168.99.100:4000/alive
HTTP/1.1 200
Content-Length: 0
Date: Mon, 22 Aug 2016 17:33:58 GMT

callcount 应该返回一个 200 响应和一个在每次调用 API 时都会增加的数字。可惜没有。

$ curl -i http://192.168.99.100:4000/callcount
HTTP/1.1 404
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 22 Aug 2016 17:37:26 GMT

"timestamp":1471887446871,"status":404,"error":"Not Found","message":"No message available","path":"/callcount"

在本地运行服务提供了预期的结果。

$ curl -i http://localhost:4000/callcount
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 22 Aug 2016 17:43:40 GMT

1

maven-spotify 插件用于从以下Dockerfile 创建图像。

FROM java:8

EXPOSE 4000

VOLUME /tmp
ADD service*.jar app.jar

# http://***.com/a/33882286/1309035
# Without this, Java uses /dev/random to seed its SecureRandom class, which can cause Java code to block unexpectedly.
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom","-jar","app.jar"]

我正在使用最新的 Docker 和 Docker-Compose 版本(2016 年 8 月 22 日下载)。

已解决!请参阅下面的更新部分。找到最终答案后将完成问题。 - 问题:为什么不能从 Docker 容器外部访问 callcount

进一步尝试:

@CrossOrigin(origins = "*", maxAge = 3600) - 结果相同 来自the spring docs 的全局 CORS 配置。 将这两个方法合并到 AliveController 中。 删除了所有从头开始重新构建的容器和映像和 docker。

更新日期

callcount API 没有被 Spring 注册。我添加了另一个 test API 来验证这一点,这也无法通过 curl 访问。 alive 仍然可以正常工作并显示在日志中。

bash-3.2$ docker logs asmstack_service_1 | grep callcount
bash-3.2$ docker logs asmstack_service_1 | grep test
bash-3.2$ docker logs asmstack_service_1 | grep alive
2016-08-23 08:42:06.530  INFO 1 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "[/alive],methods=[GET]" onto public org.springframework.http.ResponseEntity<?> de.bahr.asmstack.AliveController.alive()

我在本地使用 JDK 1.8(java.vm.vendor = Oracle Corporation)。

$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

启动方式的区别

当从 IntelliJ 和 mvn spring-boot:run 运行应用程序时,callcount 已正确注册。如果使用

运行,它不会被注册

java -Djava.security.egd=file:/dev/./urandom -jar my-service.jar

java -jar my-service.jar。这应该是无法从 Docker 容器中访问它的原因。

知道为什么会这样吗?从 2015 年底开始,在另一个项目中就是这样。

【问题讨论】:

当 Spring 启动时,日志将包括所有暴露的请求映射。在 Docker 中运行 /callcount 时,你看到它的映射了吗? 我认为您的应用程序运行良好。问题很可能是正在创建的图像。我建议你运行一个新的mvn clean package 来创建一个新图像,并检查docker images 是否真的是新创建的。总的来说,我对 spotify maven 插件的体验并不好,所以通常只是放一个 Dockerfile 并在 mvn 运行之后调用 docker build。 @daniel.eichten 我删除了每个图像和容器,并从头开始重建 docker。问题依然存在。 您如何在本地运行该服务?通过 Eclipse/IntelliJ 或 mvn spring-boot:run?必须有任何差异导致控制器无法添加或注册到您的容器中。您还可以尝试docker exec 容器中的外壳并解压缩jar 以仔细检查该类是否在其中。我还看到您使用的是基于 openjdk 的 java:8 图像。您在本地使用的是哪个 JDK? @daniel.eichten IDE 之间确实存在差异,mvnjava。相应地更新了问题。 【参考方案1】:

在@daniel.eichten 和@ShawnClark 的帮助下,我能够解决问题,但我不明白为什么这会失败/有效。 T

这不是 Docker 的问题,而是 Spring 的问题。

如here 所见(问题可能无关),我将 Spring-Application 从

@SpringBootApplication
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

@EnableAutoConfiguration
@EnableWebMvc
@Configuration
@ComponentScan
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

现在所有 API 都可以按预期访问,在 Docker 容器中运行时也是如此。

【讨论】:

以上是关于SpringBoot @RestController 无法从 docker 容器外部访问的主要内容,如果未能解决你的问题,请参考以下文章

spring Restcontroller 或 RepositoryRestResource 用啥

spring boot常用注解使用小结

Spring:RestController 和 Controller 的不同异常处理程序

Spring-Boot:同时处理多个请求

协程解析前的响应,kotlin

Junit5测试无法注入依赖但正常运行的应用程序可以注入它