9

I am experimenting with Docker for the first time, and am trying to get a Spring Boot web app to run inside a Docker container. I am building the app (which packages up into a self-contained jar) and then adding it to the Docker image (which is what I want).

You can find my SSCCE at this Bootup repo on GitHub, whose README has all the instructions to reproduce what I'm seeing. But basically:

  • I build the web app into a jar
  • Run docker build -t bootup . which succeeds
  • Run docker run -it -p 9200:9200 -d --name bootup bootup and then container seems to start up just fine, as is evidence by the docker ps output below
  • However, when I point a browser to http://localhost:9200, I get nothing

docker ps output:

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

The web app is configured to run on port 9200, not the Java default of 8080. You can see this for yourself by running the app outside of docker (so, just locally on you host machine) by running ./gradlew clean build && java -jar build/libs/bootup.jar.

To my knowledge, there is no Firewall running on my host that would be blocking ports (I am on Mac 10.11.5 and verified that System Preferences >> Security & Privacy >> Firewall is turned off).

Can anyone spot where I'm going awry?


Updates:

I ran a curl, netstat and lsof on the host:

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)

And then docker exec'd into the container and ran another netstat:

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

Update w/ photos:

Picture of my browser (Chrome) pointed to http://localhost:9200:

enter image description here

Picture of the source code at http://localhost:9200:

enter image description here

Picture of Chrome Developer Tools inspecting the page at http://localhost:9200:

enter image description here

Picture of the Network tab in Chrome Developer Tools:

enter image description here

What the heck is going on here?!?!? According to the source, the browser should be rendering my Well hello there, from Dockerland! message just fine. According to the actual browser page, it looks like there is a networking error. And according to Chrome Developer Tools, my app is returning all sorts of HTML/CSS/JS content that is not even remotely apart of my app (check out the source code, see for yourself)!!!

