Mac主机不喜欢Docker容器端口转发

9
我第一次尝试使用Docker,并尝试在Docker容器内运行Spring Boot Web应用程序。我正在构建该应用程序(它打包成一个自包含的jar文件),然后将其添加到Docker镜像中(这是我想要的)。您可以在SSCCE中找到我的代码,以及Bootup repo on GitHub上的README文件,其中包含了复制操作的所有说明。但基本上:

  • 我将Web应用程序构建为jar文件
  • 运行docker build -t bootup .,成功构建
  • 运行docker run -it -p 9200:9200 -d --name bootup bootup,容器似乎启动良好,如下所示的docker ps输出
  • 但是,当我将浏览器指向http://localhost:9200时,什么也没有发生。

docker ps输出:

CONTAINER ID        IMAGE               COMMAND                  CREATED
a8c4ee64a1bc        bootup              "/bin/sh -c 'java -ja"   2 days ago

STATUS              PORTS                    NAMES
Up 12 seconds       0.0.0.0:9200->9200/tcp   bootup

该Web应用程序已配置为在端口9200上运行而不是Java默认的8080端口。您可以通过在docker之外(即在本地主机上)运行./gradlew clean build && java -jar build/libs/bootup.jar来自行验证。
据我所知,我的主机上没有运行防火墙会阻止端口(我使用的是Mac 10.11.5,并验证了系统偏好设置>>安全性与隐私>>防火墙已关闭)。 有人能发现我哪里出错了吗?

更新:

我在主机上运行了 curl, netstatlsof:

HOST:
curl http://localhost:9200
curl: (52) Empty reply from server

netstat -an | grep 9200
tcp6       0      0  ::1.9200               *.*                    LISTEN     
tcp4       0      0  *.9200                 *.*                    LISTEN 

lsof -n -i4TCP:9200 | grep LISTEN
com.docke 2578 myuser   19u  IPv4 <someHexNumber>      0t0  TCP *:wap-wsp (LISTEN)

然后docker exec进入容器并运行另一个netstat

CONTAINER:
netstat -an | grep 9200
bash: netstat: command not found

附带照片更新:

我的浏览器截图(Chrome),指向http://localhost:9200

enter image description here

源代码的图片位于 http://localhost:9200

enter image description here

Chrome开发者工具检查位于http://localhost:9200的页面的图片:

enter image description here

Chrome 开发者工具中的 Network 标签截图:

enter image description here

这到底是怎么回事?!?! 根据消息来源,浏览器应该能够正常呈现我的来自Dockerland的问候!信息。但根据实际的浏览器页面显示,似乎存在网络错误。而根据Chrome开发者工具显示,我的应用程序返回了各种HTML/CSS/JS内容,这些内容甚至与我的应用程序没有任何关系(查看源代码,自己看看)!!!


1
如果您使用curl进行操作,会发生什么?如果您登录到容器中,是否确定有应用程序正在监听该端口? - Oliver Charlesworth
1
你确定本地9200端口上没有其他程序在运行吗?我曾经遇到过类似的问题,在Mac上使用Docker时,它没有警告已经无法绑定该端口。 - Paolo
1
两个问题:1. “我什么也没得到”是什么意思?2. 我假设 curl 是来自主机的 - 从容器内部执行相同的 curl,你得到了什么? - creativeChips
1
酷,你能让它在容器内运行吗?你的应用程序正在运行吗? - creativeChips
1
开发工具中的 “网络(Network)” 选项卡上发生了什么?尤其是状态码和响应(内部的 Response 选项卡)。 - Nitzan Tomer
显示剩余23条评论
5个回答

2
Dockerfile未将9200端口暴露给守护进程。请在ENTRYPOINT之前的Dockerfile中添加以下代码:EXPOSE 9200

感谢@Mano Marks (+1) - 请查看我推送的Dockerfile...你的建议没有起作用。有什么想法吗?另外,请查看我的更新和屏幕截图...非常奇怪/意外的行为。 - smeeb

2
假设您使用的是Docker Toolbox而不是beta版...
正确暴露端口的过程分为3步:
1. 在Dockerfile中使用“EXPOSE 8080”,其中8080只是端口号。 2. 在docker run命令中使用“-p 8080:8080”。 3. 确保在Oracle Virtual Box中设置了端口转发,以便boot2docker机器能够从端口8080接收请求。
这适用于使用Docker Toolbox的Windows和OSX。Linux不使用Oracle VirtualBox运行docker,因此这些主机不需要执行第三个步骤。

感谢@Shiraaz (+1),但我正在使用Docker for Mac,而不是Docker Toolbox(https://docs.docker.com/toolbox/overview/)。我已经多次提到过这一点,包括在悬赏说明中。我不想使用Docker Toolbox,因为它在Mac上出了名的有bug和不稳定,这也是Docker开发Docker for Mac的原因。 - smeeb
这很可能是正确的答案。他可以通过访问http://vmip:9200/来进行故障排除,其中vmip是虚拟机的IP地址。 - Marc Young
@MarcYoung Docker for Mac在任何情况下都不使用VirtualBox。它使用本地的xyhve虚拟化监控程序。 - smeeb
@smeeb 请记住,Docker for Mac 在运行时仍然会在后台运行一个虚拟机,因为它无法真正本地托管 Docker(虚拟机是加载 Linux 内核的)。我之前说错了,实际上不是 VirtualBox。请确保网络设置正常,以便路由到底层虚拟机。 - Marc Young
@smeeb - 不知道你是否已经在Docker for Mac上成功实现了端口转发? - Sood
谢谢@Shiraaz.M 我在Mac上使用Docker Toolbox(通过brew安装)。在Virtual Box中设置端口转发解决了问题。你救了我的一天:) - aietcn

