Join us on December 15 for Looking Back to the Future: 5 Process Automation Trends in 2022

Icon Close SAVE YOUR SEAT
Camunda Platform

Camunda Platform: Deploy a Process to Kubernetes with Quarkus

Our latest Camunda Platform Runtime 7.16.0 release introduces a new Quarkus Extension allowing you to add an embedded Process Engine to your Quarkus Application.

Some months ago, I joined forces with my teammate Nikola Koevski to create the Camunda Quarkus Engine Extension. Today, we present it in more detail.

Similarly to Spring Boot, the Quarkus framework:

  • is independent of a Java Application Server; you can build an uber-jar that runs directly on the JVM
  • supports dependency injection
  • has a strong focus on simplifying your local development workflow
  • has a growing list of best-of-breed Java library extensions you can choose from

Quarkus supports generating resources to deploy your application to a cluster technology like Kubernetes, OpenShift, or Knative.

In this article, you will learn in nine simple steps how to deploy a Quarkus Process Application and its Postgres database to a Kubernetes cluster.

Table of Contents

Prerequisites

  • Access to a Kubernetes cluster (e.g., via Minikube)
  • Java 11+
  • A Java IDE of your choice
  • Optionally Maven 3.8.1+

Step 1: Initial Project Setup

Let's first create the initial project setup. In this guide, we will use the build tool Gradle, but with Maven, it should work similarly. No worries if you haven't installed Gradle yet since you don't need to. The Gradle Wrapper is part of the basic project structure.

There are two options to create the initial project structure.

Using Maven

To make things as easy as possible, you can use the Maven Plugin to scaffold your project:

$ mvn io.quarkus.platform:quarkus-maven-plugin:2.3.0.Final:create \
    -DprojectGroupId=org.camunda.platform.example \
    -DprojectArtifactId=camunda-k8s-example \
    -Dextensions="camunda,kubernetes,jdbc-postgresql,jib,resteasy" \
    -DplatformVersion="2.1.2.Final" \
    -DnoCode=true \
    -DbuildTool=gradle

This command creates a project with the following Quarkus Extensions:

  • camunda-bpm-quarkus-engine
  • quarkus-kubernetes
  • quarkus-container-image-jib
  • quarkus-jdbc-postgresql
  • quarkus-resteasy

Using code.quarkus.io

If you don't want to install Maven, you can download a basic Gradle project on code.quarkus.io. Make sure to add the Camunda Quarkus Engine Extension yourself:

$ cd ./camunda-k8s-example
$ ./gradlew addExtension --extensions="camunda"

Step 2: Configure the Datasource

The application.properties file located under ./camunda-k8s-example/src/main/resources/ configures the Quarkus application.

To configure the default datasource, add the following lines to the application.properties file:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=camunda
quarkus.datasource.password=camunda
quarkus.datasource.jdbc.url=jdbc:postgresql://postgres:5432/process-engine

In Step 6 of this guide, we will create a Postgres database with matching credentials, host, port, and database name with the help of a dedicated Kubernetes deployment.

Step 3: Deploy a Process Model

Download the process model process.bpmn (right-click & save) and move it into your project under ./camunda-k8s-example/src/main/resources. The process consists of a Service Task that calls the bean serviceDelegateBean. We define this bean in Step 5 of this guide.

Create a Java class org.camunda.platform.example.ProcessDeployer with the following content:

import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.quarkus.engine.extension.event.CamundaEngineStartupEvent;

import javax.enterprise.event.Observes;
import javax.inject.Inject;

public class ProcessDeployer {

  @Inject
  public RepositoryService repositoryService;

  // Method is called as soon as the Process Engine is running
  public void deployProcess(@Observes CamundaEngineStartupEvent startupEvent) {
    // Create a new deployment
    repositoryService.createDeployment()
        .addClasspathResource("process.bpmn") // Filename of the process model
        .enableDuplicateFiltering(true) // No redeployment when process model remains unchanged
        .deploy();
  }

}

As soon as the process engine is running, the Quarkus Extension emits a CamundaEngineStartupEvent. The ProcessDeployer#deployProcess method catches the event and calls the API of the process engine to deploy the process model stored in the process.bpmn file.

Step 4: Create a REST Endpoint to start a process

