Docker image for executing gradle bootBuildImage command - spring-boot

I'm looking for a docker image to build my gradle project which also need a docker engine to execute gradle bootBuildImage command. Any recommandation ?
Thanks,
Dan

If you use the Gradle wrapper scripts (which you should), you can use any image you like as long as it has Java on it. OpenJDK is a good match.
If you don't use the wrapper scripts, you need to have an image with Gradle installed. The official Gradle image should do.
But I think what you are really asking is how to build a docker image inside a container. The bootBuildImage task doesn't need the local Docker cli tools, and only needs to connect to a daemon. That daemon could be running on a remote host, but you can also make it connect to your local host outside the container. To do this, mount the local docker socket.
Here is an example that mounts the current directory inside a container and builds a Docker image in it through the Spring Boot plugin for Gradle:
docker run --rm \
-v gradle-cache:/home/gradle/.gradle \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD":/home/gradle/project \
-w /home/gradle/project \
gradle:6.7.0-jdk11 \
gradle --no-daemon bootBuildImage
Note that it persists the Gradle home directory in a volume, which means you can't run this command concurrently. Delete the volume when no longer needed with docker volume rm gradle-cache.
Also note that it executes the build as root.

Related

Accessing persistent H2 DB in docker container

I'm deploying a springboot application and I want to use a persistent DB. So, in application.properties file, I have
spring.datasource.url=jdbc:h2:file:/home/ubuntu/db;AUTO_SERVER=TRUE;
Now this works as long as I start this application without using a container. Now, I build a docker image and try to run the application. Dockerfile looks like
FROM maven:3-jdk-11 AS maven
ARG BUILD = target/build.jar
COPY ${BUILD} build.jar
EXPOSE 8080
USER spring:spring
ENTRYPOINT["java","-jar","/build.jar"]
Now this doesn't work when I try to start it, because it searches for /home/ubuntu/db inside the container, which does not exist. Is there a way to make the app inside the docker container access the host folder /home/ubuntu/db? Thanks for the response.
The missing part is to tell docker when running the containter to mount /home/ubuntu/db from the host into the container.
You do that like this:
docker run -v <folder_on_host>:<folder_in_cointainer>
with your example:
docker run -v /home/ubuntu/db:/home/ubuntu/db
more info on docker docs: https://docs.docker.com/get-started/06_bind_mounts/
Just in case it is helpful to anyone else, the full command to be used is:
docker run -v /home/ubuntu/db:/home/ubuntu/db --privileged -p $HOST_PORT:$CONTAINER_PORT <image-name>

How to specify an alternative main class in spring boot using bootBuildImage and packeto

When calling the spring boot plugin bootBuildImage task in gradle, a docker image is created using packeto. It starts the main class specified in the springBoot plugin. Below you can find an excerpt of the build.gradle file.
springBoot {
mainClass = 'MyMainApp'
}
bootBuildImage {
imageName = "$docker_repo/${project.name}"
}
When calling docker run, docker will run a container starting MyMainApp.
However, I want to run an alternative main class, using the same docker image. I tried the following:
specifying -Dloader.main=MyOtherApp as the cmd in docker run
specifying -Dloader.main=MyOtherApp in the JAVA_TOOL_OPTIONS environment variable
specifying LOADER_MAIN=MyOtherApp as an environment variable
None of those options start MyOtherApp.
An image created by Buildpacks provides some helpful tools for launching your application. While this is nice, overriding the default start command isn't as easy as just specifying a new command to docker run.
All of the facilities provided by Buildpacks for starting up various processes in an image are described in the docs.
I'm guessing here a bit, but it sounds like you want to run your own custom process (not the process detected by the buildpack), so try this one here.
You can even override the buildpack-defined process types:
docker run --rm --entrypoint launcher -it multi-process-app bash
docker run --rm --entrypoint launcher -it multi-process-app echo hello "$WORLD" # $WORLD is evaluated on the host machine
docker run --rm --entrypoint launcher -it multi-process-app echo hello '$WORLD' # $WORLD is evaluated in the container after profile scripts are sourced
Java should be on the path, so you can run java -Dloader.main=MyOtherApp org.springframework.boot.loader.PropertiesLauncher.
https://www.baeldung.com/spring-boot-main-class#using-cli
Alternatively, you could change your app to use PropetiesLoader by default & rebuild your image. The buildpack is just pulling the launcher for the start command out of the MANIFEST.MF file. You need to use PropertiesLauncher though as that is what supports loader.main. See https://stackoverflow.com/a/66367534/1585136.

