1. Home
  2. Docs
  3. Infrastructure
  4. Camunda BPM Platform & Zeebe

Camunda BPM Platform & Zeebe

Camunda is used as the default BPMN engine for workflows. Service Tasks are implemented in TypeScript using camunda-external-task-client-js, packaged as Docker and deployed to Kubernetes.

Free Whitepaper – The BPM Renaissance: How to Thrive in a Cloud-native World [PDF]

To design BPMN workflow collaboratively, you can use Cawemo (you can request an invite from CTO). Note that Cawemo does not support Camunda-specific features, so to make the BPMN executable you still need to use Camunda Modeler.

Recommended approach is to use External Task with a worker (pull pattern). This is more portable across Camunda and Zeebe, more scalable, and generally easier to implement because it does not use the HTTP API connector. The downside is it typically Docker/Kubernetes so cannot be serverless (as a workaround, it’s possible to schedule a serverless function periodically to poll, but not recommended).

Camunda

Kubernetes Deployment

We use AWS RDS MariaDB for persistence. First we need to create kubectl secret camunda with keys:

  • mariadb-url
  • mariadb-password
  • rocketchat-loviateam-user-id
  • rocketchat-loviateam-auth-token
  • rocketchat-miluv-user-id
  • rocketchat-miluv-auth-token

Then create camunda.yaml deployment, camunda-service.yaml, and camunda-ingress.yaml.

With AWS RDS MariaDB, DB_VALIDATE_ON_BORROW must be true. Reference: AWS RDS wait_timeout seconds default is 28800, Tomcat JDBC validationInterval default is 3000 ms or 3 seconds)

camunda.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: camunda
spec:
  selector:
    matchLabels:
      app: camunda
  replicas: 1
  template:
    metadata:
      labels:
        app: camunda
    spec:
      containers:
        - name: camunda
          # image: camunda/camunda-bpm-platform:run-7.13.0-alpha4
          # https://jira.camunda.com/browse/CAM-11840
          image: hendy/camunda-bpm-platform:run-7.13.0-alpha4-CAM-11840
          imagePullPolicy: Always
          # https://github.com/camunda/camunda-bpm-platform/blob/master/distro/run/assembly/resources/production.yml
          args: ['./camunda.sh', '--production']
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          env:
            # https://github.com/camunda/docker-camunda-bpm-platform/issues/137
            - name: JAVA_OPTS
              value: ''
            # Change back to HTTP port 8080 because we terminate SSL at nginx-ingress
            - name: SERVER_PORT
              value: '8080'
            - name: SERVER_SSL_ENABLED
              value: 'false'
            - name: SERVLET_SESSION_COOKIE_SECURE
              value: 'false'
            - name: DB_DRIVER
              value: com.mysql.jdbc.Driver
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: mariadb-url
            - name: DB_USERNAME
              value: camunda
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: mariadb-password
            - name: DB_VALIDATE_ON_BORROW
              value: 'true'
            - name: CAMUNDA_BPM_RUN_AUTH_ENABLED
              value: 'true'
            - name: CAMUNDA_BPM_RUN_CORS_ENABLED
              value: 'true'
            - name: CAMUNDA_BPM_RUN_CORS_ALLOWED_ORIGINS
              value: 'file://,http://localhost:3000,https://marketing-kit.lovia.life'
          readinessProbe:
            failureThreshold: 10
            httpGet:
              path: /app/welcome/default/
              port: http
              scheme: HTTP
            initialDelaySeconds: 15
            periodSeconds: 10
          resources:
            requests:
              memory: 400Mi
              cpu: 100m
            limits:
              memory: 400Mi

camunda-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: camunda
  labels:
    app: camunda
spec:
  selector:
    app: camunda
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: http

Secure Camunda first:

  1. Create a new user, add to camunda-admin group. This will become the new admin user.
  2. Delete all demo users.
  3. Enable REST API authentication.

Before adding the ingress we’ll need to:

  1. Configure k8s-lovia-sg’s AWS CloudFront Distribution to add Alternate CNAME camunda.lovia.life
  2. Add CNAME record for camunda.lovia.life to point to that AWS CloudFront Distribution

camunda-ingress.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: camunda-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  # REQUIRES helm cert-manager
  tls:
    - hosts:
        - camunda.lovia.life
      secretName: camunda-tls
  rules:
    - host: camunda.lovia.life
      http:
        paths:
          - backend:
              serviceName: camunda
              servicePort: 8080

Check SSL certificate status:

kubectl get cert -o wide

Camunda Tomcat

We use Camunda BPM Run Docker distribution to simplify configuration and maintenance, so this section is for historical purpose only.

