gravitonian

Activiti 7 Deep Dive Series - Deploying and Running a Business Process

Blog Post created by gravitonian Employee on Dec 10, 2018

Introduction

Activiti 7 is an evolution of the battle-tested Activiti workflow engine from Alfresco that is fully adopted to run in a cloud environment. It is built according to the Cloud Native application concepts and differs a bit from the previous Activiti versions in how it is architected. There is also a new Activiti Modeler that we will have a look at in a separate article.

 

The very core of the Activiti 7 engine is still very much the same as previous versions. However, it has been made much more narrowly focused to do one job and do it amazingly well, and that is to run BPMN business processes. The ancillary functions built into the Activiti runtime, include servicing API runtime request for Query and Audit data produced by the engine and stored in the Engine's database, have been moved out of the engine and made to operate as Spring Boot 2 microservices, each running in their own highly scalable containers.

 

The core libraries of the Activiti engine has also been re-architected for version 7, we will have a look at them in another article.

 

This article dives into how you can easily deploy and run your Activiti 7 applications in a cloud environment using Docker containers and Kubernetes. There are a lot of new technologies and concepts that are used with Activiti 7, so we will have a look at this first.

 

Activiti 7 Deep Dive Article Series 

This article is part of series of articles covering Activiti 7 in detail, they should be read in the order listed:

 

  1. Deploying and Running a Business Process - this article
  2. Using the Modeler to Design a Business Process
  3. Building, Deploying, and Running a Custom Business Process
  4. Using the Core Libraries

 

Prerequisites

  • You have Docker installed.

 

Concepts and Technologies

The following is a list of concepts (terms) and technologies that you will come in contact with when deploying and using the Activiti 7 product.

 

Virtual Machine Monitor (Hypervisor)

A Hypervisor is used to run other OS instances on your local host machine. Typically it's used to run a different OS on your machine, such as Windows on a Mac. When you run another OS on your host it is called a guest OS, and it runs in a so called Virtual Machine (VM).

 

Image

An image is a number of software layers that can be used to instantiate a container. It’s a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings. This could be, for example, Java + Apache Tomcat. You can find all kinds of Docker Images on the public repository called Docker Hub. There are also private image repositories (for things like commercial enterprise images), such as the one Alfresco uses called Quay.  

 

Container

An instance of an image is called a container. You have an image, which is a set of layers as described. If you start this image, you have a running container of this image. You can have many running containers of the same image.

 

Docker

Docker is one of the most popular container platforms. Docker provides functionality for deploying and running applications in containers based on images.

 

Docker Compose

When you have many containers making up your solution, such as with Activiti 7, and you need to configure each one of the containers so they work nicely together, then you need a tool for this.

 

Docker Compose is such a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

 

Dockerfile

A Dockerfile is a script containing a successive series of instructions, directions, and commands which are to be executed to form a new Docker image. Each command executed translates to a new layer in the image, forming the end product. They replace the process of doing everything manually and repeatedly. When a Dockerfile is finished executing, you end up having built a new image, which then you use to start a new Docker container.

 

Difference between Containers and Virtual Machines

It is important to understand the difference between using containers and using VMs. Here is a picture that illustrates:

 

 

The main difference is that when you are running a container you are not kicking off a complete new OS instance. And this makes containers much more lightweight and quicker to start. A container is also taking up much less space on your hard-disk as it does not have to ship the whole OS.

 

For more info read What is a Container | Docker.

 

Cluster

A cluster forms a shared computing environment made up of servers (nodes with one or more containers), where resources have been clustered together to support the workloads and processes running within the cluster:

 

Kubernetes

Docker is great for running containers on one host, and provides all required functionality for that purpose. But in today’s distributed services environment, the real challenge is to manage resources and workloads across servers and complex infrastructures.

The most used and supported tool for this today is Kubernetes (a Greek word for “helmsman” or “pilot”), which was originally created by Google and then made open source. As the word suggest, Kubernetes undertakes the cumbersome task to orchestrate containers across many nodes, utilizing many useful features. Kubernetes is an open source platform that can be used to run, scale, and operate application containers in a cluster.

Kubernetes consists of several architectural components:

  • Kubernetes Node - a cluster node running one or more containers
    • Kube Proxy - the Kubernetes network proxy abstracts the service access points, can route to other nodes.
    • Kubelet - the kubelet is the primary node-agent. Makes sure the containers are running properly as per specification in the pod template.
    • Pods - this is the smallest unit in the Kubernetes Object Model (see next section) that can be deployed. It represents a running process in the cluster. A pod encapsulates an application container and Docker is the most used container runtime.
    • Labels - metadata that's attached to Kubernetes objects, including pods.
  • Kubernetes Master
    • API Server -  frontend to the cluster's shared state. Provides a ReST API that all components uses.
    • Replication controllers - regulates the state of the cluster. Creates new pod "replicas" from a pod template to ensure that a configurable number of pods are running.
    • Scheduler - is managing the workload by watching newly created pods that have no node assigned, and selects a node for them to run on.
  • Kubectl - To control and manage the Kubernetes cluster from the command line we will use a tool called kubectl. It talks to the API Server in the Kubernetes Master Server, which in turn talks to the individual Kubernetes nodes.
  • Services offer a low-overhead way to route requests to a logical set of pod back ends in the cluster, using label-driven selectors.

 

The following picture illustrates:

 

 

You might also have heard of Docker Swarm, which is similar to Kubernetes.

 

Kubernetes Objects

When working with Kubernetes different kind of things will be created, deployed, managed, and destroyed. We can call these things objects. There are a number of different types of objects in the Kubernetes Object Model that are good to know about. The following picture illustrates some of these objects that you are likely to come across:

 

 

Some of these objects are already familiar, here is a list explaining the rest:

 

  • Container - our application is deployed with a Docker container.
  • Volume - our application can store data via a volume that points to physical storage.
  • Pod - contains one or more containers and it is short lived, it is not guaranteed to be constantly up.
  • Replica Set - manages a set of replicated pods. Making sure the correct numbers of replicas are always running.
  • Deployment - pods are scheduled using deployments, which provides replica management, scaling, rolling updates, rollback, clean-up etc.
  • Service - defines a set of pods that provide a micro-service. Provides a stable virtual endpoint for the ephemeral (short lived) pods in the cluster.
  • Ingress - public access point for one or more services. Ingress is the built‑in Kubernetes load‑balancing framework for HTTP traffic. With Ingress, you control the routing of external traffic.
  • Secret - contains access tokens and provides access to, for example, private images.
  • Config Map - name and value pair property configuration of application.
  • Namespace - all object names in one namespace cannot clash with object names in another namespace. The use of Namespace provides complete isolation and ideally suited for addressing multi-tenancy requirements.

 

Minikube

Minikube is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster inside a VM on your laptop. It's primarily for users looking to try out Kubernetes, or to use as a development environment.

 

We will be using the newest Docker for Desktop environment, which comes with Kubernetes, so no need to install Minikube.

 

Helm

So we have a Kubernetes cluster up and running, and it is now ready for container deployments. How do we handle this efficiently? We might have a whole lot of containers for things such as database layer, application layer, web layer, search layer, etc. And they should have different configurations for scalability and failover. Sounds quite complex.

There is help however in the form of a tool called Helm. It is a package manager for Kubernetes clusters. With it we can specify exactly how, for example, the database layer deployment should look like, 3 containers with MySQL, 2 standby failover containers, autoscale like this etc. A Helm package definition is referred to as a Helm Chart. Each chart is a collection of files that lists, or defines:

 

  • The Docker container images that should be deployed (links to them).
  • The configuration of the components.
  • And the configuration of the infrastructure that the solution uses.

 

Other important Helm concepts:

 

  • Release - an instance of a chart loaded into Kubernetes.
  • Helm Repository - a repository of published charts. There is a public repo at kubernetes.io but organisations can have their own repos. Alfresco has its own chart repo for example.
  • Template - a Kubernetes configuration file written with the Go templating language (with addition of Sprig functions)

 