Create a Java class org.camunda.platform.example.StartProcessService with the following content:

import org.camunda.bpm.engine.RuntimeService;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/start-process")
public class StartProcessService {

  @Inject
  public RuntimeService runtimeService;

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String startProcessInstance() {
    String processInstanceId = runtimeService.startProcessInstanceByKey("process").getId();
    return "Process instance with id " + processInstanceId + " started!";
  }

}

The JAX-RS resource implements the GET /start-process REST API endpoint, which calls the process engine API to start a new process.

Step 5: Define a Java Delegate Bean

Create a Java class org.camunda.platform.example.ServiceDelegateBean with the following content:

import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.jboss.logging.Logger;

import javax.enterprise.context.Dependent;
import javax.inject.Named;

@Named
@Dependent
public class ServiceDelegateBean implements JavaDelegate {

  @Override
  public void execute(DelegateExecution execution) {
    Logger.getLogger(this.getClass())
        .infov("\n\nService Task called. Hurray!!");
  }

}

Whenever the engine executes the Service Task defined in the process, it calls the ServiceDelegateBean#execute method, which logs Service Task called. Hurray!! to the console.

Step 6: Create Kubernetes Objects

Next, let's create Kubernetes deployments and services for the Quarkus Application itself as well as for the Postgres database.

Quarkus Application Objects

The Quarkus extension quarkus-kubernetes allows us to create the object configurations for the Quarkus Application by running the following command:

$ ./gradlew build

You can find the generated configuration file under ./camunda-k8s-example/build/kubernetes/kubernetes.yml, which contains the definitions of the Service and Deployment objects.

In case the Kubernetes cluster runs locally on your machine, you need to set the imagePullPolicy of the deployment object to IfNotPresent. Otherwise, the cluster tries to pull the image from a remote registry.

Postgres Database Objects

To create the Kubernetes deployment and service objects for the Postgres database, add the following object definitions to the previously generated kubernetes.yml file:

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: postgres
  name: postgres
spec:
  ports:
    - port: 5432
  selector:
    app: postgres
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:latest
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: process-engine
            - name: POSTGRES_USER
              value: camunda
            - name: POSTGRES_PASSWORD
              value: camunda

Now, we are all set to apply the object definition configuration file to the Kubernetes cluster by running the following command:

$ kubectl apply -f build/kubernetes/kubernetes.yml

The command output should look as follows:

service/camunda-k8s-example created
deployment.apps/camunda-k8s-example created
service/postgres created
deployment.apps/postgres created

Step 7: Build the Container Image and Deploy it to Kubernetes

To create a container image and deploy it to Kubernetes, run the following command:

$ ./gradlew build -Dquarkus.container-image.build=true -Dquarkus.kubernetes.deploy=true

The quarkus.container-image.build flag is provided by the extension quarkus-container-image-jib which helps to build a container image based on your application. The quarkus.kubernetes.deploy flag is provided by the extension quarkus-kubernetes and deploys the image containing your application to the Kubernetes cluster.

Step 8: Start a process via REST API

To call the REST API endpoint GET /start-process from your local machine, you need to forward the port of the Kubernetes service to your host machine by running the following command:

$ kubectl port-forward svc/camunda-k8s-example 8080:80

Now, you can start a new process instance by opening the following URL in your browser: http://localhost:8080/start-process. It triggers the execution of the Java Delegate Bean defined via Service Task.

Step 9: Check Kubernetes Pod Logs

Let's have a look at the pod logs of our application deployment by running the following command:

$ kubectl logs -l app.kubernetes.io/name=camunda-k8s-example

When the process was executed successfully, you should see the following output:

Service Task called. Hurray!!

Now, it's your turn!

Now you know the first steps to get started with the Camunda Platform Process Engine and Quarkus. You can find the complete code of the example on GitHub. If you need help, you can ask your question in the forum.

We would be interested in your feedback as it helps us to iterate on the Camunda Quarkus Engine Extension. We have already collected some ideas on how we can improve the Camunda Quarkus integration. You can find a list of Feature Requests in our issue tracker. If your particular feature request is missing, you are welcome to create a new feature request.

We would also be happy to receive code contributions that you can submit as a pull request directly to our codebase on GitHub.