Prepare the Tomcat server and engine-rest configuration as Config Maps. The important part in conf/server.xml (warning: it contains username & password credentials): (reference: AWS RDS wait_timeout seconds default is 28800, Tomcat JDBC validationInterval default is 3000 ms or 3 seconds)

Then create camunda.yaml deployment, camunda-service.yaml, and camunda-ingress.yaml.

<Resource name="jdbc/ProcessEngine" auth="Container" type="javax.sql.DataSource"
  ...
  testOnBorrow="true" validationQuery="SELECT 1" validationInterval="30000"/>

And the important part in engine-rest/WEB-INF/web.xml :

  <!-- Http Basic Authentication Filter -->
  <filter>
    <filter-name>camunda-auth</filter-name>
    <filter-class>
      org.camunda.bpm.engine.rest.security.auth.ProcessEngineAuthenticationFilter
    </filter-class>
        <async-supported>true</async-supported>
    <init-param>
      <param-name>authentication-provider</param-name>
      <param-value>org.camunda.bpm.engine.rest.security.auth.impl.HttpBasicAuthenticationProvider</param-value>
    </init-param>
    <init-param>
            <param-name>rest-url-pattern-prefix</param-name>
            <param-value></param-value>
          </init-param> 
  </filter>
kubectl create configmap camunda-conf --from-file=conf/server.xml
kubectl create configmap camunda-engine-rest --from-file=engine-rest/WEB-INF/web.xml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: camunda
spec:
  selector:
    matchLabels:
      app: camunda
  replicas: 1
  template:
    metadata:
      labels:
        app: camunda
    spec:
      containers:
        - name: camunda
          image: camunda/camunda-bpm-platform:7.12.0
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          env:
            - name: DB_DRIVER
              value: com.mysql.jdbc.Driver
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: mariadb-url
            - name: DB_USERNAME
              value: camunda
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: mariadb-password
            - name: ROCKETCHAT_LOVIATEAM_USER_ID
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: rocketchat-loviateam-user-id
            - name: ROCKETCHAT_LOVIATEAM_AUTH_TOKEN
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: rocketchat-loviateam-auth-token
            - name: ROCKETCHAT_MILUV_USER_ID
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: rocketchat-miluv-user-id
            - name: ROCKETCHAT_MILUV_AUTH_TOKEN
              valueFrom:
                secretKeyRef:
                  name: camunda
                  key: rocketchat-miluv-auth-token
          volumeMounts:
            - name: engine-rest
              subPath: web.xml
              mountPath: /camunda/webapps/engine-rest/WEB-INF/web.xml
            - name: conf
              subPath: server.xml
              mountPath: /camunda/conf/server.xml
            - name: empty-dir
              mountPath: /camunda/webapps/ROOT
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/camunda-invoice
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/docs
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/examples
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/host-manager
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/camunda-welcome
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/h2
              readOnly: true
            - name: empty-dir
              mountPath: /camunda/webapps/manager
              readOnly: true
          readinessProbe:
            failureThreshold: 10
            httpGet:
              path: /camunda/app/admin/default/
              port: http
              scheme: HTTP
            initialDelaySeconds: 15
            periodSeconds: 10
          resources:
            requests:
              memory: 400Mi
              cpu: 100m
            limits:
              memory: 400Mi
      volumes:
        - name: engine-rest
          configMap:
            name: camunda-engine-rest
        - name: conf
          configMap:
            name: camunda-conf
        - name: empty-dir
          emptyDir: {}

Zeebe

Deploy Zeebe Workflow

ceefour@amanah:~/project/miluv/miluv-workers$ zbctl deploy workflows/miluv-signIn.bpmn 
{
  "key": 2251799813690820,
  "workflows": [
    {
      "bpmnProcessId": "miluv-signIn",
      "version": 1,
      "workflowKey": 2251799813690819,
      "resourceName": "workflows/miluv-signIn.bpmn"
    }
  ]
}

Create Zeebe Workflow Instance

zbctl create instance miluv-signIn --variables '{"user": {"id": 466, "name": "Hendy Irawan", "slug": "hendy"}}'

Using the HTTP Connector to call REST API

The recommended, future-proof way of implementing Service Tasks is using pull workers. Although REST APIs can be easier to implement and test (using Postman).

You can implement the REST API using NestJS+Fastify (TypeScript) or FastAPI (Python).

Examples:

Authorizations

“guest” account: To start processes from guest account (with CORS “semi-authentication”), the guest must have these authorizations:

  • Process Definition: READ and CREATE_INSTANCE on the specific Process ID.
  • Process Instance: CREATE_INSTANCE on *.

Articles

How can we help?