Gradle as non root

Could you please let me know how to run a gradle image (pulled from docker repository), with jib plugin and running as non-root user in Kubernetes pod?
I have build a gradle image using Gradle 4.6.
I’m using this image in my Kubernetes pod.
When I run the image as user - root, the gradle build is successful.
When I run the image as non-root user (because of pod RBAC enablement), the build fails as gradle is unable to create /.gradle directory and there are no sufficient privileges and getting the below error.
Failed to load native library 'libnative-platform.so' for Linux amd64.
Is there any way to grant the non-root user passed via securityContext to perform the build successfully using the gradle image?
Is there a better way to resolve the issue without changing the directory permission to 777 .
Thanks in advance!!
Check if /.gradle directory is already created by root
E.g. using stat command. You might see that current user doesn't have enough permissions to work with it:
$ stat ~/.gradle | grep Uid
> Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
If so, change owner of the directory:
$ sudo chown -R $USER ~/.gradle
Where
sudo chown -R does recursive owner update
$USER contains current username
I believe you are talking about the official Gradle image on Docker Hub.
The gradle:4.6 image is designed and meant to be run as user gradle (UID 1000).
$ docker inspect gradle:4.6 --format '{{.Config.User}}'
gradle
$ docker run --rm --entrypoint id gradle:4.6
uid=1000(gradle) gid=1000(gradle) groups=1000(gradle)
Therefore, it only works when running the image as user root (UID 0) or gradle (UID 1000).
# These all work.
$ docker run --rm --user 0 gradle:4.6
$ docker run --rm --user root gradle:4.6
$ docker run --rm --user 1000 gradle:4.6
$ docker run --rm --user gradle gradle:4.6
# However, this doesn't work.
$ docker run --rm --user 1234 gradle:4.6
FAILURE: Build failed with an exception.
* What went wrong:
Failed to load native library 'libnative-platform.so' for Linux amd64.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
Therefore, you just need to make sure that you run the container as user 1000 or gradle (on whatever container runtime environment you use). And because the image gradle:4.6 is configured to run as 1000, when you build a new image based on gradle:4.6, it should run OK on almost all container runtime platforms (unless you override the configured user at the platform level).
(Now, the following assumes that you are using Jib to build another Gradle-like image based on gradle:4.6 and that you are using this new Gradle-like image on Kubernetes. That is, the following doesn't apply if you are using Jib to containerize a normal application image inside gradle:4.6.)
However, there is a bug in Jib that does not inherit the configured user from the base image. The bug will be fixed in the next 2.3.0 release. In the meantime, you can explicitly tell Jib to configure the user in the built image. In build.gradle, set
jib.container.user = 'gradle:gradle'
or if you prefer numeric UID and GID,
jib.container.user = '1000:1000'
. Or you can set the system property on the command-line:
./gradlew -Djib.container.user='gradle:gradle' ... jib
Another option is to set the correct user on the Kubernetes side. For example, within securityContext, you can set runAsUser: 1000 and runAsGroup: 1000.
Finally, although gradle:4.6 is built to run as user gradle (UID 1000), I see that recently they have reverted this decision. Now gradle:latest is configured to run as root.
$ docker run --rm --entrypoint id gradle
uid=0(root) gid=0(root) groups=0(root)

Jenkins tutorial maven project (with Docker) fails at Build stage

I'm using the current Jenkins Maven Project tutorial using Docker:
https://jenkins.io/doc/tutorials/build-a-java-app-with-maven/
I keep getting this error at the Build stage:
[simple-java-maven-app] Running shell script
sh: can't create
/var/jenkins_home/workspace/simple-java-maven-app#tmp/durable-bae402a9/jenkins-log.txt:
nonexistent directory
sh: can't create
/var/jenkins_home/workspace/simple-java-maven-app#tmp/durable-bae402a9/jenkins-result.txt:
nonexistent directory
I've tried setting least restrictive permissions with chmod -R 777, chown -R nobody and chown -R 1000 on the listed directories, but nothing seems to work.
This is happening with the jenkins image on Docker version 17.12.0-ce, build c97c6d6 on Windows 10 Professional.
As this is happening with the Maven project tutorial on the Jenkins site, I'm wondering how many others have run into this issue.
I had also the same problem on MacOSX.
After few hours of research, I have finally find the solution.
To solve the problem, it's important to understand that Jenkins is inside a container and when the docker agent inside this container talk to your docker engine, it give path to mount volume matching inner the container. But your docker engine is outer. So to allow to work correctly path inner the container must match the same path outer the container in your host.
To allow working correctly, you need to change 2 things.
docker run arguments
Jenkinsfile docker agent arguments
For my own usage, I used this
docker run -d \
--env "JENKINS_HOME=$HOME/Library/Jenkins" \
--restart always \
--name jenkins \
-u root \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $HOME/Library/Jenkins:$HOME/Library/Jenkins \
-v "$HOME":/home \
jenkinsci/blueocean
In the Jenkinsfile
Replace the agent part
agent {
docker {
image 'maven:3-alpine'
args '-v /root/.m2:/root/.m2'
}
by
agent {
docker {
image 'maven:3-alpine'
args '-v <host_home_path>/.m2:/root/.m2'
}
It's quite likely that this issue resulted from a recent change in Docker behaviour, which was no longer being handled correctly by the Docker Pipeline plugin in Jenkins.
Without going into too much detail, the issue was causing Jenkins to no longer be able to identify the container it was running in, which results in the errors (above) that you encountered with these tutorials.
A new version (1.15) of the Docker Pipeline plugin was released yesterday (https://plugins.jenkins.io/docker-workflow).
If you upgrade this plugin on your Jenkins (in Docker) instance (via Manage Jenkins > Manage Plugins), you'll find that these tutorials should start working again (as documented).
The error message means that the directory durable-bae402a9 was not created.
Walk back through the tutorial to find the step that should have created that directory, and make whatever changes are needed to make sure it succeeds.

Use environment variables in docker

I have implemented docker project for automated setup. I use docker 1.9 on Ubuntu Server and utilize feature build-arg. I using it for set dynamic subdomain in apache virtual hosts file.
docker build --no-cache --build-arg domain=demo1.myapp.com -t imagename .
docker run -d -p 8080:80 imagename
I use domain and replace it in virtual hosts file using sed command in my script file
sed -i -e "s/defaulthost.com/$domain/g" /etc/apache2/sites-enabled/myApp.conf
My Dockerfile had code
ARG domain
RUN /bin/sh /script.sh $domain
Now I need to migrate application on AWS where I get Amazon Linux AMI. But here I get supported docker version 1.7, which do not support build-arg. I tried to upgrade but lot of dependencies block me.
Now I decide to use ENV environment variables like below.
docker run -d -p 8080:80 -e domain=demo1.myapp.com
I also changed Docker file like
My Dockerfile had code
RUN /bin/sh /script.sh
But It look like they not working in my scenario as at build time sed script replace empty value in apache file and build process failed.
If it is not possible without build arg or I am doing wrong way of set/use ENV
First, AWS can support docker 1.9.
See for instance "Getting overlay networking to work in AWS with Docker 1.9"
use a Docker Machine version 0.5.2-dev, as explained here
use the right AMI (Amazon Machine Image) Ubuntu 15.10
Set up the AWS environment variables
If you chose to remain with an old AMI and its docker 1.7, then -e option are for runtime only (creating/running containers), not build time (image).
That means if your ENTRYPOINT or CMD was: /script.sh, using inside the script $domain (and then launching your main process), that would work.

Resources