Helm Charts are stored in a Helm repository, much like JARs are stored in for example Maven Central. So Helm is actually built up of both a Client bit and a Server bit. The server bit of Helm is called Tiller and runs in the Kubernetes cluster. The Helm architecture looks something like this:

 

 

Helm is not only a package manager, it is also a deployment manager that can:

 

  • Do repeatable deployments
  • Manage dependencies: reuse and share
  • Manage multiple configurations
  • Update, rollback, and test application deployments (referred to as releases)

 

The Activiti 7 solution is packaged with Helm and Activiti provides a Helm repository with Charts related to the Activiti 7 components.

 

Cloud Native Applications

In order to understand how Activiti 7 is architected, built, and runs it is useful to know a bit about Cloud Native applications. Let’s try and explain by an example. About 10 years ago Netflix was thinking about a future where customers could just click a button and watch a movie online. There would be no more DVDs. Sending around DVDs to people just don’t scale globally, you cannot watch what you want when you want where you want, there is only so many DVDs you can send via snail mail, DVDs could be damaged etc. Netflix also had limited ways of recommending new movies to customers in an efficient way.

 

To implement this new online streaming service Netflix would have to create a new type of online service that would be:

 

  • Web-scale - everything is distributed, both services and compute power. The system is self-healing with fault isolation and distributed recovery. There would be API driven automation. Multiple applications running simultaneously.
  • Global - the movie service would have to be available on a global scale instantly, wherever you are.
  • Highly-available - when you click a button to see a movie it just works, otherwise clients would not adopt the service
  • Consumer-facing - the online service would have to be directly facing the customers in their homes.

 

What Netflix really wanted was speed and access at scale. The movie site needed to be always on, always available, with pretty much no downtime. As a consumer you would not accept that you could not watch a movie because of technical problems. They also knew that they would have to change their product continuously while it was running to add new features based on consumer needs. Basically they would have to get better and better, faster and faster. And this is key to Cloud Native applications.

 

So what is it with Cloud Native that enables you provide an application that appears to be always online and that can have new features added and delivered continuously? The following are some of the characteristics of Cloud Native applications:

 

  • Modularity - the applications can no longer be monoliths where all the functionality is baked into one massive application. Each application function need to be delivered as an independent service, referred to as microservice. In the case of Netflix they would see a transition from a traditional development model with 100 engineers producing a monolithic DVD‑rental application to a microservices architecture with many small teams responsible for the end‑to‑end development of hundreds of microservices that work together to stream digital entertainment to millions of Netflix customers every day.
  • Observability - with all these microservices it is important to constantly monitor them to detect problems and then instantly fire up new instances of the services, so it appears as if the whole application is always working.
  • Deployability - delivering the application as a number of microservices enable you to deploy these small services quickly and continuously. It also means you can upgrade and do maintenance on different parts of the application independently. The services are also deployed as Docker containers, which means that the OS is abstracted.
  • Testability - when doing continuous delivery it is important that all tests are automated, so we can deliver new features as quickly as possible.
  • Disposability - it should be very easy to get rid of feature or function in an application. This is easy if all the major functionality is independently delivered as microservices.
  • Replaceability - how easy can we replace the features that make up the application. If it is easy to dispose of features, and replace them with new ones, then the application can be very flexible to new requirements from customers.

 

So what’s the main differences compared to traditional application development:

 

  • OS abstraction - the different microservices are delivered and deployed as Docker containers, which means we don’t have to worry about what OS we need and other dependent libraries, which is often the case with traditional applications.
  • Predictable - we can adhere to a contract and have predictability on how fast and reliable we can deliver a service. With one big monolith it can be difficult to predict when a new feature can be available.
  • Right size capability - we can scale the individual services up and down depending on the load on each one of them. With traditional applications it is common to oversize the deployment to cater for “future” peak loads.
  • Continuous Delivery (CD) - we will be able to make software updates as soon as they are ready as we can depend on our automated tests to quickly spot any regressions. With one big monolith you can’t easily update one feature as you have to test the whole application before you can deliver the feature update. And there is usually very low automated test coverage.
  • Automated scaling: we can scale the whole application very efficiently and automatically depend on requirements and load. With traditional applications you would often see overscaling and most of the scaling would have to be done manually.
  • Rapid recovery - how quickly can we recover from failure. With sophisticated monitoring of the individual microservices we can easily detect faults and recover quickly. The application will also be more resistant to complete downtime as it is often possible to continue servicing customers if just one or a few of the microservices are down momentarily. With one monolith application it can take hours to figure out which part of the application the problem reside in, resulting in longer downtimes.

 

So, can anyone build these kind of solutions today, or does it require special knowledge and long experience? Yes you can develop Cloud Native apps, if you have read up on the concepts around Cloud Native applications, and you know about some of the more prominent frameworks supporting it, then you can definitely do it.

 

Here are some of the building blocks of Cloud Native Applications:

 

  • Service Registry
  • Distributed Configuration Service
  • Distributed Messaging (Streams)
  • Distributed logging and monitoring
  • Gateway
  • Circuit Breakers, Bulkheads, Fallbacks, Feign
  • Contracts

 

A lot of these features can be found in the Spring Cloud framework, which provides a number of tools that can be used to build Cloud Native applications:

 

 

A typical architecture for a cloud native solution looks like this:

 

 

Installing and enabling necessary software

This section walks through how to install and enable the required software, specifically in regards to Kubernetes.

 

Checking Docker Version

As mentioned in the beginning, you need to have Docker installer. But that’s not all, you need to have a Docker version that comes with Kubernetes. You can check this by looking at the About dialog for your Docker installation, you should see something like this:

 

 

The dialog should list Kubernetes as a supported software (i.e. in the bottom right corner). This means that we don’t need to install Kubernetes locally with for example Minikube, we are ready to deploy stuff into a Kubernetes cluster as it is provided by our Docker installation.

 

These local Kubernetes deployments will mirror exactly how a production deployment would look like in for example AWS, which is good.

 

Enabling Kubernetes

Docker for Desktop comes with Kubernetes but we still need to enable it. To do this go into Docker Preferences..., then click on the Kubernetes tab, you should see the following:

 

 

Click on Enable Kubernetes followed by the Kubernetes radio button. It runs Swarm by default if we don't specifically select Kubernetes. Click Apply to install a Kubernetes cluster. You should see the following config dialog after a couple of minutes:

 

 

This means we are ready to deploy stuff into Kubernetes.

 

Now check that you have the correct kubectl context. I have been running minikube on my Mac so I don’t have the correct context:

 

$ kubectl config current-context

minikube

 

To change to Docker for Desktop context use:

 

$ kubectl config use-context docker-for-desktop

Switched to context "docker-for-desktop".

 

Check again:

 

$ kubectl config current-context

docker-for-desktop

 

Now check the kubectl version (no need to install kubectl, it comes with Docker):

 

$ kubectl version

Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.2", GitCommit:"5fa2db2bd46ac79e5e00a4e6ed24191080aa463b", GitTreeState:"clean", BuildDate:"2018-01-18T10:09:24Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"darwin/amd64"}

 

Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-21T09:05:37Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}

 

You might have noticed that my server and client versions are different. I am using kubectl from a previous manual installation and the server is from the Docker for Desktop installation.

 

Let’s get some information about the Kubernetes cluster:

 

$ kubectl cluster-info

Kubernetes master is running at https://localhost:6443

KubeDNS is running at https://localhost:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

 

And let’s check out the nodes we got in the cluster:

 

$ kubectl get nodes

NAME                 STATUS ROLES AGE       VERSION

docker-for-desktop   Ready master 1h        v1.10.3

 

We can also find out what PODs we got in the Kubernetes system with the following command:

 

