I have a Spring Boot Kotlin Gradle app running in Docker, but after I added a new RestController, I only see a white label error page when I go to the URL (http://localhost:8080/users). The code I write doesn’t seem to be reflected in the Docker container, how can I fix this?
Controller
@RestController
@RequestMapping("/users")
class UserController {
@GetMapping
fun users(): String {
return "bzbz 🐝";
}
}
Dockerfile
# Use the official Gradle image with JDK 17 as the base image
FROM gradle:7.4.1-jdk17 AS builder
WORKDIR /app
COPY . .
RUN ./gradlew clean build -x test
EXPOSE 8080
CMD ["java", "-jar", "./build/libs/twitch-bot-0.0.1-SNAPSHOT.jar"]
compose.yml
version: '3'
services:
mysql:
image: 'mysql:latest'
environment:
- 'MYSQL_DATABASE=twitch-bot'
- 'MYSQL_PASSWORD=secret'
- 'MYSQL_ROOT_PASSWORD=secret'
- 'MYSQL_HOST=localhost'
ports:
- '3306:3306'
spring-boot-kotlin:
depends_on:
- mysql
build:
context: .
dockerfile: Dockerfile
ports:
- '8080:8080'
I see a problem with your docker-compose configuration. you are not waiting until your mysql database becomes ready to be connected by your spring-boot service. For this to work all the time you need to wait until mysql is ready and you can do that like this:
services:
mysql:
image: 'mysql:latest'
environment:
- 'MYSQL_DATABASE=twitch-bot'
- 'MYSQL_PASSWORD=secret'
- 'MYSQL_ROOT_PASSWORD=secret'
- 'MYSQL_HOST=localhost'
ports:
- '3306:3306'
healthcheck:
test: ["CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/3306'"]
interval: 5s
timeout: 240s
retries: 60
spring-boot-kotlin:
depends_on:
mysql:
condition: service_healthy
build:
context: .
dockerfile: Dockerfile
ports:
- '8080:8080'
The point is that spring-boot-kotlin
now waits until mysql
is healthy. And docker-compose now knows this when port 3306
is ready and available. There are more evolved ways to check if the mysql is ready to accept connections, for example, checking if a user can log in or if certain tables are ready, etc, but those are advanced configurations. But for a start and to test, I think this could explain the problem you are facing.
To debug these situations, you can always use docker-compose logs
or docker logs <CONTAINER_ID>
. You can also get the CONTAINER_ID by listing your running containers with docker ps
.
In our healthcheck implementations we check to see if the port 3306
is available every 5 seconds and we will try 60 times until the 240s timeout occurs. If mysql
fails to start before the 240s, everything will fail and docker-compose will not be able to start letting immediately know when something isn’t right.
Are you rebuilding the container and restarting the compose network? You’re showing nothing that would do that automatically.
Also, if you use Spring Boot gradle plugin (if you’ve generated your project using spring starter, it does), you do not need your own Dockerfile. You can directly compile an up-to-date docker image using
./gradlew bootBuildImage
. And as said by @jonrsharpe, any code change must be manually rebuilt and then you must update your docker-compose deployment (docker-compose up -d).While I did see an issue with your docker-compose configuration, it would help a lot if you add some logs from the containers for questions like this. I helps in making sure that we can provide a solution for your problem from the best possible angle, Thank you!