A survey of the Kubernetes native Java framework Quarkus

Introduction

On March 7, 2019, Red Hat announced the Kubernetes native Java framework "Quarkus".

Introducing Quarkus: a next-generation Kubernetes native Java framework https://developers.redhat.com/blog/2019/03/07/quarkus-next-generation-kubernetes-native-java-framework/

With the increasing use of Kubernetes and the shift to microservices and serverless applications, containerization of applications is becoming more common, and slow startup compared to other languages was a major disadvantage for Java. On the other hand, Quarkus has a catch phrase "Supersonic Subatomic Java (Java smaller than atom)" by booting the Linux Native binary created using GraalVM on the container. It dramatically reduces Java startup time.

I think that various people have already written blogs about Quarkus, so in this article I will also write about "Background of Quarkus appearance" and "Deploying applications using Quarkus to Kubernetes". ..

Background of the appearance of Quarkus

Due to the trend of Docker / Kubernetes and the trend toward microservices / serverless, containerization of applications is becoming common. For the development of these container applications, languages such as Golang and Ruby that are lightweight, have a short startup time, and use a small amount of memory are often adopted, and the use of Java, which has a slow startup time and a large amount of memory usage, has been avoided. I did.

However, the Java ecosystem has been around for over 20 years, and I think it's still one of the most popular languages used by many developers as of 2019.

"Quarkus" was introduced to deploy Kubernetes native container applications using Java while utilizing the knowledge and skills that Java developers have cultivated so far. Quarkus can significantly reduce startup time, reduce memory usage, and achieve small-sized container images.

We believe that Quarkus was released in an attempt to bring many Java developers into the Kubernetes ecosystem, make container applications more general, and make the Kubernetes ecosystem bigger. ..

What is GraalVM

GraalVM is a general-purpose runtime released by Oracle as open source in April 2018 that executes multiple languages such as Java, JavaScript, Ruby, and Python on a single integrated runtime.

architecture.png

Quote: https://www.graalvm.org/docs/img/architecture.png

For Java application developers, the new Just-In-Time compilation technology enables Java applications to run faster. GraalVM also has the ability to create Nativ e binaries, which can be run immediately by using static analysis to find reachable code and perform Ahead-Of-Time (AOT) compilation. You can include the code in a Native binary.

Restrictions as of March 2019

Support / unsupport status of CDI implementation

Quarkus can use CDI (Contexts and Dependency Injection), but not all CDI specifications can be implemented. The supported and unsupported features are as follows.

Supported features

Restrictions

Reference: https://quarkus.io/guides/cdi-reference.html

Build time

Quarkus uses GraalVM to create a Linux Native binary, but in my environment it takes about "9 minutes" from executing the Maven command to completing it, even though it is a simple application. It's gone. Since "development mode" that realizes hot deployment is provided, when developing in a local environment, we will not do containerization, but for the time being, we will develop using "development mode".

Let's deploy your application to Kubernetes with Quarkus

For now, let's create a simple JAX-RS application by following the Get Started at the following URL:

QUARKUS - GET STARTED https://quarkus.io/get-started/

getting-started-architecture.png

Advance preparation

To deploy an application to Kubernetes using Quarkus, you need to prepare the following environment in advance.

Creating the first application

Let's create a Maven project

To develop an application using Quarkus, create a Maven project using the following command example.

mvn io.quarkus:quarkus-maven-plugin:0.11.0:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

Running the above command will create a directory file similar to the following: Here, a sample Dockerfile is also automatically created under the src / main / docker directory.

$ tree getting-started/
getting-started/
├── pom.xml
└── src
    ├── main
    │   ├── docker
    │   │   └── Dockerfile
    │   ├── java
    │   │   └── org
    │   │       └── acme
    │   │           └── quickstart
    │   │               └── GreetingResource.java
    │   └── resources
    │       └── META-INF
    │           ├── microprofile-config.properties
    │           └── resources
    │               └── index.html
    └── test
        └── java
            └── org
                └── acme
                    └── quickstart
                        ├── GreetingResourceTest.java
                        └── NativeGreetingResourceIT.java

Quarkus BOM and quarkus-maven-plugin are pre-configured in the automatically generated pom.xml as shown below.

pom.xml