$ kubectl get pods --namespace=kube-system

NAME                                         READY STATUS RESTARTS AGE

etcd-docker-for-desktop                      1/1 Running 0 1h

kube-apiserver-docker-for-desktop            1/1 Running 0 1h

kube-controller-manager-docker-for-desktop   1/1 Running 0 1h

kube-dns-86f4d74b45-4jw6f                    3/3 Running 0 1h

kube-proxy-zwwpx                             1/1 Running 0 1h

kube-scheduler-docker-for-desktop            1/1 Running 0 1h

 

Configure memory for Docker and Kubernetes

The applications that we are going to deploy will most likely require more memory than is allocated by default. I am updating my setting from 4GB to 7GB:

 

 

Updating the memory settings will restart Docker and Kubernetes so you will have to wait a bit before you proceed with the next step.

 

Installing the Kubernetes Dashboard

What we don’t see here is a Kubernetes Dashboard POD, which is a Webapp that is really handy to have around when working with Kubernetes. We can use the Kubernetes Dashboard YAML that is available here and submit the same to the Kubernetes Master as follows:

 

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

secret "kubernetes-dashboard-certs" created

serviceaccount "kubernetes-dashboard" created

role "kubernetes-dashboard-minimal" created

rolebinding "kubernetes-dashboard-minimal" created

deployment "kubernetes-dashboard" created

service "kubernetes-dashboard" created

 

Check the PODs again:

 

kubectl get pods --namespace=kube-system

NAME                                         READY STATUS RESTARTS AGE

etcd-docker-for-desktop                      1/1 Running 0 1h

kube-apiserver-docker-for-desktop            1/1 Running 0 1h

kube-controller-manager-docker-for-desktop   1/1 Running 0 1h

kube-dns-86f4d74b45-4jw6f                    3/3 Running 0 1h

kube-proxy-zwwpx                             1/1 Running 0 1h

kube-scheduler-docker-for-desktop            1/1 Running 0 1h

kubernetes-dashboard-7b9c7bc8c9-d7dcz        0/1 ContainerCreating 0 39s

 

Wait for the Dashboard POD to load and you see STATUS as Running. It could take some time to change from ContainerCreating to Running, so be patient. Once it’s in running state, then you can set up a forwarding port to that specific Pod. We are going to do this in a more permanent way by defining a NodePort type of Service, so create a YAML file called k8s-dashboard-nodeport-service.yaml with the following content:

 

apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-nodeport
namespace: kube-system
spec:
ports:
- port: 8443
protocol: TCP
targetPort: 8443
nodePort: 31234
selector:
k8s-app: kubernetes-dashboard
sessionAffinity: None
type: NodePort

Note the nodePort property value 31234, which is the external port that the Kubernetes Dashboard will be available at (range is 30000-32767).

 

Create the service as follows:

 

$ kubectl apply -f k8s-dashboard-nodeport-service.yaml

service "kubernetes-dashboard-nodeport" created

 

Now access the Dashboard from a browser via the https://localhost:31234/#!/login URL. You will get some warnings but do proceed until you see the following dialog (if you get a 401 not authenticated error you can get to this dialog from the drop down menu in the upper right corner of the dashboard):

 

Now, you need a token to login. You can get that by executing the following command:

 

$ kubectl describe secret kubernetes-dashboard --namespace=kube-system

 

.

...

token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC10b2tlbi1iYmRtcyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImNmNDA3Y2EzLTI4NGYtMTFlOS1iZjRjLTAyNTAwMDAwMDAwMSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlcm5ldGVzLWRhc2hib2FyZCJ9.UY79izUpwp5ISaVjJFgWDTS8_dLgkFVZOIZ7uZSw2nlA9Cjp3Mh0YOGGgquYGBou0swfDAry186As7XIi1nQQaKAl9vksKQUfI5EFKHKCopKvDCettKdOgTHpCvpc25f-i4Qaf9d9TNMttsUn-jwBrXia0RxPzOe0Y3tIi6eyVDprnxyQt9tkYm2KMpqvQ_ofHbFmJSIDcY_fQXmiupFyqhao6RrGqSVVPhOg4b77vAAryL9swqlGDd22wTXXmuE1E7s7y5CWyqTlzDzGvNq9AHfjCY8HgZX-t4Xd4ukK8mAd-XBbB8mSn-elE2MSie5ZsJR3GUYSofKPk7JfkfTQQ

 

Click on the Token radio button in the dialog and then copy and paste the token in the field below. Then click the SIGN IN button, this should lead to the Dashboard as shown below:

 

 

Click on Nodes and you will see the single Kubernetes node as follows:

 

 

Installing Helm

The Helm package manager will be used to deploy container solutions, such as Activiti 7, to the Kubernetes cluster. It consist of both a client and a server. Find the appropriate installation package for your OS here.

I installed the Helm Client on my Mac using the following steps:

 

  1. Downloaded helm-v2.8.0-darwin-amd64.tar.gz.
  2. $ tar -zxvf helm-v2.8.0-darwin-amd64.tar.gz
  3. $ sudo mv darwin-amd64/helm /usr/local/bin/helm

 

Tiller, the server portion of Helm, typically runs inside of your Kubernetes cluster. The easiest way to install tiller into the cluster is simply to run helm init. This will validate that helm’s local environment is set up correctly (and set it up if necessary). Then it will connect to whatever cluster kubectl connects to by default. Once it connects, it will install tiller into the kube-system namespace.

 

I did an in-cluster installation as follows:

 

$ helm init

$HELM_HOME has been configured at /Users/mbergljung/.helm.

 

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Happy Helming!

 

To see Tiller running do:

 

$ kubectl get pods --namespace kube-system

NAME                                         READY STATUS RESTARTS AGE

etcd-docker-for-desktop                      1/1 Running 0 2d

kube-apiserver-docker-for-desktop            1/1 Running 0 2d

kube-controller-manager-docker-for-desktop   1/1 Running 0 2d

kube-dns-86f4d74b45-4jw6f                    3/3 Running 0 2d

kube-proxy-zwwpx                             1/1 Running 0 2d

kube-scheduler-docker-for-desktop            1/1 Running 0 2d

kubernetes-dashboard-7b9c7bc8c9-xq6qf        1/1 Running 0 55m

tiller-deploy-664858687b-hrtkw               1/1 Running 0 58s

 

Adding the Activiti 7 Helm Repository

Add the Activiti Helm Repository so we can pull Activiti 7 Charts and deploy Activiti 7 applications.

 

$ helm repo add activiti-cloud-charts https://activiti.github.io/activiti-cloud-charts/

"activiti-cloud-charts" has been added to your repositories

 

List the helm repositories that you have access to like this:

 

$ helm repo list

NAME                  URL                                              

stable                https://kubernetes-charts.storage.googleapis.com

local                 http://127.0.0.1:8879/charts                     

activiti-cloud-charts https://activiti.github.io/activiti-cloud-charts/

    

To see what charts are available do a search like this:

 

$ helm search

NAME                                               CHART VERSION      APP VERSION                  DESCRIPTION                                       

activiti-cloud-charts/activiti-cloud-audit         0.2.0                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/activiti-cloud-connector     0.2.0                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/activiti-cloud-demo-ui       0.1.4                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/activiti-cloud-full-example 0.2.1              1.0                          An Activiti Helm chart for Kubernetes             

activiti-cloud-charts/activiti-cloud-gateway       0.2.0                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/activiti-cloud-query         0.2.0                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/activiti-keycloak            0.1.4              1.0                          A Helm chart for Kubernetes                       

activiti-cloud-charts/application                  0.2.0              1.0                          A Helm chart for Kubernetes                       

activiti-cloud-charts/example-runtime-bundle       0.1.0                                          A Helm chart for Kubernetes                       

activiti-cloud-charts/infrastructure               0.2.0              1.0                          A Helm chart for Kubernetes                       

