I want to create docker images with CI/CD (Jenkins) of my spring boot application and push the image to a private nexus docker registry.
How to avoid adding my docker credentials to POM file and have them in GIT? Where should I pass/place the credentials instead?
Or should I just push the image manually in jenkins with docker login, docker push?
I followed this tutorial (https://docs.spring.io/spring-boot/docs/2.4.0/maven-plugin/reference/htmlsingle/#build-image-example-publish) and my POM looks like this:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>docker.example.com/library/${project.artifactId}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>user</username>
<password>secret</password>
<url>https://docker.example.com/v1/</url>
<email>user#example.com</email>
</publishRegistry>
</docker>
</configuration>
</plugin>
</plugins>
</build>
</project>
You could use variables in your POM and pass them when calling maven. You should store the credentials in jenkins credentials manager
e.g.:
...
<properties>
<DOCKER_REGISTRY/>
<DOCKER_REGISTRY_USER/>
<DOCKER_REGISTRY_PASSWORD/>
<DOCKER_IMAGE_NAME>${DOCKER_REGISTRY}/${project.artifactId}:${project.version}</DOCKER_IMAGE_NAME>
</properties>
...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${DOCKER_IMAGE_NAME}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>${DOCKER_REGISTRY_USER}</username>
<password>${DOCKER_REGISTRY_PASSWORD}</password>
<url>${DOCKER_REGISTRY}</url>
</publishRegistry>
</docker>
</configuration>
</plugin>
...
Then you could call maven like this in your jenkins pipeline:
stage('Build Docker Image') {
steps {
withCredentials([usernamePassword(credentialsId: 'YOUR_CREDENTIALS_ID', passwordVariable: 'NEXUS_PASSWORD', usernameVariable: 'NEXUS_USER')]) {
sh "mvn -DskipTests=true spring-boot:build-image -DDOCKER_REGISTRY=SUBDOMAIN.DOMAIN.COM -DDOCKER_REGISTRY_USER=$NEXUS_USER -DDOCKER_REGISTRY_PASSWORD=$NEXUS_PASSWORD"
}
}
}
You can store the credentials using Jenkins credential manager, and set them up in your mvn build command via -D parameters
Related
I would like to access the user-specific settings (passwords, usernames etc. for a shared private repository) that are contained within the settings.xml in my pom.
I need these settings for the Spring Boot Maven Plugin, because I want to use the publish feature there (pushing a created docker image to our private docker repository).
Is there a way to achieve this?
Of course I don't want to save any user-specific passwords inside the pom.xml.
The maven documentation states that it is possible to access the settings.xml (e.g. here: https://maven.apache.org/guides/getting-started/index.html#how-do-i-filter-resource-files), but it does not explain how to to that.
I expecting something like this in my pom:
<someTag>${userSettings.some.property}</someTag>
Suppose we have following configuration for spring-boot-maven-plugin
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>docker.example.com/library/${project.artifactId}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>user</username>
<password>secret</password>
<url>https://docker.example.com/v1/</url>
<email>user#example.com</email>
</publishRegistry>
</docker>
</configuration>
</plugin>
and do not want to store our credentials in pom.xml since it is available to everyone who has access to version control system. Maven way is to define profile in ~/.m2/setting.xml with our credentials and and replace credentials in pom.xml with corresponding placeholders, like:
~/.m2/settings.xml:
...
<profiles>
...
<profile>
<id>registry-example.com</id>
<properties>
<registry.username>user</registry.username>
<registry.password>secret</registry.password>
<registry.url>https://docker.example.com/v1/</registry.url>
<registry.email>user#example.com</registry.email>
</properties>
</profile>
...
</profiles>
...
pom.xml:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>docker.example.com/library/${project.artifactId}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>${registry.username}</username>
<password>${registry.password}</password>
<url>${registry.url}</url>
<email>${registry.email}</email>
</publishRegistry>
</docker>
</configuration>
</plugin>
Now, in order to tell maven to take into account the profile we have created we need to run maven with -P flag specifying id of our profile:
mvn spring-boot:build-image -Pregistry-example.com
I want to integrate the spring boot maven plugins capability to build and publish an OCI Image to a remote Repository
My Goal
I want to use the following plugin configuration:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.prefix}/${project.artifactId}:${project.version}</name>
</image>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
And now I want to pass the docker.publishRegistry variables by command line.
What I've tried so far
I've tried to pass the parameter with the -Ddocker.publishRegistry.username property but that didn't work.
When you take a look at the source code of the plugin Docker has no Parameter property assigned to it:
/**
* Alias for {#link Image#publish} to support configuration via command-line property.
*/
#Parameter(property = "spring-boot.build-image.publish", readonly = true)
Boolean publish;
/**
* Docker configuration options.
* #since 2.4.0
*/
#Parameter
private Docker docker;
https://github.com/spring-projects/spring-boot/blob/82b90d57496ba85be316b9eb88a36d81f2cc9baa/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java#L159
So I guess it is not possible to define this parameter by command line or is it?
Current Workaround
Currently I'm defining the properties by global maven properties and reuse them in the docker scope.
My pom.xml:
<properties>
<docker-registry>https://example.org</docker-registry>
<docker-registry-username>username</docker-registry-username>
<docker-registry-username>password</docker-registry-username>
</properties>
<!-- ... -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.prefix}/${project.artifactId}:${project.version}</name>
</image>
<docker>
<publishRegistry>
<username>${docker-registry-username}</username>
<password>${docker-registry-password}</password>
<url>${docker-registry}</url>
</publishRegistry>
</docker>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
And I'm building with:
./mvnw -B -s \
-Dspring-boot.build-image.publish=true \
-Ddocker-registry-username="$USERNAME" \
-Ddocker-registry-password="$PASSWORD" \
-Ddocker-registry="$REGISTRY" \
clean deploy
I have not the exact solution to you question: "passing publishRegistry parameters on the command line", but If I may, I have another workaround that shields you from exposing your credential in the pom.xml.
What i have done is to put the parameters and credential in a profile in my .m2/settings.xml like this:
<profiles>
<profile>
<id>docker-io-credentials</id>
<properties>
<docker-reg>docker.io</docker-reg>
<docker-reg.user>your-user-name</docker-reg.user>
<docker-reg.pwd>your-token-or-passwd</docker-reg.pwd>
<docker-reg.url>${docker-reg}/library/${docker-reg.user}</docker-reg.url>
</properties>
</profile>
</profiles>
Then on the command-line you can simply pass the profile's name to merge the credential to the current build.
mvn clean install -Pdocker-io-credentials
You can define placeholders in the spring-boot plugin's configuration, which refer to environment variables. This will be slightly less complex, so like
...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>main.Class</mainClass>
<image>
<name>registry-url/library/image-name:${project.version}</name>
</image>
<docker>
<publishRegistry>
<username>docker-user</username>
<password>${env.docker_registry_password}</password>
<url>https://registry-url/v1/</url>
<email>user#example.com</email>
</publishRegistry>
</docker>
</configuration>
...
See more on this topic here: https://www.baeldung.com/maven-env-variables
Just to mention that the Spring team is aware and did not consider this a bug but rather a documentation issue: https://github.com/spring-projects/spring-boot/issues/31024#issuecomment-1127905504
Very similar to what #twobiers suggested in his workaround:
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<publishRegistry>
<url>${docker.publishRegistry.url}</url>
<username>${docker.publishRegistry.username}</username>
<password>${docker.publishRegistry.password}</password>
</publishRegistry>
</docker>
</configuration>
and then I build (and publish to Github Packages Registry) my project with:
./mvnw spring-boot:build-image \
-Ddocker.publishRegistry.username=${{ github.actor }} \
-Ddocker.publishRegistry.password=${{ secrets.GITHUB_TOKEN }} \
-Ddocker.publishRegistry.url=ghcr.io \
-Dspring-boot.build-image.publish=true \
-Dspring-boot.build-image.imageName="ghcr.io/${{ github.repository }}:latest" \
-DskipTests
I'm currently developing a GitLab CI/CD pipeline which compiles, tests and builds a standard Spring Boot application.
I want to package it in a docker image and publish that to the GitLab registry to use it later on.
Spring Boot recently added the build-image goal to its maven plugin which also has the ability to publish the image to a registry.
My problem is, that I can't get the auth to work.
I'm using a maven:3.6.3-jdk-11-slim image for the job with the docker:dind service to have access to a docker daemon.
Building the image runs fine, but publishing fails.
I configured the maven plugin in the project pom to use properties for auth, which will be overwritten by the CLI in my CI/CD Job as follows:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<publishRegistry>
<username>${CI_REGISTRY_USER}</username>
<password>${CI_REGISTRY_PASSWORD}</password>
<url>${CI_REGISTRY}</url>
</publishRegistry>
</docker>
</configuration>
</plugin>
Properties defined in the POM with no value (Will be filled in by CLI call) :
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<CI_REGISTRY/>
<CI_REGISTRY_USER/>
<CI_REGISTRY_PASSWORD/>
</properties>
My maven CLI call in the Pipeline/Job uses the GitLab registry variables :
docker image job:
stage: Build
image: maven:3.6.3-jdk-11-slim
services:
- docker:dind
script:
- echo "java.runtime.version=11" > system.properties
- mvn spring-boot:build-image -DCI_REGISTRY=$CI_REGISTRY -DCI_REGISTRY_USER=$CI_REGISTRY_USER -DCI_REGISTRY_PASSWORD=$CI_REGISTRY_PASSWORD -Dspring-boot.build-image.imageName=SpringBootImage_${CI_JOB_ID} -Dspring-boot.build-image.publish=true
I was following the instructions via GitLab and Spring Boot documentation, but cant seem to identify my problem.
GitLab Registry Auth documentation
Spring Boot Maven Plugin image publishing documentation
I know it's been a while but in case others are trying to accomplish this
This works well
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.foo.Main</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
<phase>deploy</phase>
<configuration>
<image>
<name>${env.CI_REGISTRY_IMAGE}/${project.artifactId}:${project.version} </name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>${env.CI_REGISTRY_USER}</username>
<password>${env.CI_REGISTRY_PASSWORD}</password>
</publishRegistry>
</docker>
</configuration>
</execution>
</executions>
</plugin>
It uses gitlab-ci built-in env vars to configure everything
I would like to deploy my spring-boot app to Heroku.
I got it working just fine using heroku-cli-deploy plugin with the command:
heroku deploy:jar "my-app.jar" --app {my-app-name}
But now I would like to use heroku-maven-plugin to do this deployment.
I have my pom.xml build section:
<build>
<finalName>${artifact-name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.heroku.sdk</groupId>
<artifactId>heroku-maven-plugin</artifactId>
<version>3.0.4</version>
<configuration>
<appName>${heroku.appName}</appName>
<includeTarget>false</includeTarget>
<includes>
<include>${project.build.directory}/${project.build.finalName}.jar</include>
</includes>
<jdkVersion>${java.version}</jdkVersion>
<processTypes>
<web>java $JAVA_OPTS -jar
${project.build.directory}/${project.build.finalName}.jar</web>
</processTypes>
</configuration>
</plugin>
</plugins>
</build>
And I'm using mvn clean heroku:deploy to deploy the app. The deployment fails showing the following error:
Error: -jar requires jar file specification
What am I missing?
This change made it work:
<processTypes>
<web>java $JAVA_OPTS -jar ./target/${project.build.finalName}.jar</web>
</processTypes>
I am building and deploying my Spring Boot application into Tomcat with mvn tomcat:deploy and with this configuration:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<url>http://127.0.0.1:8080/manager/text</url>
<server>tomcat</server>
<path>/${project.build.finalName}</path>
<username>admin</username>
<password>password</password>
</configuration>
</plugin>
</plugins>
</build>
Application runs then at /${project.artifactId}. I would like to deploy the application to the another URL, ideally to set target URL while I call Maven deploy command. Is it possible? If so, how can I achieve it?
You can override maven properties from command line with -D option.
To specify another url for your app the interesting properties are maven.tomcat.port and maven.tomcat.path.
The following command line should do the trick :
mvn -Dmaven.tomcat.port=8181 -Dmaven.tomcat.path=/custom tomcat:deploy