<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-bom</artifactId>
            <version>${quarkus.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-maven-plugin</artifactId>
            <version>${quarkus.version}</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Let's check the code of the application

When you create a Maven project with the above command, the following sample code will be generated.

src/main/java/org/acme/quickstart/GreetingResource.java


package org.acme.quickstart;
  
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

Let's start in development mode

Next, try starting the application using "development mode". In "development mode", hot deployment by background compilation is possible, and when you update the Java file and update the browser, the Java file is automatically compiled and you can check the modified application.

$ mvn compile quarkus:dev

You can check the operation of the sample JAX-RS application by executing the following command.

$ curl http://localhost:8080/hello
hello

Let's try CDI (Contexts and Dependency Injection)

Next, let's customize the automatically generated sample application. Here, create a new service class and add a service class call to the automatically generated GreetingResource.java.

Add the following service class newly.

src/main/java/org/acme/quickstart/GreetingService.java


package org.acme.quickstart;
  
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {

    public String greeting(String name) { 
        return "hello " + name;
    }

}

Then modify the auto-generated GreetingResource.java.

src/main/java/org/acme/quickstart/GreetingResource.java


package org.acme.quickstart;
  
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.inject.Inject;
import javax.ws.rs.PathParam;

@Path("/hello")
public class GreetingResource {

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(@PathParam("name") String name) {
        return service.greeting(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

Check the operation with the following command.

$ curl http://localhost:8080/hello/greeting/quarkus
hello quarkus

Creating a Linux Native Binary

Let's generate Native binaries

Next, I use GraalVM to create a Linux Native binary, but the auto-generated pom.xml comes with executions pre-configured in the profile. With the following settings, you can create a Linux Native binary by executing the Maven command.

pom.xml


<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>io.quarkus</groupId>
                    <artifactId>quarkus-maven-plugin</artifactId>
                    <version>${quarkus.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>native-image</goal>
                            </goals>
                            <configuration>
                                <enableHttpUrlHandler>true</enableHttpUrlHandler>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Create a Linux Native binary with the following Maven command.

$ mvn package -Pnative -Dnative-image.docker-build=true

Here, the -Pnative option is an option to generate a binary that can be executed by Native, and the -Dnative-image.docker-build = true option is an option to generate a Native binary according to the OS on Docker. This is an option required to make it explicit.

By the way, when I ran it in my environment, it took "about 9 minutes" to complete the Maven command. If it takes so long without docker build running, it will not be possible to build frequently, so I would like to expect future functional improvements.

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 08:48 min
[INFO] Finished at: 2019-03-18T12:16:35+09:00
[INFO] ------------------------------------------------------------------------

Let's build Docker

containerization-process.png

When you create a Maven project, a sample Dockerfile is automatically generated, so create a Docker image based on this file.

FROM registry.fedoraproject.org/fedora-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Create a Docker image using the following command.

$ docker build -f src/main/docker/Dockerfile -t quarkus-quickstart/quickstart .

Let's check the size of the created container image. Since the base image is "fedra-minimal", it is 125MB, but you can see from the difference that it is "about 20MB" if you just run the Quarkus sample JAX-RS application. Considering that it took hundreds of MB just to install the JDK, this container image size looks pretty small.

$ docker images
REPOSITORY                                  TAG                 IMAGE ID            CREATED              SIZE
quarkus-quickstart/quickstart               latest              d93d89bde0bb        About a minute ago   125MB
registry.fedoraproject.org/fedora-minimal   latest              f0c38118c459        3 weeks ago          105MB

Let's check the operation

Next, try starting the Docker container.

$ docker run -i --rm -p 8080:8080 quarkus-quickstart/quickstart
2019-03-18 03:21:00,052 INFO  [io.quarkus](main) Quarkus 0.11.0 started in 0.012s. Listening on: http://0.0.0.0:8080
2019-03-18 03:21:00,054 INFO  [io.quarkus](main) Installed features: [cdi, resteasy]

In my environment, I was able to start it in a dramatically short time of "0.012 seconds", which is unthinkable with a conventional Java application, as in the above example. You can also check the operation with the following command.

curl http://localhost:8080/hello
hello

You can also see that the memory usage is as follows, and the sample JAX-RS application uses only 1.2MB.

$ docker stats
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
b4d39bbe3159        eloquent_bohr       0.00%               1.281MiB / 1.952GiB   0.06%               1.04kB / 0B         0B / 0B             5

Deploy to Kubernetes

Up to this point, you have created a Linux Native binary from the Java source code using the Maven command, and started the Linux binary as a container. Next, I would like to deploy an application using Quarkus on Kuberntetes (minikube).

Use the following command to create Deployment / ReplicaSet / Pod resources from the Docker image created earlier.

$ kubectl run quarkus-quickstart --image=quarkus-quickstart/quickstart:latest --port=8080 --image-pull-policy=IfNotPresent
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/quarkus-quickstart created

Next, create a Service resource with the kubectl expose command so that you can check the operation.

$ kubectl expose deployment quarkus-quickstart --type=NodePort
service/quarkus-quickstart exposed

The Kubernetes resources created by the above two commands are as follows.

$ kubectl get all
NAME                                      READY   STATUS    RESTARTS   AGE
pod/quarkus-quickstart-7986c7cd95-44k9n   1/1     Running   1          1m

NAME                         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/kubernetes           ClusterIP   10.96.0.1      <none>        443/TCP          5m
service/quarkus-quickstart   NodePort    10.103.25.16   <none>        8080:32179/TCP   27s

NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/quarkus-quickstart   1/1     1            1           1m

NAME                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/quarkus-quickstart-7986c7cd95   1         1         1       1m

You can check the operation of the Quarkus application deployed on Kubernetes (minikube) with the following command.

$ curl $(minikube service quarkus-quickstart --url)/hello/greeting/quarkus
hello quarkus

at the end

In this article, I wrote about "Background of the appearance of Quarkus" and "Deploying applications using Quarkus to Kubernetes". As mentioned above, Quarkus not only makes startup dramatically faster, but also the essential elements for container applications such as small image size and memory usage are in Java, which has a large number of developers including enterprises. I think it was a very important release in the sense that it became possible.

However, since it has just been released, we also found that there are various restrictions on CDI and that it takes a long time to build.

Quarkus seems to be a product released by Red Hat, and is developed as OSS (open source software). It has just been released, and I think we will continue to add and improve functions in the future, so I would like to continue watching.

Reference information

Recommended Posts

A survey of the Kubernetes native Java framework Quarkus
I tried the Java framework "Quarkus"
Measure the size of a folder in Java
Awesome Java: A number of great Java framework library software
[Java] When writing the source ... A memorandum of understanding ①
A review note of the Spring Framework Resource interface
A record of studying the Spring Framework from scratch
A quick explanation of the five types of static in Java
Think of a Java update strategy
[Java] Delete the elements of List
Root cause of java framework bugs
[Java version] The story of serialization
Sort a List of Java objects
A memorandum of the FizzBuzz problem
Consideration on the 2017 Java Persistence Framework (1)
A story about hitting the League Of Legends API with JAVA
Get the public URL of a private Flickr file in Java
A brief description of JAVA dependencies
Let's create a TODO application in Java 5 Switch the display of TODO
The origin of Java lambda expressions
Dynamically increase the number of elements in a Java 2D array (multidimensional array)
A collection of phrases that impresses the "different feeling" of Java and JavaScript
The story of making a game launcher with automatic loading function [Java]
Let's express the result of analyzing Java bytecode with a class diagram
How to find out the Java version of a compiled class file
[Java] How to get to the front of a specific string using the String class
Sample program that returns the hash value of a file in Java
How to get the absolute path of a directory running in Java
Name a group of regular expressions (Java)
Check the contents of the Java certificate store
Examine the memory usage of Java elements
[Java] Get the day of the specific day of the week
Memo: [Java] Check the contents of the directory
Compare the elements of an array (Java)
Try running a Kubernetes Job from Java
[day: 5] I summarized the basics of Java
What are the updated features of java 13
Easily measure the size of Java Objects
Looking back on the basics of Java
Output of the book "Introduction to Java"
Features of spring framework for java developers
Survey of Java 8 Function, Consumer, Supplier, Predicate
The story of writing Java in Emacs
[Java] Check the number of occurrences of characters
Find the difference from a multiple of 10
[Java] [Spring] Test the behavior of the logger
java framework
Up to the point of launching a Docker container built using RedHat Quarkus
A story confirming the implementation of the SendGrid Java library when mail delivery fails
Validate the identity token of a user authenticated with AWS Cognito in Java
The story of low-level string comparison in Java
JAVA: jar, aar, view the contents of the file
The story of making ordinary Othello in Java
About the official start guide of Spring Framework
About the description order of Java system properties
About the idea of anonymous classes in Java
Do you need a memory-aware implementation of Java?
A story about the JDK in the Java 11 era
Make a margin to the left of the TextField
The order of Java method modifiers is fixed
[Java] Access the signed URL of s3 (signed version 2)