Quarkus Development mode with Kubernetes and Skaffold
Tags: kubernetes, quarkus, skaffold
Published: 2019-12-10 22:21:20

**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](https://quarkus.io/blog/quarkus-1-0-0-Final-bits-are-here/) 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](https://quarkus.io/guides/getting-started#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](https://quarkus.io/guides/getting-started#prerequisites).

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](https://github.com/quarkusio/quarkus/compare/master...vorburger:issue-1390_dev-container#diff-c4838d06cf92241aa52bc00c4678b7ad) made by [Michael Vorburger](https://github.com/vorburger) I founded in GITHUB.)

```docker
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:

```java
@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](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](https://github.com/Panciz/quarkus-k8s-skaffold-example) 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](https://github.com/Panciz/quarkus-k8s-skaffold-example) 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:

```yaml
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

```java
@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](https://github.com/Panciz/quarkus-k8s-skaffold-example)!!