activiti-cloud-charts/runtime-bundle               0.2.0                                          A Helm chart for Kubernetes         

...     

 

We can see in the above listing that we have access to all the Activiti 7 Cloud app charts, including the activiti-cloud-full-example that we will deploy in a bit.

 

Activiti 7 Overview

Activiti 7 is implemented as a Cloud Native application and its main abstraction layer against different cloud services, such as message queues and service registry, is the Spring Cloud framework. By using it the Activiti team does not have to reinvent and come up with new abstraction layers for cloud services.

 

To support scaling globally the Activiti team has chosen Kubernetes container orchestration engine. Kubernetes is supported by the main cloud providers, such as Amazon and Google.

 

The Activiti 7 infrastructure can be described with the following picture:

 

 

The different building blocks in the infrastructure can be explained as follows:

 

  • Activiti Keycloak - SSO - Single Sign On to all services
  • Activiti Keycloak - IDM - Identity Management so Activiti knows what the organizational structure looks like and who the groups and users are, and who are allowed to do what.
  • Activiti Modeler - BPMN 2.0 modelling application where developers can define new process applications with one or more process definitions.
  • API Gateway - Gives user interfaces and other systems access to the process applications
  • Service Registry - a service registry that allows all the services and applications to register with it and be discovered in a dynamic way.
  • Config Server - centralized and distributed configuration service that can be used by all other services.
  • Zipkin - is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in microservice architectures.
  • Activiti Applications - each application represents one or more business process implementations. So one application could be an HR process for job applications while another application could be used for loan applications. Cloud connectors are used to make calls outside of the business process.

 

More specifically, An Activiti 7 Application consists of the following services:

 

  • Runtime Bundles - will provide different runtimes for different business models. The first available runtime is for business processes. The Process Runtime would be comparable to what was previously referred to as the Activiti Process Engine. A runtime will be as small as possible and efficient as possible. A Runtime Bundle contains a number of process definitions and is immutable. This means that it will run just a number of immutable process definitions (instances).
  • Query Service - is used to aggregate data and makes it possible to consume the data in an efficient way. There might be multiple Runtime Bundles inside an Activiti application and the Query Service will aggregate data from each of those.
  • Notification Service - can provide notifications about what is happening in the different Runtime Bundles.
  • Audit Service - this is the standard audit log that you have in BPM systems, it provides a log of what exactly happened when a process was executed.
  • Cloud Connectors - are about System - to - System interaction. Instead of having all code that talks to external systems in the process definition implementation and the process runtime, it’s now decoupled and implemented as separate services with separate SLAs. A Service Task would typically be implemented as a Cloud Connector.

 

Deploying and Running a Business Process

This section will show you how to deploy and run a business process with the Activiti 7 product using Helm and Kubernetes. We will not develop any new processes, just get a feel for how Activiti 7 works by using a provided example. Note that Activiti 7 does not come with a UI, which means you will have to interact with the system via the ReST API.

 

To do this we will use one of the examples that are available out-of-the-box. It’s called the Activiti 7 Full Example and it includes all the building blocks that conforms an Activiti 7 application, such as:

 

  • API Gateway (Spring Cloud)
  • SSO/IDM (Keycloak)
  • Activiti 7 Runtime Bundle (Example)
  • Activiti 7 Cloud Connector (Example)
  • Activiti 7 Query Service
  • Activiti 7 Audit Service

 

The following picture illustrates:

 

 

The actual example process definition is contained in what's called a Runtime Bundle. The Service Task implementation(s) and listener implementation(s) is contained in what’s called a Cloud Connector. The Client in this case will just be a Postman Collection that will be used to interact with the services. Activiti 7 Beta currently doesn’t have a Process and Task management user interface.

 

The new Activiti Modeler application can be deployed at the same time as the rest of the example, but you need to enable it manually as it is not available by default.

 

Create a Kubernetes namespace for the Activiti 7 App Deployments

We are going to create a separate namespace for the Activiti 7 application deployments. This means that any name we use inside this namespace will not clash with the same name in other namespaces.

 

$ kubectl create namespace activiti7

namespace "activiti7" created

 

Check what namespaces we have now:

 

$ kubectl get namespaces

NAME          STATUS AGE

activiti7     Active 43s

default       Active 2d

docker        Active 2d

kube-public   Active 2d

kube-system   Active 2d

 

Installing an Ingress Controller to Expose Services Externally

Before we start installing Activiti 7 components we need to install an Ingress, Front-End, Proxy, Gateway, or whatever you want to call it that can be used to expose all the Activiti 7 Kubernetes Services externally. The Activiti example installation process is simpler if services are exposed with a wildcard DNS and the DNS is mapped to an Ingress in advance of the installation.

 

Before we install the Ingress it’s always a good idea to update the local helm chart repo/cache, so we are not using an older version of the chart:

 

$ helm repo update

Hang tight while we grab the latest from your chart repositories...

...Skip local chart repository

...Successfully got an update from the "alfresco-incubator" chart repository

...Successfully got an update from the "activiti-cloud-charts" chart repository

...Successfully got an update from the "stable" chart repository

Update Complete. ⎈ Happy Helming!⎈

 

To install the Ingress we install something called a Kubernetes Ingress Controller, which will automatically create routes to the internal services that we want to expose. Run the following command:

 

helm install stable/nginx-ingress --namespace=activiti7

NAME:   intent-deer

E1119 07:45:11.270800   52554 portforward.go:303] error copying from remote stream to local connection: readfrom tcp4 127.0.0.1:61100->127.0.0.1:61103: write tcp4 127.0.0.1:61100->127.0.0.1:61103: write: broken pipe

LAST DEPLOYED: Mon Nov 19 07:45:10 2018

NAMESPACE: activiti7

STATUS: DEPLOYED

 

RESOURCES:

==> v1/ConfigMap

NAME                                  DATA AGE

intent-deer-nginx-ingress-controller  1 0s

 

==> v1/ServiceAccount

NAME                       SECRETS AGE

intent-deer-nginx-ingress  1 0s

 

==> v1beta1/ClusterRole

NAME                       AGE

intent-deer-nginx-ingress  0s

 

==> v1beta1/PodDisruptionBudget

NAME                                       MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS  AGE

intent-deer-nginx-ingress-controller       1 N/A 0          0s

intent-deer-nginx-ingress-default-backend  1 N/A 0          0s

 

==> v1/Pod(related)

NAME                                                        READY STATUS RESTARTS AGE

intent-deer-nginx-ingress-controller-9b5fb9b4f-4985v        0/1 ContainerCreating 0 0s

intent-deer-nginx-ingress-default-backend-6f7884d594-85dph  0/1 ContainerCreating 0 0s

 

==> v1beta1/ClusterRoleBinding

NAME                       AGE

intent-deer-nginx-ingress  0s

 

==> v1beta1/Role

NAME                       AGE

intent-deer-nginx-ingress  0s

 

==> v1beta1/RoleBinding

NAME                       AGE

intent-deer-nginx-ingress  0s

 

==> v1/Service

NAME                                       TYPE CLUSTER-IP EXTERNAL-IP PORT(S)                     AGE

intent-deer-nginx-ingress-controller       LoadBalancer 10.98.10.209 <pending> 80:30006/TCP,443:30107/TCP  0s

intent-deer-nginx-ingress-default-backend  ClusterIP 10.111.24.254 <none> 80/TCP                      0s

 

==> v1beta1/Deployment

NAME                                       DESIRED CURRENT UP-TO-DATE AVAILABLE AGE

intent-deer-nginx-ingress-controller       1 1 1 0 0s

intent-deer-nginx-ingress-default-backend  1 1 1 0 0s

 

NOTES:

The nginx-ingress controller has been installed.

It may take a few minutes for the LoadBalancer IP to be available.

You can watch the status by running 'kubectl --namespace activiti7 get services -o wide -w intent-deer-nginx-ingress-controller'

 