smeeb
  • 22,487
  • 41
  • 197
  • 389
  • 1
    What happens if you hit it with curl? What happens if you shell into the container - are you sure something's listening on the port? – Oliver Charlesworth Aug 26 '16 at 12:16
  • 1
    Are you sure nothing else is running on 9200 locally already? I had similar that docker on Mac didn't warn it had failed to bind the port – Paolo Aug 26 '16 at 12:17
  • Thanks @OliverCharlesworth (+1) - please see my updates, any ideas? – smeeb Aug 26 '16 at 12:36
  • Thanks @Paolo (+1) - please see my updates, any ideas? The code is all up there on GitHub. – smeeb Aug 26 '16 at 12:36
  • 1
    "curl: (52) Empty reply from server" port forwarding appears to be working, your application isn't outputting anything. – BMitch Aug 26 '16 at 12:45
  • Thanks @BMitch (+1) - so if you clone that repo and run `./gradlew clean build && java -jar build/libs/bootup.jar`, you'll see that it starts up just fine. So when you run this locally on your host it spins up and serves content from `http://localhost:9200` without issues. It only seems to be when its running from inside a Docker container where something seems to be choking it or causing it to act wonky. How does my `Dockerfile` look? In other words, this isn't a problem with my application (as a standalone app), this seems to be a problem with my app running in Docker. – smeeb Aug 26 '16 at 12:47
  • I'd have a look, but you require something outside of your container to be run before `docker build` will work, and I don't feel comfortable running your code outside of a sandbox. – BMitch Aug 26 '16 at 13:08
  • Lots of people post non-Dockerized SSCCE's on StackOverflow that are trusted by the community, but fair enough! For other StackOverflowers, this is just a Spring web app that serves a dummy HTML message from the base url. Only requires Java 8. – smeeb Aug 26 '16 at 13:12
  • 1
    @smeeb Try using lsof to confirm it really is the docker process listening on the port? http://stackoverflow.com/questions/4421633/who-is-listening-on-a-given-tcp-port-on-mac-os-x – Paolo Aug 26 '16 at 13:23
  • Thanks @Paolo (+1) - please see my update. In the output, I only changed the username and a hex number that seemed like it might be sensitive info to post on SO. But yes, it does look like something docker-related is listening on 9200. – smeeb Aug 26 '16 at 13:28
  • 1
    which version of docker are you using ? the newest "native" version or the one that relies on virtualbox ? can you please tell me the result of : 1) `docker-machine ls` 2) `docker-machine ip default` – Andrea Di Lisio Aug 28 '16 at 18:53
  • Thanks @AndreaDiLisio (+1) - I'm on Docker for Mac, so I guess "native". When I do `docker --version` it tells me 1.12.0. (1) Output of `docker-machine` was very large but started with `Usage: docker-machine [OPTIONS] COMMAND [arg...]`. And (2) the output of `docker-machine ip default' is `Host does not exist: "default"`. – smeeb Aug 28 '16 at 19:46
  • Can we see more of the dockerfile? Did you [expose](https://docs.docker.com/engine/reference/builder/#/expose) 9200 in the dockerfile? – Mano Marks Aug 29 '16 at 17:38
  • 1
    Two questions: 1. What do you mean by "I get nothing"? 2. I assume the `curl` was from the host - what do you get for the same `curl` from within the container? – creativeChips Aug 29 '16 at 18:05
  • Thanks @ManoMarks the complete source code is on GitHub at the link provided in the original question, including the Dockerfile. – smeeb Aug 29 '16 at 18:08
  • Thanks @creativeChips (+1) - To address your questions, (1) sorry, to clarify, by "*I get nothing*", means I get a blank web page with no content sent back from the server at all. (2) If I stop my Docker container and re-run it with the commands displayed above, and then SSH into the container, and run `curl http://localhost:9200`, I get `curl: (7) Failed to connect to localhost port 9200: Connection refused`. Are we on to something, maybe?!? Thanks again! – smeeb Aug 29 '16 at 18:14
  • 1
    Cool, can you make it work within the container? is your application running? – creativeChips Aug 29 '16 at 18:35
  • 1
    btw, do you get the same results for 127.0.0.1? – creativeChips Aug 29 '16 at 19:01
  • Thanks @creativeChips (+1 for last several comments). I think you're on to something. When I SSHed into my container, I saw that a `bootup.jar` was in fact already running. Please see my updates w/ screenshots...does this mean anything to you?!? So it looks like my app *is* sending back the correct HTML (see 2nd screenshot) but that the browser doesn't like it for some reason...and to answer your question, **no**, if I point the browser to `http://127.0.0.1:9200` then it literally receives an empty response and nothing is available if I *View Page Source*. – smeeb Aug 29 '16 at 19:25
  • 1
    What's happening in the `Network` tab of the dev tools? Especially the status code and the response (the inner `Response` tab) – Nitzan Tomer Aug 29 '16 at 19:53
  • 1
    what about http://0.0.0.0:9200 – Marc Young Aug 29 '16 at 20:07
  • Thanks @NitzanTomer (+1) - please see my latest update, I added a screenshot of the `Network` tab in Chrome Developer Tools. There doesn't appear to be a `Response` tab anywhere, not sure what that implies. – smeeb Aug 29 '16 at 20:12
  • Thanks @MarcYoung (+1) - when I go to `http://0.0.0.0:9200` I get the same exact results as if I go to `http://127.0.0.1:9200` (see above). – smeeb Aug 29 '16 at 20:13
  • Make sure you remove any caching. Also, has `curl` behaviour changed? please share. Couldn't hurt to try and see `wget` as well ... – creativeChips Aug 29 '16 at 20:13
  • Thanks @creativeChips (+1) - `curl http://localhost:9200 curl: (52) Empty reply from server` and I don't expect `wget` to be any different. Also I cleared all caches and even went to `http://localhost:9200` from browsers I've never even opened before. No difference. – smeeb Aug 29 '16 at 20:15
  • @MarcYoung - when you recommend visiting vmip:9200, whats your actual recommended command? Let's say `` is the IP of my docker container. are you suggesting i try to telnet into it or something?!? – smeeb Aug 29 '16 at 20:16
  • Have you checked if maybe your server does indeed receive the requests, but maybe it just doesn't like them and misbehaves? Maybe you have logs or it prints to the console? – Nitzan Tomer Aug 29 '16 at 20:21
  • Thanks @NitzanTomer but check out the 2nd screenshot above. *It is* returning the correct response (that HTML should be getting rendered as a simple web page) according to the page source. – smeeb Aug 29 '16 at 20:37

5 Answers5

2

The Dockerfile doesn't expose 9200 to the daemon. Add

EXPOSE 9200

to the Dockerfile before ENTRYPOINT

Mano Marks
  • 8,591
  • 3
  • 25
  • 28
  • Thanks @Mano Marks (+1) - please see my pushed Dockerfile...your suggestion *did not* work. Any ideas? Also, please see my updates w/ screen shots...very weird/unexpected behavior. – smeeb Aug 29 '16 at 19:33
2

Assuming you are using Docker Toolbox and not the beta ...

There is a 3 step process for exposing a port properly:

  • use EXPOSE 8080 where 8080 is just a port number in the Dockerfile
  • use -p 8080:8080 in your docker run command
  • Make sure that you setup port forwarding in Oracle Virtual Box so that the boot2docker machine is able to receive requests from port 8080.

This applies to both Windows and OSX where Docker Toolbox is being used. Linux doesn't use Oracle VirtualBox to run docker so those hosts do not need to do the third point

Shiraaz.M
  • 2,758
  • 20
  • 38
  • Thanks @Shiraaz (+1) but I am using Docker for Mac [which is not Docker Toolbox](https://docs.docker.com/toolbox/overview/). I mentioned this several times, including in the bounty description. I am not interested in using Docker Toolbox because it was notoriously buggy/wonky with Macs, which is why the Docker folks made Docker for Mac in the first place. – smeeb Aug 29 '16 at 20:03
  • This is likely the correct answer. He can troubleshoot by visiting http://vmip:9200/ where vmip is the IP address of the virtualbox VM – Marc Young Aug 29 '16 at 20:03
  • @MarcYoung Docker for Mac does not use VirtualBox for anything. It uses native xyhve hypervisor. – smeeb Aug 29 '16 at 20:04
  • @smeeb remember that docker for mac is still running a VM under the hood since it can't host docker truly natively (the vm is what loads the linux kernel). I misspoke when I said virtualbox. Make sure the networking is all working as expected to route to the underlying VM – Marc Young Aug 29 '16 at 20:06
  • @smeeb - curious if you got the port forwarding to work with docker for mac? – Sood May 12 '17 at 03:22
  • Thanks @Shiraaz.M I'm using docker toolbox on mac (installed via brew). Setting up port forwarding in Virtual Box did the trick. You saved my day : ) – aietcn May 21 '18 at 15:53
2

I ran your repo as-is on Docker 1.12 on OSX.

If you look carefully at your container startup:

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)

Although application.yml and Dockerfile both contain 9200, the application is starting on 8080

Marc Young
  • 3,541
  • 3
  • 15
  • 21
  • Thanks @Marc Young - I think Shiraaz is onto something. I have always been under the impression that Gradle/Spring SDK were bundling my `application.yml` into the final uberjar, and it appears that this *might* not be the case (I will confirm tonight). If that is the case, then Spring would fall back to the default port of 8080, which would explain what you're seeing. So the prevalent theory here (purported by both Shiraaz and yourself) is that Spring can't find an external config file and is defaulting to the Java EE default of 8080. Stay tuned... (and thank you so much here!!!). – smeeb Aug 29 '16 at 21:01
1

Going to add another answer here because I saw something related to the Github Repo that you posted:

So the repo is a spring boot repo with an application.yml file.

Your Dockerfile looks like this:

FROM openjdk:8

RUN mkdir /opt/bootup

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

Which is adding the built jar to the image. If my understanding is correct, the jar does not include application.yml because:

  • It is not part of the build (gradle would package the src/main only). It is sitting on the project root folder
  • It is not explicitly added to Docker

So therefore one can assume that your app is actually running on 8080 (the default) at the moment?

A couple of options that one could try:

  • Try exposing 8080 instead of 9200 (or expose both) and see if that makes a difference?
  • The entrypoint command can append the port --server.port=9200
  • The application.yml file should be added to the image (you might need to add an argument to reference it properly) [ADD application.yml /opt/bootup, after first ADD command]
  • Include the application.yml file in src/main/resources so that spring boot can pick it up automatically.

References

Spring Boot reference documentation on the order of loading for external configuration

Shiraaz.M
  • 2,758
  • 20
  • 38
  • This is an interesting theory @Shiraaz (+1). I will try it out later tonight. I guess I was operating under the assumption that Gradle was bundling my `application.yml` into the guts of the final uberjar. And it may very well be, but you're correct, I'm not 100% certain at this point. I'll give it a try and will report back! However, the fact that the page source for `http://localhost:9200` shows the correct HTML leaves me feeling uncertain about this suggestion...stay tuned! – smeeb Aug 29 '16 at 20:41
  • That being said, let's say (for now) that it turns out that Spring expects the YAML file to be located in the same directory as the JAR file (so in my case, `/opt/bootup`). Can you tell me what change(s) I'd need to make to my Dockerfile so that I copy/add `application.yml` to `/opt/bootup`? – smeeb Aug 29 '16 at 20:47
  • 1
    @smeeb It's very similar to how you added the jar file. It should be something like `ADD application.yml /opt/bootup` ideally after the first `ADD` command. The first argument after `ADD` will either be `application.yml` or `./application.yml` – Shiraaz.M Aug 29 '16 at 21:13
  • 1
    Thanks @Shiraaz (+1) - so I've done some digging and it looks like I need to tell Spring the location of the external config file. So if I used your example and did `ADD application.yml /op/bootup` then I need to run the app using `java -Dspring.config= -jar build/libs/bootup.jar`. So would my `ENTRYPOINT` command be `ENTRYPOINT java -Dspring.config=. -jar bootup.jar` or `ENTRYPOINT java -Dspring.config=/opt/bootup -jar bootup.jar` or something else? – smeeb Aug 29 '16 at 21:18
  • 1
    @smeeb I would start with putting the file in the same directory. It should be able to pick up the application.yml file automatically without the need to modify the entrypoint. If it does not pick up, fall back to adding `--spring.config.location=file:application.yml` at the end of the entrypoint – Shiraaz.M Aug 29 '16 at 21:24
  • Hi @Shiraaz, please see my updated/pushed Dockerfile. The 2nd ADD command (that is, `ADD application.yml /opt/bootup`) is not working. I can tell because after I run the container and then SSH into it (via `docker exec -it /bin/bash`) I only see `bootup.jar` in the `/opt/bootup` directory. Any ideas where I'm going awry here? – smeeb Aug 30 '16 at 01:34
  • Also, I had previously tried `ADD ./application.yml /opt/bootup` (prepending `./` to the front of the filename) but that didn't work either...meh. – smeeb Aug 30 '16 at 01:42
  • @smeeb you added `ADD application.yml /op/bootup` ... it should be 'opt' not 'op' :) – Shiraaz.M Aug 30 '16 at 06:29
  • Thanks @Shiraaz - that did it! Thank you so much for taking the time to read all the details and troubleshoot this for me! – smeeb Aug 30 '16 at 08:07
0

Good News! (for MacOSx 10.15.7)

I found the same issue as you, and I was able to solve it by directly opening VirutalBox connection

Go here first:

VirtualBox hosts Docker Images Network Controller setting changed to bridged then logged into the virtual machine within VirtualBox

And found the actual machine's adapter labeled:

eth0

after I noted the setting it was originally NAT so I changed to bridged and then

enter image description here

I was able to use its address vs. localhost.

After I used the public address I used:

curl -i [bridged_ip_address_here]:9200

it then worked flawlessly.

However I also noticed some firewalls and accessibility options that needed permission as well.

Accessibility fix

I pray this helps you.