Docker for local development with spring boot - spring-boot

I want to use docker with a spring boot application in my development environment.
For production I first generate the war file using mvn package then I build an image using this dockerfile :
FROM tomcat:9.0-jre8-alpine
COPY target/backend-0.0.1-SNAPSHOT.war $CATALINA_HOME/webapps/api.war
But for dev purpose I want to be able to check my changes when I edit my code and not have to redo mvn package then build the image then run the container. Changes made to the code can be watched using spring boot devtools so that my app is recompiled every time I make changes to the source code.
But then I thought to use an image, still with tomcat, and setup a volume. But I don't know which files I have to watch. Is it the folder target or some specific files inside it ? And to which folder inside my image do I link the volume to ? Something like $CATALINA_HOME/webapps I presume.
If anyone can help me to point me in the right direction it would be greatly appreciated ? Thanks.

I managed to do it using a Maven Docker image. mapping the source code using volumes, and setting the working directory so Maven can find the pom.xml. In your docker-compose.yml:
version: '3.1'
services:
backend:
image: maven:3.6.3-jdk-8
command: mvn spring-boot:run
ports:
- 8080:8080
- 8085:8085
volumes:
- .:/usr/src/mymaven:rw
working_dir: /usr/src/mymaven
Make sure you have the boot tools enabled in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
And if you need, enable the remote debugger:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.3.RELEASE</version>
<configuration>
<jvmArguments>
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8085
</jvmArguments>
</configuration>
</plugin>
Then you can attach your code editor to the Docker container:
https://code.visualstudio.com/Docs/editor/debugging#_launch-versus-attach-configurations

Related

Dockerfile "VOLUME" equivalent when using Spring Boot Paketo Buildpack