An example Ingress that makes use of the controller:

 

 apiVersion: extensions/v1beta1

 kind: Ingress

 metadata:

   annotations:

     kubernetes.io/ingress.class: nginx

   name: example

   namespace: foo

 spec:

   rules:

     - host: www.example.com

       http:

         paths:

           - backend:

               serviceName: exampleService

               servicePort: 80

             path: /

   # This section is only required if TLS is to be enabled for the Ingress

   tls:

       - hosts:

           - www.example.com

         secretName: example-tls

 

If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:

 

 apiVersion: v1

 kind: Secret

 metadata:

   name: example-tls

   namespace: foo

 data:

   tls.crt: <base64 encoded cert>

   tls.key: <base64 encoded key>

 type: kubernetes.io/tls

 

With the Ingress Controller created the services can then later on be accessed from outside the cluster. To access anything via the Ingress Controller we need to find out its IP address:

 

$ kubectl get services --namespace=activiti7

NAME                                        TYPE CLUSTER-IP EXTERNAL-IP PORT(S)                      AGE

intent-deer-nginx-ingress-controller        LoadBalancer 10.98.10.209 localhost     80:30006/TCP,443:30107/TCP   35m

intent-deer-nginx-ingress-default-backend   ClusterIP 10.111.24.254 <none> 80/TCP                       35m

 

In this case the external IP address is localhost, which means IP 127.0.0.1. We cannot use localhost as that would not work both inside and outside Docker containers. We will use the host’s IP and the public nip.io service for DNS.

 

Note that you might need to run kubectl get services... several times until you can see the EXTERNAL-IP for your Ingress Controller. If you see PENDING, wait for a few seconds and run the command again.

 

Clone the Activiti 7 Helm Charts source code

We will need to do a few changes in the Helm Chart for the Full Example so let’s clone the source code as follows:

 

$ git clone https://github.com/Activiti/activiti-cloud-charts

Cloning into 'activiti-cloud-charts'...

 

This clones the Helm Chart source code for all Activiti 7 examples.

 

Configure the Full Example

The next step is to parameterize the Full Example deployment. The Helm Chart can be customized to turn on and off different features in the Full Example, but there is one mandatory parameter that needs to be provided, which is the external IP address for the Ingress Controller that is going to be used by this installation.

 

When we are running locally we need to find out the current IP address. We cannot use localhost (127.0.0.1) as then containers cannot talk to each other, such as the Runtime Bundle container talking to the Keycloak container. So, for example, find the IP in the following way:

 

$ hostname

MBP512-MBERGLJUNG-0917.local

MBP512-MBERGLJUNG-0917:activiti-cloud-full-example mbergljung$ ping MBP512-MBERGLJUNG-0917.local

PING mbp512-mbergljung-0917.local (10.244.50.42): 56 data bytes

64 bytes from 10.244.50.42: icmp_seq=0 ttl=64 time=0.068 ms

Make sure this is not a bridge IP address. Look for something like en0 when doing ifconfig.

The custom configuration is done in the values.yaml file located here: https://github.com/Activiti/activiti-cloud-charts/blob/master/activiti-cloud-full-example/values.yaml (you can copy this file or change it directly). We can update this file in the Helm Chart source code that we cloned previously.

 

Open up the activiti-cloud-charts/activiti-cloud-full-example/values.yaml file and replace the string REPLACEME with 10.244.50.42.nip.io, which is the EXTERNAL IP of the Ingress Controller. We are using nip.io as DNS service to map our services to this External IP which will follow the following format: <EXTERNAL_IP>.nip.io.

Important! Make sure that you can ping 10.244.50.42.nip.io:

 

$ ping 10.244.50.42.nip.io

PING 10.244.50.42.nip.io (10.244.50.42): 56 data bytes

64 bytes from 10.244.50.42: icmp_seq=0 ttl=64 time=0.041 ms

64 bytes from 10.244.50.42: icmp_seq=1 ttl=64 time=0.083 ms

...

 

If this does not work then you might experience a DNS rebind protection problem with your router. See more info here.

As mentioned, the Activiti BPMN Modeler application needs to be enabled manually. So change the following value to true:

 

activiti-cloud-modeling:
enabled: true

You should end up with a file looking something like this (note. I removed all the stuff that were commented out):

 

global:
keycloak:
url: "http://activiti-keycloak.10.244.50.42.nip.io/auth"
gateway:
host: &gatewayhost "activiti-cloud-gateway.10.244.50.42.nip.io"

activiti-cloud-modeling:
enabled: true

application:
runtime-bundle:
enabled: true
image:
pullPolicy: Always

activiti-cloud-query:
image:
pullPolicy: Always

activiti-cloud-connector:
enabled: true
image:
pullPolicy: Always

activiti-cloud-audit:
image:
pullPolicy: Always

infrastructure:
activiti-keycloak:
keycloak:
enabled: true
keycloak:
ingress:
enabled: true
path: /
proxyBufferSize: "16k"
hosts:
- "activiti-keycloak.10.244.50.42.nip.io"
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers 'Access-Control-Allow-Methods: "POST, GET, OPTIONS, PUT, PATCH, DELETE"';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization"';
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
preStartScript: |
/opt/jboss/keycloak/bin/add-user.sh -u admin -p admin
/opt/jboss/keycloak/bin/add-user-keycloak.sh -r master -u admin -p admin
cp /realm/activiti-realm.json .
sed -i 's/placeholder.com/*/g' activiti-realm.json

activiti-cloud-gateway:
ingress:
enabled: true
hostName: *gatewayhost
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/enable-cors: true
nginx.ingress.kubernetes.io/cors-allow-headers: "*"
nginx.ingress.kubernetes.io/x-forwarded-prefix: true

 

So what are we going to deploy with this configuration? The following picture illustrates:

 

 

We can see here that everything is accessed via the Ingress controller at 10.244.50.42.nip.io. Then you just add the service name in front of that to get to the API Gateway and the Keycloak service.

 

Deploy the Full Example

When we have customized the configuration of the Full Example Helm Chart we are ready to deploy the chart by running the following command. However, before doing that it’s always a good idea to update the local helm chart repo/cache, so we are not using an older version of the chart:

 

$ helm repo update

Hang tight while we grab the latest from your chart repositories...

...Skip local chart repository

...Successfully got an update from the "alfresco-incubator" chart repository

...Successfully got an update from the "activiti-cloud-charts" chart repository

...Successfully got an update from the "stable" chart repository

Update Complete. ⎈ Happy Helming!⎈

 

Now, remember to stand in the correct directory locally where you made the changes to the values.yaml file, then do the installation as follows:

 

activiti-cloud-charts mbergljung$ cd activiti-cloud-full-example/

activiti-cloud-full-example mbergljung$ helm install -f values.yaml activiti-cloud-charts/activiti-cloud-full-example --namespace=activiti7

NAME:   ignorant-bunny

LAST DEPLOYED: Mon Nov 19 09:41:30 2018

NAMESPACE: activiti7

STATUS: DEPLOYED

 

RESOURCES:

==> v1/ConfigMap

NAME                       DATA AGE

ignorant-bunny-rabbitmq    2 2s

ignorant-bunny-keycl       2 2s

ignorant-bunny-keycl-test  1 2s

 

==> v1beta1/Role

NAME                     AGE

ignorant-bunny-rabbitmq  2s

 

==> v1/Service

NAME                               TYPE CLUSTER-IP EXTERNAL-IP  PORT(S)         AGE

activiti-cloud-modeling-backend    ClusterIP 10.102.61.115 <none>     80/TCP         2s

activiti-cloud-modeling            ClusterIP 10.99.1.27 <none>     80/TCP         2s

audit                              ClusterIP 10.99.4.236 <none>     80/TCP         1s

example-cloud-connector            ClusterIP 10.111.163.56 <none>     80/TCP         1s

