Quarkus Development mode with Kubernetes and Skaffold

panciz

Davide Poletti

Posted on December 10, 2019

Quarkus Development mode with Kubernetes and Skaffold

How to create a workflow to automatically apply changes to a Quarkus services running on container inside Kubernetes.

With the announce of the First Release the Red Hat’s Quarkus framework becomes available for production .

What is Quarkus ?!

Quarkus is a Kubernetes-native Java stack for cloud-native and serverless application development. Quarkus promises faster startup times and lower memory consumption than traditional Java-based microservices frameworks.

see Quarkus Getting Start Development mode.

We can say that the Development mode allows to build and deploy in-container java code as fast as you can do using using javascript and npm in the Node.js framework.
In order demonstrate how can be powerfull the Quarkus framework I went a bit further the quarkus getting start tutorial . I created a development workflow that allows to apply immediately the code changes into the container running in the Kubernetes cluster. Leveraging on Skaffold the workflow can re-build and deploy the java based containers in Kubeternets automatically.
Below I report in writing my experiment in the form of a I simple tutorial.

As first step set up the environment following the instruction in the prerequisites section.

Then create the the getting start project using the maven archetype:

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

Just check everything is ok by running:

./mvnw compile quarkus:dev

As second step we need a Docker image to run the code in the container. Actually the framework already produces a Docker image in

/src/main/docker/Dockerfile.jvm

just by running:

./mvnw package

Unfortunately that is just the production image and it does not run the code in the development mode. In order to run the service in Quarkus development mode we need an image that includes maven and run the application with the compile:dev task.

This is the image Dockerfile.dev we are going to use. ( it’s based on an old commit made by Michael Vorburger I founded in GITHUB.)

FROM centos:latest
ENV MAVEN_VERSION=3.6.2
ENV MAVEN_URL=https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz
RUN yum -y update && \
    yum install -y --setopt=skip_missing_names_on_install=False \ 
    rsync java-11-openjdk-devel && \
    yum clean all -y && \
mkdir -p /usr/share/maven && \
curl -fsSL ${MAVEN_URL} | tar -xzC /usr/share/maven --strip-components=1 && \
ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
ENV JAVA_HOME /etc/alternatives/java_sdk
RUN adduser -u 9999 -g 0 quarkus
USER 9999
RUN mkdir /home/quarkus/dev/
WORKDIR /home/quarkus/dev/
COPY pom.xml /home/quarkus/dev/
COPY src /home/quarkus/dev/src
# Run quarkus just one to populate .m2
RUN cd /home/quarkus/dev/ && \ 
mvn clean compile dependency:go-offline dependency:resolve-plugins && \ mvn quarkus:dev & \ until $(curl --output /dev/null --silent --head --fail http://localhost:8080); do printf '.'; sleep 1; done && \ kill %1
# a simple script that runs mvn quarkus:dev
COPY mvn-run-dev.sh /home/quarkus/
USER root
RUN mv /home/quarkus/mvn-run-dev.sh /usr/local/bin
RUN chmod a+x /usr/local/bin/mvn-run-dev.sh
RUN chgrp -R 0 /home/quarkus && \
chmod -R g=u /home/quarkus && \
chmod -R a+rwx /home/quarkus
USER 9999
CMD ["bash", "-c", "/usr/local/bin/mvn-run-dev.sh"]

Now it’s possible to run the image sharing the src directory with the host machine.

docker build -f src/main/docker/Dockerfile.dev -t quarkus-simplerest .
docker run -i -d --rm -p 8080:8080 -v $(pwd)/src/:/home/quarkus/dev/src --name simplerest quarkus-simplerest

With this settings we are now able to modify the source code and the application running in the container will be recompiled automatically.
For example you can add a method to the Greetings.java class:

@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public String helloName(@PathParam(value = "name") String name) {
   return "hello hello "+name;
}

and try to call the new rest method at http://localhost:8080/hello/bob .

Now that we have a docker image suitable for the development mode we can use it inside Kubernetes and exploit Skaffold in order to iterate on the application source code locally then deploy to local or remote Kubernetes clusters.

First we need to create the configuration to set up a simple Kubernetes cluster. I do not post the entire configuration you can find it in this github repository containing the complete example.

The cluster contains this three objects:

  • one Deployment object that manage the pod that runs the instance of the Quarkus docker image we created before.
  • one Cluster-IP service to exposes the Pod on a cluster-internal IP
  • an Ingress Service to grant external HTTP access to the service.

To run the cluster locally we can use minikube. Just remember to set up correctly the minikube docker environment by:

  • switch on to the minikube docker environment using minikube docker-env
  • Install the docker image
docker build -f src/main/docker/Dockerfile.dev -t quarkus-simplerest .
  • make sure to enable the minikube ingress controller
minikube addons enable ingress

You can find all the kuberneted configuration in the github repository inside the k8s dir. It’s possible to test the Kubernetes configurations using.

kubectl apply -f k8s/

The service should be available at the address

http://$(minikube ip)/hello

Now the Quarkus java service it runs inside Kubernetes in development mode. In order update the code live inside the pod we now have to use Skaffold.
This is the Skaffold configuration we can use:

apiVersion: skaffold/v1beta2
kind: Config
build:
   local:
     push: false
artifacts:
    -image: quarkus-simplerest
     context: .
     docker:
        dockerfile: src/main/docker/Dockerfile.dev
     sync:
        '**/*.java': .
deploy:
   kubectl:
     manifests:
       - k8s/quarkus-simplerest-deployment.yaml
       - k8s/quarkus-simplerest-ip-service.yaml

As you notice this configuration indicates to Skaffold to synchronize all the java files in the subdirectories in the pods created with our Dockerfile.dev .
Running Skaffold with:

skaffold dev

the tools will recreate the docker image and redeploy it in the Kubernetes cluster.

*That’s it, now we are finally ready to made some modification to our code and *Skaffold& will update them to the container containing the quarkus application running in *development mode.

That’s means that we are able to modify the code in the project and see the result live!!!

For example we can add the method

@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}/{surname}")
public String helloName(@PathParam(value = "name") String name,
                   @PathParam(value = "surname") String surname) {
   return "hello mr "+name+" "+surname;
}

and see the result after few seconds at:

https://$(minikube ip)/hello/davide/poletti

As I already said all the code can be found in this repo!!

💖 💪 🙅 🚩
panciz
Davide Poletti

Posted on December 10, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related