Understanding Startup Time Difference Between JRE and JDK Based Images - java

I am struggling to understand the startup time difference between JRE based application and JDK based application.
Think that I have an application with sample DockerFile
# syntax=docker/dockerfile:experimental
ARG BASE_IMAGE_FROM=<image>
FROM $BASE_IMAGE_FROM/maven-jdk11:0.0.2 as builder
WORKDIR /builder
COPY settings.xml /settings.xml
COPY . .
RUN --mount=type=cache,id=m2-cache,target=/root/.m2 /root/build.sh <app-name>
## app
FROM $BASE_IMAGE_FROM/<jre-name>
ARG APP_PATH=/builder
ARG DEPENDENCY=${APP_PATH}/target/dependecy
WORKDIR /server
COPY <somethings>
USER <some-user>
# update class to run
ENTRYPOINT exec java ${JAVA_OPTS} -cp app/config:app/lib/setup.jar:app:app/lib/* <app-name>
In the FROM $BASE_IMAGE_FROM/ part of the file, I specify JDK based or JRE based image. The current application is based on java 11 alpine JDK image. I've changed it with JRE based image. There is a big difference in size of the images.
I also thought there should be improvements in the startup time because of the overhead of the JDK. Yet, the startup time didn't change. I couldn't figure it why.
Any opinions?

Both the JDK and the JRE contain the same java runtime. The JDK contains additional tools like the javac compiler, javadoc documentation tool, and so on.
So in your setup, the JRE-based image will be much smaller, but when the container starts up, the ENTRYPOINT java ... starts the same JVM, loads the same class files, and runs the same application.
There's nothing different here other than the disk space required by the JDK functionality you're not using (if you need to pull the image, also the network I/O and time to download the unused files).

Related

set linker in docker image made by jib java

Ok, I encountered linking problems while creating jib docker image.
I copy the files i want into container
jib {
allowInsecureRegistries = true
extraDirectories{
paths{
path{
from = file('jnetpcap/jib')
into = '/native'
}
}
}
.
.
.
and in other task, i point to those libraries
task cmdScript(type: CreateStartScripts) {
mainClassName = "cic.cs.unb.ca.ifm.Cmd"
applicationName = "cfm"
outputDir = new File(project.buildDir, 'scripts')
classpath = jar.outputs.files + project.configurations.runtime
defaultJvmOpts = ["-Djava.library.path=/native"]
}
I checked, and those libraries are added correctly to container. It is not a problem with copying libs, but setting up linker.
cmdScript sets correct linker if i build project with distTar, but i don't know how to set up linker when building it with jibDockerBuild.
I couldn't find anwer to my issue here so decided to seek help on SO.
UPDATE
I have found some clues here.
I have updated my jib task by adding
jib {
allowInsecureRegistries = true
extraDirectories{
paths{
path{
from = file('jnetpcap/jib')
into = '/native'
}
}
}
container.jvmFlags = ["-Djava.library.path=/native/*"]
But I keep getting the same error.
error message is
exception in thread main java.lang.unsatisfiedlinkerror 'long com.slytechs.library.NativeLibrary.dlopen(java.lang.String)'
The issue is largely unrelated to Jib. The root cause is missing required libraries inside the container environment.
First things first, it should be container.jvmFlags = ["-Djava.library.path=/native"] (not /native/* with the asterisk).
Now, jNetPcap is a Java wrapper around Libpcap and WinPcap libraries found on various Unix and Windows platforms. That is, on Linux (which is the OS of the container you are building), it depends on Libpcap and requires it to be installed on the system. Most OpenJDK container images (including the one Jib uses as a base image) does not come with Libpcap pre-installed, and I suspect the first problem being that you are not installing Libpcap into the container.
jNetPcap also requires loading other native libraries. In the case of my example below, they were the two .so shared object files that come with the jNetPcap package: libjnetpcap-pcap100.so and libjnetpcap.so.
For explanation, below is the complete example that creates a working container image.
Dockerfile
# This Dockerfile is only for demonstration.
FROM adoptopenjdk/openjdk11
# "libpcap-dev" includes the following files:
# - /usr/lib/x86_64-linux-gnu/libpcap.a
# - /usr/lib/x86_64-linux-gnu/libpcap.so -> libpcap.so.0.8
# - /usr/lib/x86_64-linux-gnu/libpcap.so.0.8 -> libpcap.so.1.8.1
# - /usr/lib/x86_64-linux-gnu/libpcap.so.1.8.1
RUN apt-get update && apt-get install -y libpcap-dev
# My machine is x86_64 running Linux.
RUN curl -o jnetpcap.tgz https://master.dl.sourceforge.net/project/jnetpcap/jnetpcap/1.4/jnetpcap-1.4.r1300-1.linux.x86_64.tgz
# The tar includes the following files:
# - jnetpcap-1.4.r1300/jnetpcap.jar
# - jnetpcap-1.4.r1300/libjnetpcap-pcap100.so
# - jnetpcap-1.4.r1300/libjnetpcap.so
RUN tar -zxvf jnetpcap.tgz
# .class file compiled with "javac -cp jnetpcap.jar MyMain.java"
COPY MyMain.class /my-app/
ENTRYPOINT ["java", "-cp", "/my-app:/jnetpcap-1.4.r1300/jnetpcap.jar", "-Djava.library.path=/jnetpcap-1.4.r1300", "MyMain"]
MyMain.java
import java.util.*;
import org.jnetpcap.*;
public class MyMain {
public static void main(String[] args) {
Pcap.findAllDevs(new ArrayList<>(), new StringBuilder());
System.out.println("SUCCESS!");
}
}
$ docker build -t test .
$ docker run --rm test
SUCCESS!
Therefore, as long as you copy necessary dependent libraries and have correct configuration, you should be able to make it work with Jib.
For installing Libpcap, I can think of a couple options:
Prepare a custom base image (for example, apt-get install libpcap-dev as above) and configure jib.from.image to use it.
Manually download and copy the libpcap.so file into, say, /usr/lib, using the extraDirectories feature. (You can even make your Gradle project dynamically download the file when building your project.)
For copying jNetPcap native libraries (libjnetpcap-pcap100.so and libjnetpcap.so), it's the same story. However, looks like you already manually downloaded and attempted copying them using the extraDirectories feature, so I guess you can continue doing so. But still, preparing a custom base image is another viable option. Note that in the example above, I configured -Djava.library.path=... for jNetPcap (BTW, there are many other ways to have Linux and JVM load shared libraries in an arbitrary directory), but if you copy the .so files into some standard locations (for example, /usr/lib), you wouldn't even need to set -Djava.library.path.
For all of the native libraries (.so files) above, make sure to download the right binaries compatible with the container architecture and OS (probably amd64 and Linux in your case).

CloudFoundry and JDK

I'm struggling with the deployment of a spring app that needs to compile java code during runtime. My app calls the javac command when a user submits a solution to a problem, so it can later run java
I'm deploying to cloud foundry and using the java-buildpack, but unfortunately, it doesn't come with JDK, only JRE is available and that thing has no javac or java commands available.
Do you guys know a way on how to add JDK to cloud foundry, without having to write my own custom buildpack.
Thanks
I would suggest you use multi-buildpack support and use the apt-buildpack to install a JDK. It should work fine alongside the JBP. It just needs to be first in the list.
https://github.com/cloudfoundry/apt-buildpack
Example:
Create an apt.yml.
---
packages:
- openjdk-11-jdk-headless
Bundle that into your JAR, jar uf path/to/your/file.jar apt.yml. It should be added to the root of the JAR, so if you jar tf path/to/your/file.jar you should see just apt.yml and nothing prefixed to it.
Update your manifest.yml. Add the apt-buildpack first in the list.
---
applications:
- name: spring-music
memory: 1G
path: build/libs/spring-music-1.0.jar
buildpacks:
- https://github.com/cloudfoundry/apt-buildpack#v0.2.2
- java_buildpack
Then cf push. You should see the apt-buildpack run and install the JDK. It'll then be installed under ~/deps/0/lib/jvm/java-11-openjdk-amd64. It does not appear to end up on the PATH either, so use a full path to javac or update the path.

Add specific JRE to .Net Dockerfile

I'm setting up a .net Docker image, with some .net code within it. The code, however, needs access to a very specific version of Java Runtime (jre-7u9-windowsx64.exe).
I don't know exactly where to start in adding this executable into my dotnet Dockerfile.
The current Dockerfile for dotnet
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
WORKDIR /name
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "name.dll"]
I would just like to get the JRE added to the Dockerfile so it is installed or available when Docker spins up.
Because the JRE forms part of your runtime environment, you're going to want to install it into your # Build runtime image.
Your ideal goal is to find the lowest common multiple, sufficient to run both ASP.NET and your .NET code and the JRE and your Java code.
Option #1: Find a (trusted) container image that runs both ASP.NET and JRE
Presumably, you've pursued this approach and been unsuccessful.
Option #1: Start from your currently working solution and add
I'm unfamiliar with mcr.microsoft.com/dotnet/core/aspnet but this may well continue to suffice as your baseline and it has the advantage that you know it will run your .NET app.
So, your process will be to determine what additionally -- if anything -- you'll need to install to be able to run jre-7u9-windowsx64.exe.
A hacky way to determine this would be to insert whatever the installer is for this binary (between lines 13-14). This will take the form RUN the jre-7u9-windowsx64.exe installer. Then, try to build your container and see what errors arise.
A more considered approach would be to identify whether you need to install additional packages to support jre-7u9-windowx64.exe and, if you do, you'll need to install those (using further RUN ...) commands beforehand.
Option #2: Start from a minimal baseline and add
Alternatively, you could start from a more foundational baseline. I assume the OS here is Windows rather than Linux. Presumably there's a minimal Windows container image?
Then you'd need to add whatever is needed to:
Get ASP.NET and your .NET code working
Get JRE and your Java code working
This would provide a more considered foundation for your image but at the cost of requiring you to solve two puzzles.
Option #3: Start from a working JRE image and add
Included for completeness but not-recommended in this case.

How to create very small JRE using jrecreate in ejdk for windows 10?

I want to ship my java application (command line tool) along with a small jre. I tried a lot using jrecreate options of ejdk but, I am unable to create jre for windows 10. Please help me in this regard. I referred the following links. It is always creating for linux. How to do it for Windows ?
I used the the following command:
jrecreate.bat --profile compact2 --dest compact2-client --vm all
https://blogs.oracle.com/jtc/introducing-the-ejdk
https://www.oracle.com/technetwork/java/embedded/embedded-se/downloads/index.html
https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/installing.htm
Assumption:
You already know what you are going to package into your JRE.
Like you have a list of dependencies or you have a jar file and you need to extract the dependencies and create a JRE with only those dependencies
If Java version is not of concern then you can stick to Java 9 and follow this post here.
https://medium.com/azulsystems/using-jlink-to-build-java-runtimes-for-non-modular-applications-9568c5e70ef4
I tried this a couple of weeks ago and it worked.
Regarding Win or Linux, it depends upon where you are running the jlink command
I used the following command to get compacted jre with profile compact1 in MAC.
jrecreate.sh -d ./compact1-jre/ -p compact1

System Startup Folders/Methods on UNIX Platforms?

I have a Java application that needs to implement installation features of making a JAR launch on startup.
I can do this on Window by entering a REG file into the registry, but how can I do this on UNIX platforms? Linux and Mac if the methods are different.
Do Linux and Mac have system startup folders?
Remember that I need to do this programmatically not through system preferences or anything like that.
On Linux, the classic way would be through adding a script in the appropriate /etc/rcN.d/ directory (where N is a number 0-6 representing the 'run level'). I'm not sure whether that's still the recommended way, but it usually is still supported. This would also work with minor variations for other mainstream Unix variants (Solaris, HP-UX, AIX).
On Mac, you have to work harder. The files /etc/rc.common, /etc/rc.imaging and /etc/rc.netboot are related, but there are no /etc/rcN.d directories. There's also a script rc and another rc.local. Typing man rc reveals:
DESCRIPTION
rc.local is now unsupported and has been replaced with launchd(8), which bootstraps itself via the launchctl(1) bootstrap subcommand to read in launchd(8) jobs from the standard locations.
SEE ALSO
launchd(8), launchctl(1)
So, you should investigate launchctl and launchd, particularly launchctl.
This is how I would do it on ubuntu.
First create a bash script to run the java app, similar to.
#!/bin/bash
java -jar "helloworld.jar"
and save it, in this case called 'HELLOWORLD' in /etc/init.d.
Need to make the script executable so need to run
chmod +x HELLOWORLD
Finally to make it run on start up
update-rc.d HELLOWORLD defaults
On Macs I think its launchd, and on linux its init.d. They are config files.

Categories