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

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:

Related

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?

Failure: (ID: 838926df) did not find any jar files with a Main-Class manifest entry

when running gcloud app deploy on my spring boot app, this error happens in Cloud Build.
There can be one or many issues because of which you get this error.
For resolution please check below things -
Your app.yaml should have entrypoint and runtime information as below -
runtime: java11
entrypoint: java -Xmx64m -jar blahblah.jar
your pom.xml should have appengine maven plugin dependency
`
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
`
don't modify jar if you want to replace any configs use command -
jar uf blahblah.jar filename.yaml
make sure you have packaging as a jar in pom.xml like this -
<packaging>jar</packaging>
I deleted my maven plugin by accident, so don't delete it.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
I was able to fix it for my AppEngine + Gradle project by adding
jar {
enabled = false
archiveClassifier = ''
}
to the build.gradle file.
Try to comment out in pom.xml packaging into war file, example:
<!-- <packaging>war</packaging> -->
It fixed the issue for me. Good minimal+workable example is here: https://codelabs.developers.google.com/codelabs/cloud-app-engine-springboot#0

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

Specify version when deploying to nexus from maven

I've forked Confluent's Kafka Connect HDFS writer and now I'd like to deploy a version of this jar to my local Nexus.
mvn clean deploy Works like a charm and deploys the jar.
https://[nexus]/repository/releases/io/confluent/kafka-connect-hdfs/5.0.0/kafka-connect-hdfs-5.0.0.jar
So far so good, but to make a distinction between the confluent versions and my own deployment I'd like to change the version of the build to something like 5.0.0-1 or so (preferably the tag name when pushed, but that's step 2)
The pom.xml is basically the same as the 5.0.0-post release, but here the most important parts:
<parent>
<groupId>io.confluent</groupId>
<artifactId>kafka-connect-storage-common-parent</artifactId>
<version>5.0.0</version>
</parent>
<artifactId>kafka-connect-hdfs</artifactId>
<packaging>jar</packaging>
<name>kafka-connect-hdfs</name>
<organization>
<name>Confluent, Inc.</name>
<url>http://confluent.io</url>
</organization>
<url>http://confluent.io</url>
<description>
A Kafka Connect HDFS connector for copying data between Kafka and Hadoop HDFS.
</description>
...
<dependencies>
...
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-connect-storage-common</artifactId>
<version>${confluent.version}</version>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-connect-storage-core</artifactId>
<version>${confluent.version}</version>
</dependency>
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.confluent</groupId>
<version>0.10.0</version>
<artifactId>kafka-connect-maven-plugin</artifactId>
...
So first I added <version> tags to the pom.xml, but it started using that as the default for all confluent.version and complained that it couldn't find for example: https://[nexus]/repository/releases/io/confluent/kafka-connect-storage-hive/5.0.0-1/kafka-connect-storage-hive-5.0.0-1.pom
Next I tried the versions plugin from maven
mvn versions:set -DnewVersion=5.0.0-1 clean deploy
But that complained about the parent:
[ERROR] Failed to execute goal org.codehaus.mojo:versions-maven-plugin:2.7:set (default-cli) on project kafka-connect-hdfs: Project version is inherited from parent. -> [Help 1]
I don't even care if the version is 5.0.0 in the code, I just wat to deploy to a different version in our artifactory.
I'm not a maven expert, so maybe I'm missing some very basic clue, but all help is welcome.
So there were some good suggestions, but in the end the one thing that worked best for me in our setup was to use the deploy:deploy-file command for maven.
mvn deploy:deploy-file \
-Dfile=target/kafka-connect-hdfs-5.0.0.jar \
-DrepositoryId=[nexus id] \
-Durl=[nexus url] \
-Dversion=$TAG \
-DgroupId=io.confluent \
-DartifactId=kafka-connect-hdfs
The major downside was that I had to respecify parameters that were already present in the pom.xml (artifactId, groupId, ect), but it works, and that's what counts :-)
You can specify the version with ${revision} parameter.
To do this, you need to add <version> tag with this variable in pom.xml:
<artifactId>kafka-connect-hdfs</artifactId>
<version>5.0.0-${revision}</version>
<packaging>jar</packaging>
And then provide it to the maven command. E.g.,
mvn clean package -Drevision=01 will generate kafka-connect-hdfs-5.0.0-01.jar file.