2

我在我的OSX上使用Docker 1.12按照原样运行了你的repo。

如果你仔细查看容器启动:

2016-08-29 20:52:31.028  INFO 5 --- [           main] o.eclipse.jetty.server.ServerConnector   : Started ServerConnector@47949d1a{HTTP/1.1}{0.0.0.0:8080}
2016-08-29 20:52:31.033  INFO 5 --- [           main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)

尽管application.yml和Dockerfile中都包含9200,但应用程序正在8080上启动。

感谢@Marc Young - 我认为Shiraaz有点眉目了。我一直以为Gradle/Spring SDK会将我的application.yml捆绑到最终的uberjar中,但似乎这可能不是事实(我今晚会确认)。如果是这样的话,那么Spring将回退到默认端口8080,这可以解释你所看到的情况。因此,这里流行的理论(由Shiraaz和你自己提出)是Spring找不到外部配置文件,并默认使用Java EE的默认端口8080。请继续关注...(非常感谢你!) - smeeb

1

我在这里添加另一个答案,因为我看到你发布的Github Repo相关的内容:

所以该Repo是一个带有application.yml文件的Spring Boot Repo。

您的Dockerfile如下所示:

FROM openjdk:8

RUN mkdir /opt/bootup

ADD build/libs/bootup.jar /opt/bootup
WORKDIR /opt/bootup
EXPOSE 9200
ENTRYPOINT java -jar bootup.jar

这是将构建的jar添加到镜像中。如果我的理解正确,由于以下原因,jar文件不包括application.yml

  • 它不是构建的一部分(gradle只会打包src/main)。它位于项目根目录下
  • 它没有明确地添加到Docker中

因此,可以假设您的应用程序当前正在8080上运行(默认端口)?

有几个选项可以尝试:

  • 尝试暴露8080而不是9200(或同时暴露两者),看看是否有所不同?
  • entrypoint命令可以附加端口--server.port=9200
  • 应该将application.yml文件添加到镜像中(您可能需要添加一个参数来正确引用它)[在第一个ADD命令之后添加ADD application.yml /opt/bootup]
  • 在src/main/resources中包含application.yml文件,以便spring boot可以自动捡起。

参考资料

Spring Boot 外部配置加载顺序参考文档


1
@smeeb,这非常类似于您添加jar文件的方式。理想情况下,在第一个ADD命令之后应该是这样的 ADD application.yml /opt/bootup。在ADD之后的第一个参数将是application.yml./application.yml - Shiraaz.M
1
感谢@Shiraaz (+1) - 我做了一些调查,看起来我需要告诉Spring外部配置文件的位置。所以如果我使用你的例子并且执行 ADD application.yml /op/bootup,那么我需要使用 java -Dspring.config=<LOCATION_OF_YAML> -jar build/libs/bootup.jar 运行应用程序。那么我的 ENTRYPOINT 命令是 ENTRYPOINT java -Dspring.config=. -jar bootup.jar 还是 ENTRYPOINT java -Dspring.config=/opt/bootup -jar bootup.jar 或者其他什么? - smeeb
1
@smeeb 我建议将文件放在同一个目录下。这样应用程序就可以自动读取application.yml文件,而无需修改入口点。如果它没有被读取到,可以在入口点的末尾添加 --spring.config.location=file:application.yml 作为备选方案。 - Shiraaz.M
嗨@Shiraaz,请查看我的更新/推送的Dockerfile。第二个ADD命令(即ADD application.yml /opt/bootup)无法正常工作。我可以确定,因为在我运行容器然后通过docker exec -it <containerId> /bin/bash SSH进入它之后,我只在/opt/bootup目录中看到bootup.jar。你有任何想法我在哪里出错了吗? - smeeb
另外,我之前尝试过 ADD ./application.yml /opt/bootup(在文件名前面加上 ./),但那也不起作用... 唉。 - smeeb
显示剩余4条评论

0

好消息!(适用于MacOSx 10.15.7)

我发现了与您相同的问题,并成功通过直接打开VirtualBox连接来解决它。

首先,前往此处:

VirtualBox hosts Docker Images Network Controller setting 更改为桥接模式,然后登录VirtualBox中的虚拟机。

在虚拟机中找到实际机器的适配器标签:

eth0

在注意到原始设置为NAT后,我将其更改为桥接模式,然后

enter image description here

我成功使用了IP地址,而非本地主机地址。

在使用公共地址后,我输入以下命令:

curl -i [桥接IP地址]:9200

它顺利工作了。

但是我也注意到一些需要授权才能访问的防火墙和可访问性选项。

Accessibility fix

我祈祷这能对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接