query                              ClusterIP 10.108.106.152 <none>     80/TCP         1s

ignorant-bunny-rabbitmq-discovery  ClusterIP None <none>     15672/TCP,5672/TCP,4369/TCP,61613/TCP,61614/TCP  1s

ignorant-bunny-rabbitmq            ClusterIP None <none>     15672/TCP,5672/TCP,4369/TCP,61613/TCP,61614/TCP  1s

rb-my-app                          ClusterIP 10.108.116.9 <none>     80/TCP         1s

activiti-cloud-gateway             ClusterIP 10.104.143.117 <none>     80/TCP         1s

ignorant-bunny-keycl-headless      ClusterIP None <none>     80/TCP         1s

ignorant-bunny-keycl-http          ClusterIP 10.96.152.246 <none>     80/TCP         1s

 

==> v1beta1/Ingress

NAME                                   HOSTS     ADDRESS PORTS AGE

ignorant-bunny-activiti-cloud-gateway  activiti-cloud-gateway.10.244.50.42.nip.io  80 1s

ignorant-bunny-keycl                   activiti-keycloak.10.244.50.42.nip.io     80 1s

 

==> v1beta1/Deployment

NAME                                     DESIRED CURRENT UP-TO-DATE AVAILABLE AGE

ignorant-bunny-activiti-cloud-modeling   1 1 1 0 1s

ignorant-bunny-activiti-cloud-audit      1 1 1 0 1s

ignorant-bunny-activiti-cloud-connector  1 1 1 0 1s

ignorant-bunny-activiti-cloud-query      1 1 1 0 1s

ignorant-bunny-runtime-bundle            1 1 1 0 1s

ignorant-bunny-activiti-cloud-gateway    1 1 1 0 1s

 

==> v1beta1/StatefulSet

NAME                     DESIRED CURRENT AGE

ignorant-bunny-rabbitmq  1 1 1s

ignorant-bunny-keycl     1 1 1s

 

==> v1/Pod(related)

NAME                                                     READY STATUS RESTARTS AGE

ignorant-bunny-activiti-cloud-modeling-6f6fb4448c-cpjzf  0/2 ContainerCreating 0 1s

ignorant-bunny-activiti-cloud-audit-db5665849-wrmbc      0/1 ContainerCreating 0 1s

ignorant-bunny-activiti-cloud-connector-8fdc5f57d-7x6n9  0/1 ContainerCreating 0 1s

ignorant-bunny-activiti-cloud-query-f586d6cb-7dc2k       0/1 ContainerCreating 0 1s

ignorant-bunny-runtime-bundle-7bd664f89f-gf276           0/1 Pending 0 1s

ignorant-bunny-activiti-cloud-gateway-78f99df5f7-fv5zw   0/1 Pending 0 1s

 

==> v1/Secret

NAME                         TYPE DATA AGE

ignorant-bunny-rabbitmq      Opaque 2 2s

ignorant-bunny-keycl-db      Opaque 1 2s

ignorant-bunny-keycl-http    Opaque 1 2s

ignorant-bunny-realm-secret  Opaque 1 2s

 

==> v1/ServiceAccount

NAME                     SECRETS AGE

ignorant-bunny-rabbitmq  1 2s

activiti-cloud-gateway   1 2s

 

==> v1/Role

NAME                    AGE

activiti-cloud-gateway  2s

 

==> v1beta1/RoleBinding

NAME                     AGE

ignorant-bunny-rabbitmq  2s

 

==> v1/RoleBinding

activiti-cloud-gateway  2s

 

This installs the full example based on the remote activiti-cloud-charts/activiti-cloud-full-example Helm chart from the Activiti 7 Helm chart repo with the custom configuration from the local values.yaml file.

 

Now, wait for all the services to be up and running, check the pods as follows:

 

$ kubectl get pods --namespace=activiti7

NAME                                                         READY STATUS RESTARTS AGE

ignorant-bunny-activiti-cloud-audit-db5665849-wrmbc          0/1 Running 0 43s

ignorant-bunny-activiti-cloud-connector-8fdc5f57d-7x6n9      0/1 ContainerCreating 0 43s

ignorant-bunny-activiti-cloud-gateway-78f99df5f7-fv5zw       0/1 ContainerCreating 0 43s

ignorant-bunny-activiti-cloud-modeling-6f6fb4448c-cpjzf      0/2 ContainerCreating 0 43s

ignorant-bunny-activiti-cloud-query-f586d6cb-7dc2k           0/1 Running 0 43s

ignorant-bunny-keycl-0                                       0/1 Running 0 43s

ignorant-bunny-rabbitmq-0                                    0/1 Running 0 43s

ignorant-bunny-runtime-bundle-7bd664f89f-gf276               0/1 ContainerCreating 0 43s

intent-deer-nginx-ingress-controller-9b5fb9b4f-4985v         1/1 Running 0 1h

intent-deer-nginx-ingress-default-backend-6f7884d594-85dph   1/1 Running 0 1h

 

Pay attention to the READY column, it should show 1/1 in all the pods before we can proceed. If some pods are not starting it can be useful to look at it in the Kubernetes Dashboard. Select the activiti7 namespace and then click on Pods:

 

 

In this case we can see that there is not enough memory to load all pods. If you see an insufficient memory error then you need to increase the available memory for Kubernetes running in Docker for Desktop (Preferences... | Advanced | Memory). You will also see a lot of these readiness probe failed... errors. They will eventually disappear, but it will take 5-10 minutes.

 

When all starts successfully you should see the following after a while:

 

 

We can also check the status of the Kubernetes node by selecting Cluster | Nodes | docker-for-desktop:

 

 

It is important to notice that Helm created a release of our Chart. Because we haven’t specified a name for this release Helm choose a random name, in my case ignorant-bunny. This means that we can manage this release independently of other deployments that we do.

 

You can run helm ls to see all deployed applications:

 

$ helm ls --namespace=activiti7

NAME           REVISION UPDATED                  STATUS   CHART                             NAMESPACE

ignorant-bunny 1        Mon Nov 19 09:41:30 2018 DEPLOYED activiti-cloud-full-example-0.4.0 activiti7

intent-deer    1        Mon Nov 19 07:45:10 2018 DEPLOYED nginx-ingress-0.19.2              activiti7

 

To delete a release do helm delete <release-name>.

 

Checking what Users and Groups that are available

Before we start interacting with the deployed example application, let’s check out what users and groups we have available in Keycloak. These can be used in process definitions and when we interact with the application.

 

Access Keycloak Admin Console on the following URL: http://activiti-keycloak.10.244.50.42.nip.io/auth/admin/master/console  

 

Login with admin/admin.

 

You should see a home page such as:

 

 

From this page we can access users and groups under the Manage section in the lower left corner. But first, note that there is already a security realm setup called activiti. We will be using it in a bit. There is also an Activiti client setup and the following Roles:

 

 

The ACTIVITI_ADMIN and ACTIVITI_USER roles are important to understand because they tell Activiti 7 what part of the core API the user can access. In the next section we will use the modeler user to login to the Activiti Modeler and it’s a member of the ACTIVITI_MODELER role.

 

Now, click on Users | View all users so we can see what type of users we have to work with when accessing the application:

 

 

So there are several custom users configured. We can use for example the testuser as it has the role ACTIVITI_USER applied:

 

 

Now, let’s move on and have a look at how we can interact with the deployed Activiti 7 application. But first, let’s just verify we got the Activiti BPMN Modeler app running.

 

A quick look at the Activiti 7 Modeler

Let’s have a quick look at the new Activiti 7 Modeler. So we can make sure it has been deployed properly. You can access the BPMN Modeler App via the http://activiti-cloud-gateway.10.244.50.42.nip.io/activiti-cloud-modeling URL. Login with modeler/password as this user is part of the ACTIVITI_MODELER role, you should now see:

 

 

