Can't connect to Spring Boot app running on Docker locally - spring-boot

I stuck with the problem that can't open my REST Spring Boot app on localhost:8091 in browser.
Here is my docker-compose.xml (everything is deployed locally on Docker Desktop):
version: '3.3'
services:
postgres:
build:
context: services/postgres
dockerfile: Dockerfile.development
command: postgres
ports:
- "5432:5432"
environment:
- POSTGRESS_USER=postgres
- POSTGRESS_DB=postgres
- POSTGRESS_PASSWORD=qqq
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
- "db-data:/var/lib/postgresql/data"
app:
build:
context: services/app
dockerfile: Dockerfile.development
command: java -jar ./app.jar
environment:
- PORT=8091
network_mode: host
image: 'my-java-app'
ports:
- 8091:8091
depends_on:
- postgres
angular:
build:
context: services/angularfrontend
dockerfile: Dockerfile.development
image: 'my-angular-app'
ports:
- 80:80
volumes:
db-data:
Spring Boot App starts normally on 8091 and connects to the database, but then I can't make calls to it's API from my local machine ("connection refused").
Angular app opens normally (on localhost:80), but can't make calls to localhost:8091 Spring Boot app.

The call from angular service container to localhost:8091 fails, right?
Try to override in your angular frontend container the call to the backend
use app:8091 (this is how the backend service is called) instead of localhost:8091.
In the 'angular' container localhost doesn't translate to 'app' container.
You can't get from a container into a different container using localhost.
localhost inside a container will translate to the ip of that container.
Try to make in your angular application the call to the backend configurable, after that override that configuration in docker-compose using environment.
Also do that for the springboot app application.
I don't see in the environment that you override the call to the postgress.
Expose that configuration in application.properties and override in docker-compose, after that remove network_mode: host

If you really want to use network_mode: host, you don't need to specify <source>:<dest> because the app is listening on 8091 directly on the host network:
...
app:
build:
context: services/app
dockerfile: Dockerfile.development
command: java -jar ./app.jar
environment:
- PORT=8091
network_mode: host
image: 'my-java-app'
depends_on:
- postgres
...
If you want to run the java app like the other containers, simply remove this line from the compose file and the network mode will default to bridge:
network_mode: host

Related

Communication between 2 dockerized spring boot applications using custom docker network

I have a Hello service and World service. The Hello service has a rest point /hello/hw which internally calls rest end point in World service to return string "Hello World".
The application works fine as expected if the system is not dockerized.
However the issue is when I want to dockerize the system.
I use docker-compose to containerize both services. I have defined a custom network in the docker compose file (named as custom_net).
The rest end point /hello/hw is reachable but Hello service is not able to call the rest end point in the World service.
The application.properties in Hello service has the url of World service as http://localhost:8082/world
I get UnknownHostException when I set the url as http://custom_net:8082/world or to http://custom_net:8092/world
I get Connection refused exception when I don't change the url in application.properties.
The log message states 'failed: Connection refused: localhost/127.0.0.1:8082;'
I am not sure what configurations are needed to bridge the 2 services
The command docker network list shows the name of custom network as 'tempfolder_custom_net'.
The command docker inspect shows that both hello and world service are registered to this network
version: '3'
services:
hello_service:
image: 'openjdk:8-jdk-alpine'
restart: always
container_name: hello_service
volumes:
- ./deploy:/root
networks:
- custom_net
depends_on:
- world_service
command: sh -c "java -jar -Dspring.config.location=file:///root/hello/application.yml /root/hello/hello-0.0.1-SNAPSHOT.jar "
ports:
- 8091:8081
world_service:
image: 'openjdk:8-jdk-alpine'
restart: always
container_name: world_service
volumes:
- ./deploy:/root
command: sh -c "java -jar -Dspring.config.location=file:///root/world/application.yml /root/world/world-0.0.1-SNAPSHOT.jar "
ports:
- 8092:8082
networks:
- custom_net
networks:
custom_net:
driver: bridge
Application.yml of Hello service ...
server:
port: 8081
services:
world:
url: http://localhost:8082/world
You should build new image base on openjdk:8-jdk-alpine image, and add ENTRYPOINT
You should change this:
server:
port: 8081
services:
world:
url: http://localhost:8082/world
To :
server:
port: 8081
services:
world:
url: http://world_service:8092/world
Make sure, both containers are running.
#Thanh Nuguyen Van thanks for providing useful hints in troubleshooting.
The issue was with the host name world_service. Looks like underscore is not a valid URL. Below is my updated docker compose file. I didn't even have to use custom docker network. I updated the url in application.yml to http://worldservice:8082/world.
Also note that the port is 8082 not 8092. We have to mention the application's port and not the docker exposed port.
version: '3'
services:
helloservice:
image: 'openjdk:8-jdk-alpine'
restart: always
container_name: helloservice
volumes:
- ./deploy:/root
#networks:
#- custom_net
depends_on:
- worldservice
command: sh -c "java -Xdebug -jar -Dspring.config.location=file:///root/hello/application.yml /root/hello/hello-0.0.1-SNAPSHOT.jar "
ports:
- 8091:8081
worldservice:
image: 'openjdk:8-jdk-alpine'
restart: always
container_name: worldservice
volumes:
- ./deploy:/root
command: sh -c "java -Xdebug -jar -Dspring.config.location=file:///root/world/application.yml /root/world/world-0.0.1-SNAPSHOT.jar "
ports:
- 8092:8082
#networks:
#- custom_net
#networks:
#custom_net:
#driver: bridge

