Dockerfile "VOLUME" equivalent when using Spring Boot Paketo Buildpack - spring-boot

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.

Related

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

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?

Spring Boot 2.3 Maven Docker build adds "docker.io" prefix

I'm trying to build a Docker image for my Spring Boot service using mvn spring-boot:build-image as mentioned in the guide here. It also mentions
The result is an image called docker.io//:latest by default. You can modify the image name in Maven using
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>my/preferred/name</name>
</image>
</configuration>
</plugin>
</plugins>
</build>
It seems pretty straightforward... however, this results in a Docker image with the name docker.io/my/preferred/name.
How do I get rid of the docker.io prefix?
My guess is that you are confusing the parser with so many slashes (/). Since there is no reasonable way to parse my/preferred/name, it will be considered as just a name (as a whole) in which case the default docker.io/library/ will be prepended to it.
Here is how you can specify the name of the image in the name tag.
name (maps to docker.io/library/name)
domain/name
domain:port/name
domain:port/name:tag
domain:port/name#digest
Your my/preferred/name name falls under the first category.

Docker for local development with 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

How to build the docker image using jib-maven-plugin, but not push by default?

I have a simple SpringBoot application and I want to build docker image using Jib Maven plugin.
Following is my plugin configuration:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.1.0</version>
<configuration>
<from>
<image>openjdk:11-jdk-slim</image>
</from>
<to>
<image>username/appname</image>
<tags>
<tag>latest</tag>
<tag>${project.version}</tag>
</tags>
</to>
<container>
<mainClass>demo.Application</mainClass>
<ports>
<port>8080</port>
<port>8787</port>
</ports>
</container>
</configuration>
</plugin>
I just want to build the image locally and run it. I don't want to build and push to docker registry in one go.
When I run the command mvn jib:build it is automatically getting pushed to DockerHub using my credentials from Docker config (/Users/username/.docker/config.json).
Is there a way I can disable push and another goal just to push the image to registry?
Since you said jib:dockerBuild is not an option, the closest workaround is jib:buildTar; the goal creates a local tarball at target/jib-image.tar (path configurable with <outputPaths><tar>). Running jib:buildTar (or any jib:... goals) for the first time will build and cache everything necessary to build an image. Therefore, subsequent jib:build runs will be no-op in terms of building layers. It will only need to send layers missing in a remote registry.
Of course, one downside of the workaround is that it creates an unnecessary tarball, which may be large.
Unrelated to you question, you don't need to set <tag>latest</tag>, because the target image reference <image>username/appname<image> already implies <image>username/appname:latest<image>. The <tags> configuration is for pushing additional tags on top of <to><iamge>.
From JIB documentation Jib Maven Plugin, you can run
mvn jib:dockerBuild
This will push an image to a Docker daemon after building.
If you are using podman in mac or windows (with wsl2)...
specify path where podman command can be found
mvn jib:dockerBuild -Djib.dockerClient.executable=$(which podman)
I just checked and it is possible to have the image built by Jib locally. First make sure that your Docker daemon is running locally. And then do it like this:
<to>
<image><your-local-image-name></image>
</to>
I guess yours might not have worked because your Docker daemon wasn't running or your image name started with your username, which made Jib think that you actually want to push the image to Docker Hub.

Resources