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
- Step 1: Initial Project Setup
- Step 2: Configure the Datasource
- Step 3: Deploy a Process Model
- Step 4: Create a REST Endpoint to start a process
- Step 5: Define a Java Delegate Bean
- Step 6: Create Kubernetes Objects
- Step 7: Build the Container Image and Deploy it to Kubernetes
- Step 8: Start a process via REST API
- Step 9: Check Kubernetes Pod Logs
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.