How to expose ${PORT} spring boot application random port in docker? - spring-boot

How to expose ${PORT} spring boot application in docker?
Spring boot application starts on a random port and I have to expose the same in docker.
Is it possible?

As mentioned in the comment, better to have a static port on the application side otherwise mapping exact port will be hard.
How to configure port for a Spring Boot application
Another option is to use the host network, so you will be able to access the application using container port, but this option is available for Linux only.
If you use the host network mode for a container, that container’s network stack is not isolated from the Docker host (the container shares the host’s networking namespace), and the container does not get its own IP-address allocated. For instance, if you run a container which binds to port 80 and you use host networking, the container’s application is available on port 80 on the host’s IP address.
docker-host-network
docker run -it --net host --rm my_app
suppose your app is running on some random port 8087, then you will able to access the application using http://localhost:8087 because of --net host

Related

Connect to a MariaDB Docker Container in a own Docker network remotly

Hi what I am actually trying is to connect remotly from a MySQL Client in Windows Subsystem for Linux mysql -h 172.18.0.2 -P 3306 -u root -p and before that I started the Docker Container as follows: docker container run --name testdb --network testnetwork -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mysqlRootPassword -e MYSQL_DATABASE=localtestdb -d mariadb/server.
The purpose why I put the container in a own network, is because I also have a dockerized Spring Boot Application (GraphQL-Server) which shall communicated with this db. But always when I try to connect from my built-in mysql client, in my Windows Subsystem for Linux, with the above shown command. I got the error message: ERROR 2002 (HY000): Can't connect to MySQL server on '172.18.0.2' (115).
What I already tried, to solve the problem on my own is, look up whether the configuration file line (bind-address) is commented out. But it wont work. Interestingly it already worked to set up a docker container with MariaDB and connect from the outside, but now when I try exactly the same, only with the difference that I now put the container in a own existing network, it wont work.
Hopefully there some one out there which is able to help me with this annonying problem.
Thanks!
So far,
Daniel
//edit:
Now I tried the solution advice from a guy from this topic: How to configure containers in one network to connect to each other (server -> mysql)?. Futhermore I linked my Spring Boot (server) application with the "--link databaseContainerName" parameter to the MariaDB container.
Now I am able to start both containers without any error, but I am still not able to connect remotly to the MariaDB container. Which is now running in a virtual docker network with his own subnet.
I explored this recently - this is by design - container isolation. Usually only main (service httpd) host is accessible externally, hiding internal connections (hosts it communicates to deliver response).
Container created in own network is not accessible from external adresses, even from containers in the same bridge but other network (172.19.0.0/16).
Your container should be accessible on docker host address (127.0.0.1 if run locally) and mapped ("-p 3306:3306") port - 3306. But of course it won't work if many running db containers have the same mapping to the same host port.
Isolation is done using firewall - iptables. You can list rules (iptables -L) to see that - from docker host level.
You can modify firewall to allow external access to internal networks. I used this rule:
iptables -A DOCKER -d 172.16.0.0/12 -j ACCEPT
After that your MySQL containerized engine should be accessible using internal address 172.18.0.2 and source (not mapped) port 3306.
Warnings
it disables all isolation, dont't use it on production;
you have to run this after every docker start - rules created/modified by docker on the fly
not every docker container will respond on ping, check it from docker host (linux subsystem in this case) first, from windows cmd later
I used this option (in docker.service) to make rule permanent:
ExecStartPost=/bin/sh -c '/etc/iptables/accept172_16.sh'
For docker on external(shared in lan) host you should use route add (or hosts file on your machine or router) to forward 172.x.x.x addresses into lan docker host.
Hint: use portainer project (with restart policy - always) to manage docker containers. It's easier to see config errors, too.

How to access a port on the host machine when running docker container on MacOS with --network=host?