Gitlab CI/CD for java spring maven application

I am new to Gitlab CI/CD and would like to understand how to use it correctly.
I have the following project structure:
project structure
and the following pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javasampleapproach</groupId>
<artifactId>spring-boot-rest-mysql</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringRestMySQL</name>
<description>Demo project for Spring Boot - REST -MySQL</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
Can you help me please configure .gitlab-ci.yml and CI Lint on GitLab in order to test correctly build, test and deploy?
When I added default pipeline on GitLab it always fails in test stage with the following error:
Failed to bind properties under '' to com.zaxxer.hikari.HikariDataSource:
Property: driverclassname
Value: org.postgresql.Driver
Origin: "driverClassName" from property source "source"
Reason: Failed to load driver class org.postgresql.Driver in either of HikariConfig class loader or Thread context classloader
Action:
Update your application's configuration
2018-10-11 22:55:34.413 ERROR 491 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener#776aec5c] to prepare test instance [com.javasampleapproach.springrest.mysql.SpringRestMySqlApplicationTests#796d3c9f]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125) ~[spring-test-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108) ~[spring-test-5.0.7.RELEASE.jar:5.0.7.RELEASE]
but I did not even use or import org.postgresql.Driver for nothing, I use MySQL.
.gitlab-ci.yml template that i added is:
# This file is a template, and might need editing before it works on your project.
---
# Build JAVA applications using Apache Maven (http://maven.apache.org)
# For docker image tags see https://hub.docker.com/_/maven/
#
# For general lifecycle information see https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
#
# This template will build and test your projects as well as create the documentation.
#
# * Caches downloaded dependencies and plugins between invocation.
# * Verify but don't deploy merge requests.
# * Deploy built artifacts from master branch only.
# * Shows how to use multiple jobs in test stage for verifying functionality
# with multiple JDKs.
# * Uses site:stage to collect the documentation for multi-module projects.
# * Publishes the documentation for `master` branch.
variables:
# This will supress any download for dependencies and plugins or upload messages which would clutter the console log.
# `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
# As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
# when running from the command line.
# `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"
# Cache downloaded dependencies and plugins between builds.
# To keep cache across branches add 'key: "$CI_JOB_NAME"'
cache:
paths:
- .m2/repository
# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
# Because some enforcer rules might check dependency convergence and class duplications
# we use `test-compile` here instead of `validate`, so the correct classpath is picked up.
.validate: &validate
stage: build
script:
- 'mvn $MAVEN_CLI_OPTS test-compile'
# For merge requests do not `deploy` but only run `verify`.
# See https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
.verify: &verify
stage: test
script:
- 'mvn $MAVEN_CLI_OPTS verify site site:stage'
except:
- master
# Validate merge requests using JDK7
validate:jdk7:
<<: *validate
image: maven:3.3.9-jdk-7
# Validate merge requests using JDK8
validate:jdk8:
<<: *validate
image: maven:3.3.9-jdk-8
# Verify merge requests using JDK7
verify:jdk7:
<<: *verify
image: maven:3.3.9-jdk-7
# Verify merge requests using JDK8
verify:jdk8:
<<: *verify
image: maven:3.3.9-jdk-8
# For `master` branch run `mvn deploy` automatically.
# Here you need to decide whether you want to use JDK7 or 8.
# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
# Mount your `settings.xml` as `/root/.m2/settings.xml` which holds your secrets.
# See https://maven.apache.org/settings.html
deploy:jdk8:
# Use stage test here, so the pages job may later pickup the created site.
stage: test
script:
- 'mvn $MAVEN_CLI_OPTS deploy site site:stage'
only:
- master
# Archive up the built documentation site.
artifacts:
paths:
- target/staging
image: maven:3.3.9-jdk-8
pages:
image: busybox:latest
stage: deploy
script:
# Because Maven appends the artifactId automatically to the staging path if you did define a parent pom,
# you might need to use `mv target/staging/YOUR_ARTIFACT_ID public` instead.
- mv target/staging public
dependencies:
- deploy:jdk8
artifacts:
paths:
- public
only:
- master
on my local environment i usually do:
to start backend:
mvn clean install
spring-boot:run
to start frontend:
ng serve
Thanks in advance

Resources