I'm currently working on migrating the containerization of Spring Boot App from Dockerfile file to the Spring Boot Maven Plugin build-image.
Now I am wondering how to configure a volume in this scenario. The equivalent to having a VOLUME ["/var/store"] declartion in the Dockerfile. I already Googled for a while, help appreciated. THX!
It depends on the purpose.
If you want to add a volume mount when the buildpacks are running, then you would add a <binding> to your pom.xml.
https://docs.spring.io/spring-boot/docs/2.5.2/maven-plugin/reference/htmlsingle/#build-image.customization
Volume bind mounts that should be mounted to the builder container when building the image. The bindings will be passed unparsed and unvalidated to Docker when creating the builder container.
Bindings must be in one of the following forms:
<host source path>:<container destination path>[:<options>]
<host volume name>:<container destination path>[:<options>]
Ex: results in /host/workspace being mounted into /workspace when the buildpacks execute
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<bindings>
<binding>/host/workspace:/workspace</binding>
</bindings>
</image>
</configuration>
</plugin>
</plugins>
</build>
</project>
This would be the same as using the pack build --volume flag, if one is using the pack cli instead of Spring Boot's Maven plugin.
You can bind volumes when you run your application. That simply uses the standard tools and arguments for your container runtime. For example, you can docker run -v and map in a volume.
If you want the specific behavior of the VOLUME entry in a Dockerfile (which doesn't actually do 1 or 2 above), that's not exposed for images created using Buildpacks, which is what Spring Boot is using. If this is what you want, I would encourage you to read this SO post on volumes and reconsider if you really need it at all.

Debug Quarkus app packaged with quarkus-container-image-jib

I would like to run the same image in our dev, staging and production environments. For our dev environment I would like to be able to connect a debugger.
I build the image for our Quarkus app like this (mvn package):
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
...
</dependencies
<properties>
<quarkus.container-image.build>true</quarkus.container-image.build>
<quarkus.jib.ports>8080,5005</quarkus.jib.ports>
<quarkus.container-image.image>...</quarkus.container-image.image>
...
</properties>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>1.12.1.Final</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
Is it possible to start the application in debug mode while otherwise running with prod profile? Passing JAVA_OPTIONS=-Ddebug=true via an environment variable to the container causes a Exception in thread "main" java.nio.file.NoSuchFileException: /work/lib/deployment/deployment-class-path.dat.
If not, what would be the appropriate way to accomplish such a setup?
There are a few ways to do that, but they all rely on the fact that in Quarkus you can control Jib to use whatever launch script you want for the container image.
By default the container-image is created with a minimal java -jar ... style ENTRYPOINT, but that can be changed using the quarkus.jib.jvm-entrypoint.
If you use the default base image which already contains a run-java.sh script that provides a host of options (see https://hub.docker.com/r/fabric8/java-alpine-openjdk11-jre#startup-script-run-javash) then what you are trying to accomplish could be done by setting the following in application.properties:
quarkus.jib.jvm-entrypoint=/deployments/run-java.sh
quarkus.jib.environment-variables."JAVA_APP_DIR"=/work # this is needed so the script knows where the Quarkus jar is
Then launch the application using:
docker run --rm -p 8080:8080 -p 5005:5005 -e JAVA_DEBUG=true gandrian/getting-started:1.0.0-SNAPSHOT
Now the application will have the debug port open (but won't suspend) and you can connect to it with a debugger
It is possible to make the same Quarkus image open a debug port in your Kubernetes Deployment, i.e. without rebuilding the image only for debugging.
When using JIB with a Red Hat UBI base image, e.g. quarkus.jib.base-jvm-image=registry.access.redhat.com/ubi8/openjdk-11-runtime:1.14 , this works by overriding the container command in your Deployment K8S descriptor like this:
containers:
- name: your-app
image: your-app-image
command: ["java"]
args: ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", -Djava.util.logging.manager=org.jboss.logmanager.LogManager", "-jar", "quarkus-run.jar"]
This overrides the Docker image's Entrypoint, which looks like this (gleaned from docker inspect):
"Entrypoint": [
"java",
"-Djava.util.logging.manager=org.jboss.logmanager.LogManager",
"-jar",
"quarkus-run.jar"
],
This might work for other base images and container builders other than JIB as well, if the Entrypoint is the same.
For the records, the Quarkus image Entrypoint with JIB seems to be determined programmatically here: https://github.com/quarkusio/quarkus/blob/c28a72b63104d8856fac1481b107c9f9ff3e7f1a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java#L390

Configure custom container image LABEL using Cloud Native Build Packs/Paketo.io with spring-boot-maven-plugin for GitHub Container Registry link

I created a simple Spring Boot application using https://start.spring.io/. Now I want to use Paketo.io / Cloud Native Build Pack support of the spring-boot-maven-plugin to build a container image and push it to the GitHub Container Registry using GitHub Actions.
My pom.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.jonashackt</groupId>
<artifactId>helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>helloworld</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>15</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I successfully managed to create a Docker image using the mvn spring-boot:build-image command inside my GitHub Actions workflow. I also successfully pushed it to the GitHub Container Registry (following this guide). Here's my build.yml workflow:
name: publish
on: [push]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: Set up JDK 15
uses: actions/setup-java#v1
with:
java-version: 15
- name: Build the hello-world Docker image
run: |
echo 'Login to GitHub Container Registry'
echo $CR_PAT | docker login ghcr.io -u jonashackt --password-stdin
echo 'Build a container image from our Spring Boot app using Paketo.io / Cloud Native Build Packs'
mvn spring-boot:build-image --batch-mode --no-transfer-progress
echo 'tag Paketo build image to have the right GitHub Container Registry coordinates'
docker tag helloworld:0.0.1-SNAPSHOT ghcr.io/jonashackt/helloworld:latest
docker push ghcr.io/jonashackt/helloworld:latest
env:
CR_PAT: ${{ secrets.CR_PAT }}
But the Container Registry image isn't linked to the GitHub repository. Since this has to be done with a specific OCI compliant LABEL inside the Dockerfile:
LABEL org.opencontainers.image.source="https://github.com/jonashackt/helloworld"
How could one configure this LABEL using Cloud Native Build Packs / Paketo together with the spring-boot-maven-plugin?
As the spring-boot-maven-plugin transparently wraps Paketo.io / Cloud Native Build Packs, the best way is to start in https://paketo.io/docs. There's a section on how to apply custom labels to application images:
Paketo users may add labels to the application image using the Image
Labels Buildpack.
As org.opencontainers.image.source is a OCI-specific label, the the Image Labels Buildpack will set the correct label for us. All we have to do, is to pass an environment variable to the Paketo build that we prefix with BP_OCI_. Have a look at the possible OCI specific labels in the docs. For example, if we run the following Paketo build:
pack build spring-boot-buildpack
--path . \
--builder paketobuildpacks/builder:base \
--env "BP_OCI_DESCRIPTION=Demo Application"
The resulting application image will have a LABEL defined with the value org.opencontainers.image.description Demo Application. So in order to set org.opencontainers.image.source we need to define the environment variable BP_OCI_SOURCE for our Paketo build!
But as we use the spring-boot-maven-plugin here, we need to somehow configure this environment variable inside our pom.xml, since we don't directly interact with the Paketo CLI. The the documentation tells us that we can use the image.env tags inside a configuration tag to define
Environment variables that should be passed to the builder.
In order to configure the OCI-specific LABEL org.opencontainers.image.source https://github.com/yourGitHubUserOrOrgaName/yourRepositoryName to the application image build by Paketo using the spring-boot-maven-plugin, add the following tags to your pom.xml (here's also a fully comprehensible example project including a pom.xml):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<env>
<BP_OCI_SOURCE>https://github.com/yourGitHubUserOrOrgaName/yourRepositoryName</BP_OCI_SOURCE>
</env>
</image>
</configuration>
</plugin>
</plugins>
</build>
Now your image gets linked to your GitHub repository. If you look at your account's packages and click into the build image, you should see all your README.md information mapped like this:

Spring Boot Cloud Native Buildpacks on Heroku?

I configured my Spring Boot project to use cloud Native Buildpacks to build its docker image as below:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>my-image</name>
</image>
</configuration>
</plugin>
</plugins>
</build>
The resulting Docker image works fine, but I would like to deploy it on Heroku.
I pushed it to the Heroku Container Registry and tried to release it as explained below:
https://devcenter.heroku.com/articles/container-registry-and-runtime#building-and-pushing-image-s
https://devcenter.heroku.com/articles/container-registry-and-runtime#releasing-an-image
When I run:
heroku container:release web --app my-app
I get the following error:
▸ Expected response to be successful, got 404
By googling the error, it seems that it is due to the fact that the resulting Docker image uses
ENTRYPOINT instead of CMD to start the application, as explained in the links below:
https://github.com/grandnode/grandnode/issues/563
https://github.com/heroku/cli/issues/1081
https://devcenter.heroku.com/articles/container-registry-and-runtime#dockerfile-commands-and-runtime
Is it possible to configure the Spring Boot Buildpack in order to use CMD instead of ENTRYPOINT? Or any other solution to deploy the resulting Docker image on Heroku?

Docker + Spring Boot/MVC WAR = Tomcat 404

so I am trying to dockerize my Spring Boot web app. Under IntelliJ it works fine, but as soon as I deploy it - the http status 404 appears instead of my homepage.
project structure
-src
-main
-java
...my packages
-resources
...application.properties and others
-webapp
-WEB-INF
-views
...jsp pages
-resources
...js, css, images
-test
The pom.xml includes spring-boot-starter dependencies, Tomcat and spring-boot-maven plugin.
Dockerfile
FROM tomcat:8.0.51-jre8-alpine
RUN rm -rf /usr/local/tomcat/webapps/*
COPY ./target/charity-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/ROOT.war
I think somehow the Docker doesn't see the WEB-INF folder. I'm in trouble with this for over 2 days... Any ideas?
CMD ["catalina.sh", "run"]
Just for the record - this Dockerfile finally worked:
FROM java:8
ADD /target/charity-0.0.1-SNAPSHOT.war charity.war
ENTRYPOINT ["java", "-jar", "/charity.war"]
Just to confirm, you are doing
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
and mark the packaging as a war in pom.xml
Also, you need to modify the main class of your application
Please take reference from below link
https://www.baeldung.com/spring-boot-war-tomcat-deploy

Resources