I have set up a couple of containers that interact with each other. The main application container runs on --network = host because it queries several mySQL containers running on different ports exposed on the host network.
I am trying to hit the application on the host but get an error:
curl: (7) Failed to connect to 0.0.0.0 port 36081: Connection refused
I am working on Docker installed on MacOS.
I have read several questions that indicate that docker on MacOS runs on a VM. But what is the workaround to access the application from the host? Any way to get the IP of the said VM?
You cannot use --network=host on Mac to connect via host ports but binding to host port using -p options works.
https://docs.docker.com/docker-for-mac/networking/#/there-is-no-docker0-bridge-on-osx
I WANT TO CONNECT TO A CONTAINER FROM THE MAC
Port forwarding works
for localhost; --publish, -p, or -P all work. Ports exposed from Linux
are forwarded to the host.
Our current recommendation is to publish a port, or to connect from
another container. This is what you need to do even on Linux if the
container is on an overlay network, not a bridge network, as these are
not routed.
For your use case,
You need to create a docker network and attach both the DB and application containers to this network. Then the containers will be able to talk to each other by their name. You can also publish the application container port so that you can access it from your host.
https://docs.docker.com/network/bridge/
Instead of creating the network, attaching the containers to the network etc manually, you can use docker-compose.
https://docs.docker.com/compose/

Cannot connect to Container-optimized-os (running a spring-boot application using docker) using external ip

I have created a Google compute instance with Container-optimized-OS image.
I have configured the firewall to allow http and https.
I am using the docker image with spring boot application which connects to cloudsql. When I use run command on compute engine instance ssh, i.e. (docker run --rm name), the spring boot app is started successfully.
When I try to access the webservices through compute engine instance external ip, it is not working.
I went through a different question, and found that I should try using the sudo wget http://localhost command on the instance cli first and if it is good then everything should be good. But I am getting a connection refused message on 127.0.0.1:80.
I also tried the command to open port from Container optimized OS, I.E.
sudo iptables -w -A INPUT -p tcp --dport 80 -j ACCEPT , nothing is working.
The default port for Spring Boot is 8080 and not 80.
Run this command inside the instance container to see what ports are in LISTENING state:
sudo netstat -tulpn | grep LISTEN
You can redirect port 80 to port 8080 with this command:
sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
Note: This iptables command only redirects port 80 to 8080 on network interfaces. This has no effect for localhost or 127.0.0.1.
For Google Compute Engine instances you do not need to enable ports using iptables. This is done via Google VPC firewall rules. You can use both but make sure you understand exactly what you are configuring and the side effects.
Note: Your Spring Boot application needs to listen on 0.0.0.0 and not 127.0.0.1 nor localhost. The last two are internal only addresses. 0.0.0.0 means listen on all network interfaces.
Note: Do not use sudo in front of wget. This is not necessary.
First, confirm what port your springboot application uses - if it's 8080 or 80. This depends on what you have configured inside application.properties file. This port is referred to as ContainerPort in below steps.
Execute docker run <image-name>:<tag>. This will run the image and show container logs on the console. If there is something wrong with your spring-boot app, the logs will show that and the container will shutdown. Press Ctrl+C to stop the container and return to shell.
If there is no error in step 1 run docker run -d -p<HostPort>:<ContainerPort> <image-name>:<tag>. Here HostPort is any free port in your GCP host VM and ContainerPort is the port used by your spring boot application within the container. Option d starts your container in detached mode.
Run docker ps and make sure that the container started in step 2 is running. It may not run if there is an error - for example if the HostPort you specified is already in use.
If step 3 shows that the container is running, execute curl http://localhost:<HostPort>/<End-Point-Path>. Here End-Point-Path is a valid path to a working endpoint within the container. If the endpoint is correct you should see expected result from the spring-boot app in the console.
Navigate to Google Cloud Console -> VPC network -> Firewall rules and add a firewall rule to open HostPort on your GCP VM.
Access your endpoint via the VM's external IP with URL - http://<VM-External-IP>:<HostPort>/<End-Point-Path>
Unless there is an application issue with your spring-boot app these steps should get you going.
I was able to build the correct solution by your help (John Hanley and Cyac).
I am combining both solutions in order to help the next person facing this.
As told by John, by default Spring boot uses port 8080, not 80 and as specified by Cyac you need to specify the port as 80 explicitly in application.properties file using
server.port=80
Make sure you expose the port 80 in docker image
On GCP Contaier optimized OS make sure you have allowed traffic for HTTP and HTTPs
Run command:
sudo iptables -w -A INPUT -p tcp --dport 80 -j ACCEPT
Run docker using:
docker run -p 80:80 SPRING_IMAGE.
Where SPRING_IMAGE is the name of the docker image with spring boot build.
Test by using curl http://localhost/ENDPOINT_NAME , e.g. http://localhost/shops/all