Not able to connect ActiveMQ docker container from another docker container

version: '3.9'
services:
activemq:
image: rmohr/activemq:5.15.9-alpine
restart: always
ports:
- 61616:61616
- 8161:8161
- 5672:5672
container_name: activemq
app-service:
image: app-service:v1
restart: always
ports:
- 8080:8080
container_name: app-service
links:
- activemq
depends_on:
- activemq
In my app service I've configured the ActiveMQ broker URL using Spring Boot spring.activemq.broker-url=tcp://activemq:61616 and also username and password.
When I am trying to run docker-compose up the app service showing below error
DefaultMessageListenerContainer : Could not refresh JMS Connection for
destination 'queueName' - retrying using FixedBackOff{interval=5000,
currentAttempts=5, maxAttempts=unlimited}. Cause: Java.lang.NullPointerException.
I can access the ActiveMQ web console on browser (e.g. using http://localhost:8161).
Without docker container the same code is working fine in localhost.
I also had this exact problem and what helped me is adding spring.activemq.broker-url=tcp://activemq:61616 to docker-compose for app environment tag. For me it's like that:
app:
build:
context: .
container_name: app
ports:
- 8080:8080
environment:
- spring.activemq.broker-url=tcp://activemq:61616
depends_on:
- activemq
I think containerized spring app doesn't see broker-url from app properties for whatever reason
Yes, The big reason is your app run before activemq service.
You can try docker-compose up and see the console log in terminal.
Fixed:
It is not a good idea yet,but you can go to docker app and click on restart with you app container's name and then everything will work.

How to access an external server folder from a spring boot docker container?

I'm using docker-compose to run spring boot application inside a docker container. A spring boot uses embedded tomcat to run.
There is an external folder on a server (/opt/cp/uploads) with images which I would like to access from a spring boot docker container.
Within the docker-compose.yaml file there are following containers defined:
nginx
mysql
springboot-app
nginx and CloudFlare redirect the domain to the spring app on the port 8080.
I'd like to access images like this:
https://domainname.com/uploads/imageName.png
Using tomcat this was done in server.xml:
<Context docBase="/opt/uploads" path="/uploads"/>
but this option is obviously not available via application.properties with embedded tomcat.
How does one come about this?
My docker-compose.yaml:
version: '3.8'
services:
nginx:
container_name: some-nginx
hostname: nginx
image: nginx:1.19.2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
restart: unless-stopped
network_mode: host
mysqldb:
image: mysql:8.0.20
hostname: mysqldb
container_name: cp-mysqldb
environment:
- MYSQL_ROOT_PASSWORD=pass1234
- MYSQL_DATABASE=db_name
- MYSQL_USER=root
- MYSQL_PASSWORD=pass1234
ports:
- "3306:3306"
volumes:
- cp-mysqldb-data:/opt/mysql
restart: unless-stopped
springboot-app:
image: openjdk:8
hostname: cp
container_name: cp-springboot
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysqldb:3306/db_name?autoReconnect=true&useSSL=false&useUnicode=yes&characterEncoding=UTF-8&characterSetResults=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
ports:
- "8080:8080"
depends_on:
- mysqldb
volumes:
- cp-springboot-data:/opt/cp
- ./target/cp-springboot-0.0.1-SNAPSHOT.war:/ROOT.war
command: ["java", "-jar",
"-Dspring.profiles.active=prod",
"ROOT.war"]
restart: always
volumes:
cp-springboot-data: {
}
cp-mysqldb-data: {
}
Maybe better use Tomcat for docker image and not OpenJDK.
That way you can setup your Tomcat configuration for the external path
Take a look here: https://medium.com/#iamvickyav/deploying-spring-boot-war-in-tomcat-based-docker-2b689b206496
Actually, this option IS available via application.properties with embedded tomcat:
spring.mvc.static-path-pattern=/uploads/**
spring.resources.static-locations=file:/opt/uploads

Make a request to a spring api running in a docker container from windows host

So, I searched around for an answer on this matter but either people don't address the issue or they say there's no problem doing this on their computer (mac or linux). It seems like this might be a windows problem.
I have a spring api running on a docker container (linux container). I use docker desktop on windows and I'm trying to make a request (in insomnia/postman/wtv) to that api.
If I run the api locally making the following request works perfectly:
http://localhost:8080/api/task/
This will list multiples task elements.
I've containerized this application like so:
Dockerfile
FROM openjdk:11.0.7
COPY ./target/spring-api-0.0.1-SNAPSHOT.jar /usr/app/
WORKDIR /usr/app
RUN sh -c 'touch spring-api-0.0.1-SNAPSHOT.jar'
ENTRYPOINT ["java", "-jar", "spring-api-0.0.1-SNAPSHOT.jar"]
docker-compose.yml
version: '3.8'
services:
api:
build: .
depends_on:
- mysql
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/test?createDatabaseIfNotExist=true
ports:
- "8080:80"
mysql:
image: mysql
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_USER=root
- MYSQL_PASSWORD=root
- MYSQL_DATABASE=test
If I do docker-compose up this works without issue:
The problem is, if I try to call the same endpoint as before from localhost I don't get any response.
Insomnia returns an error saying: Error: Server returned nothing (no headers, no data)
I've also tried connecting to the container's ip (got it from docker inspect) but no luck.
Ports are exposed in docker-compose.yml. What am I missing?
Thanks in advance.
Port mapping is incorrect.Spring boot application started at 8080 (from the image I see) inside container and it should be mapped to 8080 inside the container.
It should be like below:
ports:
- "8080:8080"

How to get docker containers from different projects speaking to each other

I have developed and dockerised two applications web (react) and api (laravel, mysql), they have separate codebases and separate directories.
Could somebody please help explain how I can get my web application talking to my api whilst using docker at the same time
Update: Ultimately what I want to achieve is to have both my frontend and backend running on port 80 without having to have two web servers running as containers so that my docker development environment will work the same as using valet or mamp etc.
For development you could make use of docker-compose.
Key benefits:
Configure your app's services in YAML.
Single command to create/start the services defined on this configuration.
Compose creates a default network for your app. Each container joins this default network and they can see each other.
I use the following structure for a project.
projectFolder
|_backend (laravel app)
|_frontend (react app)
|_docker-compose.yml
|_backend.dockerfile
|_frontend.dockerfile
My docker-compose.yml
version: "3.3"
services:
frontend:
build:
context: ./
dockerfile: frontend.dockerfile
args:
- NODE_ENV=development
ports:
- "3000:3000"
volumes:
- ./frontend:/opt/app
- ./frontend/package.json:/opt/package.json
environment:
- NODE_ENV=development
backend:
build:
context: ./
dockerfile: backend.dockerfile
working_dir: /var/www/html/actas
volumes:
- ./backend:/var/www/html/actas
environment:
- "DB_PORT=3306"
- "DB_HOST=mysql"
ports:
- "8000:8000"
mysql:
image: mysql:5.6
ports:
- "3306:3306"
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=homestead"
- "MYSQL_USER=homestead"
- "MYSQL_PASSWORD=secret"
- "MYSQL_ROOT_PASSWORD=secret"
volumes:
dbdata:
Each part of the application is defined by a service in the docker-compose file. E.g.
frontend
backend
mysql
Docker-compose will create a default network and add each container to it. The hostname for
each container will be the service name defined in the yml file.
For example, the backend container access the mysql server with the name mysql. You can
see this on the service definition itself:
backend:
...
environment:
- "DB_PORT=3306"
- "DB_HOST=mysql" <-- The hostname for the mysql container is the name of the service
With this, in the react app, I can setup the proxy configuration in package.json as follows
"proxy": "http://backend:8000",
One last thing, as mentioned by David Maze in the comments. Add the backend to your
hosts file, so the browser could resolve that name.
E.g /etc/hosts on ubuntu
127.0.1.1 backend

Resources