All good so far, let’s create a Process Application just to be sure it works all the way. Click Create new | Application and give it a name and description:

 

 

Cool, all works!

 

Quick test to verify environment setup

We can do a quick test with curl to verify that the Activiti 7 Full Example has been deployed correctly.

 

First, request an access token for testuser from Keycloak:

 

$ curl -d 'client_id=activiti' -d 'username=testuser' -d 'password=password' -d 'grant_type=password' 'http://activiti-keycloak.10.244.50.42.nip.io/auth/realms/activiti/protocol/openid-connect/token' | python -m json.tool

 % Total    % Received % Xferd  Average Speed Time    Time Time Current

                                Dload Upload Total Spent Left  Speed

100  3106 100  3032 100  74 400  9 0:00:08 0:00:07  0:00:01 691

{

   "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoTUJPYnNyb1BJS2JJeTVOUGdnU0pPSGRDcmZFUWlva2otQUIwQjBaS3U0In0.eyJqdGkiOiJkMDU1ZmZlMy05MzNjLTQzNGUtYmExMC0yMjc5MzA3ODkwMTkiLCJleHAiOjE1MzYzMDM1NjUsIm5iZiI6MCwiaWF0IjoxNTM2MzAzMjY1LCJpc3MiOiJodHRwOi8vYWN0aXZpdGkta2V5Y2xvYWsuMTkyLjE2OC4xLjcxLm5pcC5pby9hdXRoL3JlYWxtcy9hY3Rpdml0aSIsImF1ZCI6ImFjdGl2aXRpIiwic3ViIjoiMzMxYTEyZDEtNzY2ZS00ODk3LWIwYTgtMzA5YWU1Y2FlYjI1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWN0aXZpdGkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiJlOWM0MDU2Ny04YTA4LTQ3MGEtOWZlNS0wNTgyNGM1YTczNjgiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9ndy5qeC1zdGFnaW5nLmFjdGl2aXRpLmVudmFsZnJlc2NvLmNvbSIsImh0dHA6Ly9qeC1zdGFnaW5nLXF1aWNrc3RhcnQtaHR0cC5qeC1zdGFnaW5nLmFjdGl2aXRpLmVudmFsZnJlc2NvLmNvbSIsImh0dHA6Ly9hY3Rpdml0aS1jbG91ZC1kZW1vLXVpLmp4LXN0YWdpbmcuYWN0aXZpdGkuZW52YWxmcmVzY28uY29tIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsIkFDVElWSVRJX1VTRVIiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3R1c2VyIiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InVzZXIiLCJlbWFpbCI6InRlc3R1c2VyQHRlc3QuY29tIn0.VTiYczKzW5StCVRcva-s9RbWROrzjHDAIGCYID6VERSC3R8lT86s3Qxux6chJDYKTCsNriN1xRQbQrPN3BPeTmB7EDlTFVtLHSA56sqp3ok9o9SoyDahcZ1JzWyKlQW3E63YVcofXv03sOx7yW70ZnIDEq6P5mb-frK_nA5jEoOG-Za6geTHj6z0yYBl0y3ropcT2NNhbofaaW8H6rQqiLHbEPddnds0QIoJvVXSbfUlW7MVlZjCfQS5RA4yg1OdkdWfBPBa3Tn1qM5wHBcZElWXmOYTi2B6bG1MSK7ufkca32S2fvB7vLhjBHzSxk2wzk8p_PUCeDdmTZTW_bTaxA",

   "expires_in": 300,

   "refresh_expires_in": 1800,

   "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoTUJPYnNyb1BJS2JJeTVOUGdnU0pPSGRDcmZFUWlva2otQUIwQjBaS3U0In0.eyJqdGkiOiI1ZGFmNDJmNy0yZmNlLTRjOGEtOTYyMC1kNTA3NGQzZTkxYzciLCJleHAiOjE1MzYzMDUwNjUsIm5iZiI6MCwiaWF0IjoxNTM2MzAzMjY1LCJpc3MiOiJodHRwOi8vYWN0aXZpdGkta2V5Y2xvYWsuMTkyLjE2OC4xLjcxLm5pcC5pby9hdXRoL3JlYWxtcy9hY3Rpdml0aSIsImF1ZCI6ImFjdGl2aXRpIiwic3ViIjoiMzMxYTEyZDEtNzY2ZS00ODk3LWIwYTgtMzA5YWU1Y2FlYjI1IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6ImFjdGl2aXRpIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZTljNDA1NjctOGEwOC00NzBhLTlmZTUtMDU4MjRjNWE3MzY4IiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwiQUNUSVZJVElfVVNFUiIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.hWiwSCILYBhRJO1-yTYu00zncDTMWVXrJuPPfJw6DpESe-UPr_ny5ffCzmUcql4PWl83VD0NXMIbAKxlJU3X1d7A8CMIfxJFdk_NVyKxB9TWrnUMZvTAT3Mf2y8Qj2UyEciQCpQBBkPyjJV1zS7R51i3dxIcDwDMLvaBBgNNSqnYKqagxMt2yGaC5fJkYt6hqOHuuSEMUTK2yL8kvc21Q-_AR8bdWy6zWD7geelYL3Wlhp83I_3I8-GZJ0iQRFsSOBeLZqhE7lBj7E4DxZKW6UEsTP77R49dFXPtryt4t5xWrDk1f14jIRe4-OWDbzE4mCNjwWTd07nOl5vxMhlrTg",

   "token_type": "bearer",

   "not-before-policy": 0,

   "session_state": "e9c40567-8a08-470a-9fe5-05824c5a7368",

   "scope": "email profile"

 

Now, use this access token to make a call to the Runtime Bundle for deployed process definitions:

 

$ curl http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1/process-definitions -H "Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoTUJPYnNyb1BJS2JJeTVOUGdnU0pPSGRDcmZFUWlva2otQUIwQjBaS3U0In0.eyJqdGkiOiJkMDU1ZmZlMy05MzNjLTQzNGUtYmExMC0yMjc5MzA3ODkwMTkiLCJleHAiOjE1MzYzMDM1NjUsIm5iZiI6MCwiaWF0IjoxNTM2MzAzMjY1LCJpc3MiOiJodHRwOi8vYWN0aXZpdGkta2V5Y2xvYWsuMTkyLjE2OC4xLjcxLm5pcC5pby9hdXRoL3JlYWxtcy9hY3Rpdml0aSIsImF1ZCI6ImFjdGl2aXRpIiwic3ViIjoiMzMxYTEyZDEtNzY2ZS00ODk3LWIwYTgtMzA5YWU1Y2FlYjI1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWN0aXZpdGkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiJlOWM0MDU2Ny04YTA4LTQ3MGEtOWZlNS0wNTgyNGM1YTczNjgiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9ndy5qeC1zdGFnaW5nLmFjdGl2aXRpLmVudmFsZnJlc2NvLmNvbSIsImh0dHA6Ly9qeC1zdGFnaW5nLXF1aWNrc3RhcnQtaHR0cC5qeC1zdGFnaW5nLmFjdGl2aXRpLmVudmFsZnJlc2NvLmNvbSIsImh0dHA6Ly9hY3Rpdml0aS1jbG91ZC1kZW1vLXVpLmp4LXN0YWdpbmcuYWN0aXZpdGkuZW52YWxmcmVzY28uY29tIiwiaHR0cDovL2xvY2FsaG9zdDozMDAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsIkFDVElWSVRJX1VTRVIiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3R1c2VyIiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InVzZXIiLCJlbWFpbCI6InRlc3R1c2VyQHRlc3QuY29tIn0.VTiYczKzW5StCVRcva-s9RbWROrzjHDAIGCYID6VERSC3R8lT86s3Qxux6chJDYKTCsNriN1xRQbQrPN3BPeTmB7EDlTFVtLHSA56sqp3ok9o9SoyDahcZ1JzWyKlQW3E63YVcofXv03sOx7yW70ZnIDEq6P5mb-frK_nA5jEoOG-Za6geTHj6z0yYBl0y3ropcT2NNhbofaaW8H6rQqiLHbEPddnds0QIoJvVXSbfUlW7MVlZjCfQS5RA4yg1OdkdWfBPBa3Tn1qM5wHBcZElWXmOYTi2B6bG1MSK7ufkca32S2fvB7vLhjBHzSxk2wzk8p_PUCeDdmTZTW_bTaxA" | python -m json.tool

 % Total    % Received % Xferd  Average Speed Time    Time Time Current

                                Dload Upload Total Spent Left  Speed

100  7123   0 7123   0 0 856      0 --:--:-- 0:00:08 --:--:--  1535

{

   "_embedded": {

       "cloudProcessDefinitionImpls": [

           {

               "appName": "default-app",

               "appVersion": "",

               "serviceName": "rb-my-app",

               "serviceFullName": "rb-my-app",

               "serviceType": "runtime-bundle",

               "serviceVersion": "",

               "id": "895e673a-ebea-11e8-9efc-069f25afb347",

               "name": "parentProcess",

               "key": "parentproc-8e992556-5785-4ee0-9fe7-354decfea4a8",

               "version": 1,

               "_links": {

                   "self": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1/process-definitions/895e673a-ebea-11e8-9efc-069f25afb347"

                   },

                   "startProcess": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1/process-instances"

                   },

                   "home": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1"

                   }

               }

           },

           {

               "appName": "default-app",

               "appVersion": "",

               "serviceName": "rb-my-app",

               "serviceFullName": "rb-my-app",

               "serviceType": "runtime-bundle",

               "serviceVersion": "",

               "id": "895e8e50-ebea-11e8-9efc-069f25afb347",

               "name": "SingleTaskProcessGroupCandidatesTestGroup",

               "key": "singletask-b6095889-6177-4b73-b3d9-316e47749a36",

               "version": 1,

               "_links": {

                   "self": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1/process-definitions/895e8e50-ebea-11e8-9efc-069f25afb347"

                   },

                   "startProcess": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1/process-instances"

                   },

                   "home": {

                       "href": "http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/v1"

                   }

               }

           },