Docker Mac alternative to --net=host

According to the docker documentation here
https://docs.docker.com/network/host/
The host networking driver only works on Linux hosts, and is not supported on Docker for Mac, Docker for Windows, or Docker EE for Windows Server.
On Mac what alternatives do people use?
My scenario
I want to run a docker container that'll host a micro-service
The micro-service has dependencies upon databases that I'm also running via docker
I thought I'd be able to use --net=host on Mac when running the micro-service
But the micro-service port is not exposed
I can override the db addresses (they default to localhost) on the microservice.
But that involves robust --env usage
What's the simplest / most elegant solution?
The most simple and most elegant solution is to use docker named bridge network.
You can create a custom bridge network (default is bridge) like this:
docker network create my-network
Every container deployed inside this network can communicate with each other by using the container name.
$ docker run --network=my-network --name my-app ...
$ docker run --network=my-network --name my-database...
In the example above you can connect to your database from inside your application by using my-database:port. If the container port is exposed in the Dockerfile you don't need to map it on your host and you can keep all your communication internal inside your custom docker bridge network.
In most cases the application its port is mapped (example: -p 80:80) so localhost:80 is mapped on container:80 and you can access the app from on your localhost. If the app needs to communicate with a db you don't need to expose the port of the db and you don't have to map it on localhost as explained already above.
Just keep the communication between app and db internal in your custom bridge network.

Map port of Elasticsearch in Docker

I want to start an Elasticsearch container in Docker. By default I see nearly everywhere something like:
docker run -d -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
Now is my question: Why are we mapping the port on our host network? I understand port mapping but I don't see the big advantage of it.
In my opinion I would always do something like this:
$ docker network create logging
20aa4c7bf2d8289d8cbd485c3e384f9371eed87204625998687c61e4bad27f14
$ docker run -d --name es --net logging docker.elastic.co/elasticsearch/elasticsearch:5.5.1
And connect to the ES by using it's name (es in this case) and deploying containers in the same network. I would think my ES is more secure in its private docker network.
I see there is an advantage for port mapping when your containers which need to connect to elasticsearch aren't in the same network. But are there other advantages or why is this always shown with port mapping?
So host access is more about accessibility. If you are running docker on local machine and you want to access the app only on that machine, then host mapping is not need.
Now if you need to access this app on a external computer other than your docker host then you need to do that port mapping.
docker run -d -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
This maps the host port 9200 (left side) to 9200 inside the docker (right side). The listening interface is 0.0.0.0 which means all interfaces. And hence it is accessible to to anyone how has access to this machine.
If you want to make it more secure then you do it like below
docker run -d -p 127.0.0.1:9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
This would listen on local host only. So only you can access it on the machine. But if you need to access it from some place else then you would use a SSH tunnel
ssh -L 9200:127.0.0.1:9200 <user>#<HOSTIP>
And on that machine you can access it on 127.0.0.1:9200
Next level of security is added when you use a firewall like ufw, firewalld etc.
What you did with network command
docker network create logging
Basically creates new network and isolates other docker containers from accessing it on the host. But as long as external accessibility is concerned, you still need to map it to the host port
Hope this answers your question

Resources