Recently I came across Quarkus framework. With this framework you can significantly (even in order of magnitude) reduce Java application startup time and RAM memory consumption. This makes it very attractive for running Java applications in containers on Kubernetes or OpenShift.
In Quarkus Java applications can be executed in OpenJDK JVM. However in order to get highest memory and startup time reduction you should compile your Java application code into native executable using GraalVM.
Here is example based on OpenJDK s2i image how you can build Quarkus Java application image:
# In this example I'm using OpenJDK 11 image from Red Hat image registry which requires authentication, hence in first step you might need to get your image pull secret. In general you'll need s2i OpenJDK image containing Maven >= 3.5.3 otherwise build might fail.
# get image pull secret from
# https://access.redhat.com/terms-based-registry/#/accounts
# and save it to my-pull-secret.yaml file
$ oc create -f my-pull-secret.yaml
$ oc secrets link builder my-pull-secret
$ oc secrets link builder my-pull-secret
$ oc new-app \
registry.redhat.io/openjdk/openjdk-11-rhel8~https://github.com/jstakun/hello-quarkus.git --name=java-quarkus
# or first import OpenJDK image to your cluster
$ oc import-image registry.redhat.io/openjdk/openjdk-11-rhel8 --confirm \
# or first import OpenJDK image to your cluster
$ oc import-image registry.redhat.io/openjdk/openjdk-11-rhel8 --confirm \
--all=true -n openshift
$ oc new-app \
openshift/openjdk-11-rhel8~https://github.com/jstakun/hello-quarkus.git --name=quarkus-java
Here is single command example for building Native executable for the same Java application:
$ oc new-app \
quay.io/quarkus/ubi-quarkus-native-s2i:19.2.1~https://github.com/jstakun/hello-quarkus.git --name=native-quarkus
This command produces very large image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app sha256:4e0feed735fdee1403b8f23a35ad0b9b0ae42baec41e324824f72fd45af2a424 ab444dc9a48f 3 minutes ago 1.38 GB
If you would like to build much smaller image you can build native executable locally and then use s2i binary build with Universal Base Image minimal version:
#Build native executable locally:
$MAVEN_OPTS="-Xmx4G -Xss128M \
-XX:MetaspaceSize=1G -XX:MaxMetaspaceSize=2G \
-XX:+CMSClassUnloadingEnabled"
mvn clean package -Pnative -DskipTests
#create binary s2i build
$ oc new-build --name=hello-quarkus \
--dockerfile=$'FROM registry.access.redhat.com/ubi7/ubi-minimal:latest\nCOPY *-runner /application\nRUN chgrp 0 /application && chmod +x /application\nCMD /application\nEXPOSE 8080'
#--from-file specifies native executable file created above
$ oc start-build hello-quarkus --from-file=/projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner
#run pod after image build finish
$ oc new-app hello-quarkus
This should produce much smaller image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app/hello-quarkus-ubi-minimal sha256:169396aa1199bcf7d8bfab444357ccffe35a01f366a572e3f1486a0214271c35 bc050c6c6ccd 27 minutes ago 129 MB
You can try to decrease size of your image even further using Father Linux UBI micro image which I built and pushed to my quay registry:
$ oc new-build --name=hello-quarkus --dockerfile=$'FROM quay.io/jstakun/ubi8-micro:0.1\nCOPY *-runner /application\nRUN mkdir /vertx && chgrp -R 0 /vertx && chmod -R g=u /vertx && chgrp 0 /application && chmod +x /application\nCMD /application -Djava.io.tmpdir=/vertx\nEXPOSE 8080'
This should produce even smaller image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app/hello-quarkus-ubi-micro sha256:a2f6e6c81487ccf174f568223e65477c07736370331d77cebd1122c862eddd33 5044755e8456 32 minutes ago 91.2 MB
If you want to try to build smallest image you can try to do that from the scratch and add only libraries which are referenced by your executable and sh:
#check what libraries are referenced by your native executable:
$ ldd /projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner
linux-vdso.so.1 (0x00007ffc25b54000)
libm.so.6 => /lib64/libm.so.6 (0x00007f298e977000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f298e757000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f298e553000)
libz.so.1 => /lib64/libz.so.1 (0x00007f298e33c000)
librt.so.1 => /lib64/librt.so.1 (0x00007f298e133000)
libc.so.6 => /lib64/libc.so.6 (0x00007f298dd70000)
/lib64/ld-linux-x86-64.so.2 (0x00007f298ecf9000)
#and sh
$ ldd /bin/sh
linux-vdso.so.1 => (0x00007ffc4dfb4000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f0ec5407000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f0ec5203000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0ec4e36000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0ec5631000)
#build container from the scratch using buildah
container=$(buildah from scratch)
mnt=$(buildah mount $container)
mkdir $mnt/bin
mkdir $mnt/lib64
buildah config --workingdir /bin $container
buildah copy $container /projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner /bin/application
buildah copy $container /bin/sh /bin/sh
buildah copy $container /lib64/libtinfo.so.5 /lib64
buildah copy $container /lib64/ld-linux-x86-64.so.2 /lib64
buildah copy $container /lib64/libm.so.6 /lib64
buildah copy $container /lib64/libpthread.so.0 /lib64
buildah copy $container /lib64/libdl.so.2 /lib64
buildah copy $container /lib64/libz.so.1 /lib64
buildah copy $container /lib64/librt.so.1 /lib64
buildah copy $container /lib64/libc.so.6 /lib64
buildah copy $container /lib64/ld-linux-x86-64.so.2 /lib64
buildah config --port 8080 $container
buildah config --entrypoint /bin/application $container
buildah commit --format docker $container hello-quarkus-minimal:latest
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/hello-quarkus-minimal latest 2dd6b83e8432 8 seconds ago 28 MB
#run container
$ podman run localhost/hello-quarkus-minimal:latest -d
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) hello 1.0.0-SNAPSHOT (running on Quarkus 1.0.0.Final) started in 0.009s. Listening on: http://0.0.0.0:8080
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) Profile prod activated.
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Now check startup time in the logs and RAM memory consumption for both running pods. You should see big difference. If you compare with simplest Spring Boot example difference should be even bigger in terms of startup time, RAM consumption and image size.
This command produces very large image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app sha256:4e0feed735fdee1403b8f23a35ad0b9b0ae42baec41e324824f72fd45af2a424 ab444dc9a48f 3 minutes ago 1.38 GB
If you would like to build much smaller image you can build native executable locally and then use s2i binary build with Universal Base Image minimal version:
#Build native executable locally:
$MAVEN_OPTS="-Xmx4G -Xss128M \
-XX:MetaspaceSize=1G -XX:MaxMetaspaceSize=2G \
-XX:+CMSClassUnloadingEnabled"
mvn clean package -Pnative -DskipTests
#create binary s2i build
$ oc new-build --name=hello-quarkus \
--dockerfile=$'FROM registry.access.redhat.com/ubi7/ubi-minimal:latest\nCOPY *-runner /application\nRUN chgrp 0 /application && chmod +x /application\nCMD /application\nEXPOSE 8080'
#--from-file specifies native executable file created above
$ oc start-build hello-quarkus --from-file=/projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner
#run pod after image build finish
$ oc new-app hello-quarkus
This should produce much smaller image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app/hello-quarkus-ubi-minimal sha256:169396aa1199bcf7d8bfab444357ccffe35a01f366a572e3f1486a0214271c35 bc050c6c6ccd 27 minutes ago 129 MB
You can try to decrease size of your image even further using Father Linux UBI micro image which I built and pushed to my quay registry:
$ oc new-build --name=hello-quarkus --dockerfile=$'FROM quay.io/jstakun/ubi8-micro:0.1\nCOPY *-runner /application\nRUN mkdir /vertx && chgrp -R 0 /vertx && chmod -R g=u /vertx && chgrp 0 /application && chmod +x /application\nCMD /application -Djava.io.tmpdir=/vertx\nEXPOSE 8080'
This should produce even smaller image:
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
image-registry.openshift-image-registry.svc:5000/my-quarkus-app/hello-quarkus-ubi-micro sha256:a2f6e6c81487ccf174f568223e65477c07736370331d77cebd1122c862eddd33 5044755e8456 32 minutes ago 91.2 MB
If you want to try to build smallest image you can try to do that from the scratch and add only libraries which are referenced by your executable and sh:
#check what libraries are referenced by your native executable:
$ ldd /projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner
linux-vdso.so.1 (0x00007ffc25b54000)
libm.so.6 => /lib64/libm.so.6 (0x00007f298e977000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f298e757000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f298e553000)
libz.so.1 => /lib64/libz.so.1 (0x00007f298e33c000)
librt.so.1 => /lib64/librt.so.1 (0x00007f298e133000)
libc.so.6 => /lib64/libc.so.6 (0x00007f298dd70000)
/lib64/ld-linux-x86-64.so.2 (0x00007f298ecf9000)
#and sh
$ ldd /bin/sh
linux-vdso.so.1 => (0x00007ffc4dfb4000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f0ec5407000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f0ec5203000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0ec4e36000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0ec5631000)
#build container from the scratch using buildah
container=$(buildah from scratch)
mnt=$(buildah mount $container)
mkdir $mnt/bin
mkdir $mnt/lib64
buildah config --workingdir /bin $container
buildah copy $container /projects/hello-quarkus/target/hello-1.0.0-SNAPSHOT-runner /bin/application
buildah copy $container /bin/sh /bin/sh
buildah copy $container /lib64/libtinfo.so.5 /lib64
buildah copy $container /lib64/ld-linux-x86-64.so.2 /lib64
buildah copy $container /lib64/libm.so.6 /lib64
buildah copy $container /lib64/libpthread.so.0 /lib64
buildah copy $container /lib64/libdl.so.2 /lib64
buildah copy $container /lib64/libz.so.1 /lib64
buildah copy $container /lib64/librt.so.1 /lib64
buildah copy $container /lib64/libc.so.6 /lib64
buildah copy $container /lib64/ld-linux-x86-64.so.2 /lib64
buildah config --port 8080 $container
buildah config --entrypoint /bin/application $container
buildah commit --format docker $container hello-quarkus-minimal:latest
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/hello-quarkus-minimal latest 2dd6b83e8432 8 seconds ago 28 MB
#run container
$ podman run localhost/hello-quarkus-minimal:latest -d
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) hello 1.0.0-SNAPSHOT (running on Quarkus 1.0.0.Final) started in 0.009s. Listening on: http://0.0.0.0:8080
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) Profile prod activated.
2019-12-06 08:48:11,158 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Now check startup time in the logs and RAM memory consumption for both running pods. You should see big difference. If you compare with simplest Spring Boot example difference should be even bigger in terms of startup time, RAM consumption and image size.
Quarkus is community project but soon will get productized as part of OpenShift Container Platform. Enjoy!
Brak komentarzy:
Prześlij komentarz