...

 

It’s all working and we got a bunch of process definitions back that has been deployed in our Runtime Bundle. We can now explore the Runtime Bundle further by starting process instances etc.

 

Interacting with the Full Example Deployment

So the Full Example application is now deployed, but how do you interact with it to start a process and manage the tasks for it? There is no UI but we can use a Postman Collection that is available with the source code for the Full Example.

 

Clone the following project:

 

$ git clone https://github.com/Activiti/activiti-cloud-examples.git

$ git checkout master

 

(Make sure you get the master branch, otherwise the environment variables we will set up does not match)

 

In Postman import the collection as follows. Select File | Import… so you see:

 

 

Click Choose Files and then navigate and pick the following file (i.e. Activiti v7 REST API.postman_collection.json) from the project we just cloned:

 

 

Before calling any service you will need to create a new Environment in Postman. You can do that by going to the Manage Environment icon (cogwheel in upper right corner):

 

 

In Manage Environments click the Add button to add a new environment. Give it a name such as Activiti 7. Then configure the following variables for the environment: gatewayUrl (value = activiti-cloud-gateway.10.244.50.42.nip.io), idmUrl (value = activiti-keycloak.10.244.50.42.nip.io) and realm (value = activiti):

 

 

Click Add to add this new Activiti 7 environment. The gatewayUrl and idmUrl variable values you will recognize from the values.yaml file that we configured earlier on before we deployed the application. The keycloak realm is preconfigured to activiti.

 

Make sure that you select the environment in the drop down in the upper right corner. Also, select the collection that we imported:

 

 

To be able to make any ReST calls we need to acquire an access token from Keycloak. If you go to the keycloak folder in the Postman collection and select the getKeycloakToken you will get an access token:

 

 

You can see here how the environment variables are being used in the POST URL (i.e. {{idmUrl}}/auth/realms/{{realm}}/protocol/openid-connect/token). Click the Send button to make the ReST call:

 

 

The returned access token will be used to authenticate further requests. Look at the Tests tab and you will see a script that sets the access token variable (i.e. kcAccessToken) that is then used by other ReST calls:

 

 

Note that this token is time sensitive and it will be automatically invalidated at some point, so you might need to request it again if you start getting unauthorized errors (you will see 401 Unauthorized errors to the right in the middle of the screen).

 

Once we get the token for a user we can interact with all the user endpoints. For example, we can invoke a ReST call to see what Process Definitions that are deployed inside our Example Runtime Bundle (rb-my-app/getProcessDefinitions):

 

 

Now, let’s start a process instance with one of the process definitions. If we look at the source code for the Example Runtime Bundle we can see that there are a number of process definitions deployed, such as:

 

  • ConnectorProcess.bpmn20.xml - process with service task implemented as Cloud Connector   
  • SignalCatchEventProcess.bpmn20.xml - process that waits for event
  • SignalThrowEventProcess.bpmn20.xml - process that sends message to external service, similar to service task
  • Simple subprocess.bpmn20.xml - process with user task. Can be used as subprocess
  • Subprocess Parent.bpmn20.xml - parent process with subprocess
  • SimpleProcess.bpmn20.xml - simple process with user task
  • SubProcessTest.fixSystemFailureProcess.bpmn20.xml - another process that shows subprocess usage
  • processWithVariables.bpmn20.xml - process with user task and process variables

 

We will use the SimpleProcess that has one User task. To start a process instance based on this process definition we can use the startProcess Postman request that is in the rb-my-app folder. This request is defined as the {{gatewayUrl}}/rb-my-app/v1/process-instances POST ReST call. It is specifically targeting a Runtime Bundle identified by the /rb-my-app URL path:

 

 

Note the POST body, it specifies the processDefinitionKey as SimpleProcess to tell Activiti to start a process instance based on the latest version of the SimpleProcess definition. I also removed the process variables that were specified by default for this call as the process does not expect to be initialized with any variables. The commandType property has also been changed to payloadType:

 

 

{
"processDefinitionKey": "SimpleProcess",
"payloadType":"StartProcessPayload"
}

Click the Send button (but make sure you have a valid access token first, otherwise you will see a 401 Unauthorized status). You should get a response with information about the new process instance:

 

 

Next thing to do would probably be to list the active tasks. We can do that with the getTasks request. It uses the {{gatewayUrl}}/rb-my-app/v1/tasks?page=0&size=10 URL, which is associated with the rb-my-app runtime bundle. If we run the getTasks request in the rb-my-app folder we should see a response such as this:

 

 

We can see the id of the task here and it is ba485a1c-ec07-11e8-9efc-069f25afb347. We should be able to use it to claim the task (we need to claim it before we can complete it as it is a pooled task). Use the claimTask POST request using the id to claim the task for the user:

 

 

Here I have updated the URL to http://{{gatewayUrl}}/rb-my-app/v1/tasks/ba485a1c-ec07-11e8-9efc-069f25afb347/claim?assignee=hruser using the task id. The task is now assigned to the hruser and can be completed by that user using the completeTask request as follows:

 

 

Here I have also updated the URL to include the task id: {{gatewayUrl}}/rb-my-app/v1/tasks/ba485a1c-ec07-11e8-9efc-069f25afb347/complete.

 

This concludes the interactive part where we talk to the runtime bundle via its ReST API. If you are wondering about how to get access to the complete ReST API without using Postman, then this can be achieved with the following URL: http://activiti-cloud-gateway.10.244.50.42.nip.io/rb-my-app/swagger-ui.html:

 

 

You might also want to check out the Audit and Query services.

 

In the next article we will use the Activiti Modeler to define a custom Process Definition.

Outcomes