In this tutorial, we'll walk you through deploying an application on the C8 platform using Kubernetes.
Throughout the tutorial, we'll use kubectl
to perform administrative tasks on the cluster. If this is not part of your toolkit, please refer to the installation instructions available on Kubernetes Documentation.
The first step is to ensure that the C8 platform has been deployed successfully. Let's list the pods in the namespace where you installed the platform:
kubectl get pods -n <c8-namespace>
You should ensure that all listed pods display a complete READY status. For example, after running the command, you might see output similar to:
NAME READY STATUS RESTARTS AGE
camunda-platform-elasticsearch-master-0 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-identity-56bdc66dd6-wqw77 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-keycloak-0 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-operate-59c6b564cc-hc5l4 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-postgresql-0 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-tasklist-647b8b9fb5-hlj7d 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-zeebe-0 1/1 Running 1 (3d16h ago) 3d17h
camunda-platform-zeebe-gateway-796c8885d5-85hnm 1/1 Running 1 (3d16h ago) 3d17h
The list may vary depending on your installation, but it should be consistent with the components you chose to install (refer to C8 Platform Overview).
Ensure that all listed pods display a complete READY status.
Your applications should adhere to Kubernetes' principle of separation and isolation. For this example, the application corresponds to users different from those who are administrators.
kubectl create namespace c8-payment-demo
kubectl config set-context --current --namespace=c8-payment-demo
By creating a dedicated namespace for application installation, we ensure proper isolation and organization within the Kubernetes cluster. We will now use this namespace in the tutorial.
To enable communication with Zeebe, any application needs an authentication token if the authentication layer is enabled. The Identity component (documentation) is responsible for managing this authentication.
How to get the Identity URL?
To obtain the Identity URL, retrieve the ingress of the platform:
kubectl get ingress -n camunda-platform
This command provides a list of ingresses:
NAME CLASS HOSTS ADDRESS PORTS AGE
camunda-platform nginx local.distro.ultrawombat.com 10.96.189.147 80, 443 3d18h
camunda-platform-zeebe-gateway nginx zeebe.local.distro.ultrawombat.com 10.96.189.147 80, 443 3d18h
In Kubernetes, an ingress exposes internal HTTP and HTTPS services outside the cluster. Note that there are multiple listed ingresses, one dedicated to the Zeebe Gateway and another to other services. We are interested in the latter, which hosts most of the C8 platform services.
To introspect the ingress and obtain the route associated with the Identity service:
kubectl describe ingress camunda-platform -n camunda-platform
This command provides details about the ingress:
Name: camunda-platform
Namespace: camunda-platform
Address: 10.96.189.147
Ingress Class: nginx
Default backend: <default>
TLS:
camunda-platform terminates local.distro.ultrawombat.com
Rules:
Host Path Backends
---- ---- --------
local.distro.ultrawombat.com
/auth camunda-platform-keycloak:80 (10.244.1.7:8080)
/identity camunda-platform-identity:80 (10.244.1.5:8080)
/operate camunda-platform-operate:80 (10.244.2.3:8080)
/optimize camunda-platform-optimize:80 ()
/tasklist camunda-platform-tasklist:80 (10.244.2.5:8080)
We are interested in the /identity
backend, which provides the URL https://local.distro.ultrawombat.com/identity
. Keep in mind that accessing a service via the ingress corresponds to external access.
- Log on the Identity web page
- Click on "Add application".
- Set the Name to "payment-app" and select type "M2M".
- Click on "Add".
- In the list of applications, click on the newly created "payment-app".
- Reveal or copy the "Client Secret".
Save both the Client ID and the Client secret for later use.
In this step, we will deploy the sample application using kubectl
. This deployment aims to be minimal and does not cover all the cases and constraints you may encounter in your production cluster.
First, clone the repository containing the Payment Example Process Application:
git clone git@github.com:camunda-community-hub/camunda-8-examples.git
Navigate to the Kubernetes manifests directory within the cloned repository:
cd camunda-8-examples/payment-example-process-application/kube/manifests
The structure of the manifests is as follows:
-
deployment.yaml: Describes the deployment of the application. In this case, we will use a single replica and pass some configuration variables to the container.
-
service.yaml: Describes how the application is exposed on its HTTP stream.
-
secrets.yaml: This file is used to store sensitive application data, such as the Client secret retrieved in the previous step.
-
ingress.yaml (optional): Describes the exposure of the application service outside the cluster, allowing access to it.
- Edit the
ingress.yaml
file and replace the value of- host: your-host.dv
with a domain pointing to your cluster. Important: The sample application does not currently support hosting in a subdirectory.
Optional: Using Port Forwarding Instead of Ingress
If you prefer not to set up an Ingress or if your environment does not support it, you can use port forwarding with kubectl
to access your application. This method allows you to access your application's service locally without exposing it externally.
To forward a local port to a port on a pod, you can use the following kubectl
command:
kubectl port-forward <pod-name> <local-port>:<pod-port> -n <namespace>
Replace <pod-name>
with the name of your pod, <local-port>
with the local port you want to use on your machine, <pod-port>
with the port on the pod where your application is running, and <namespace>
with the namespace where your application is deployed.
For example, if your application's pod is named payment-example-process-application-6d99cdc9cd-c7lmt
and it exposes port 8080
, and you want to forward it to local port 8080
, you would run:
kubectl port-forward payment-example-process-application-6d99cdc9cd-c7lmt 8080:8080 -n c8-payment-demo
After running this command, you can access your application locally at http://localhost:8080
.
Keep in mind that port forwarding is a convenient way for development and testing but may not be suitable for production environments where you need a more robust networking solution like Ingress.
Optional: Using Routes Instead of Ingress (OpenShift)
If you prefer to use OpenShift's Routes instead of Kubernetes' Ingress for external access to your application, you can configure routes instead.
Routes in OpenShift allow external traffic to access services inside the cluster. Here's how you can create a route for your application:
- Create a route for your application using the
oc
command-line tool:
oc expose service <service-name> --port=<port> --name=<route-name>
Replace <service-name>
with the name of your service, <port>
with the port your application is listening on, and <route-name>
with the desired name for your route.
For example:
oc expose service payment-example-process-application --port=8080 --name=payment-route
This command creates a route named payment-route
to expose the service payment-example-process-application
on port 8080
.
- Retrieve the hostname for your route:
oc get route payment-route
This command will display the hostname for your route, which you can use to access your application externally.
Once the route is created, you can access your application using the provided hostname. For example, if the hostname for your route is payment-route.apps.example.com
, you can access your application at http://payment-route.apps.example.com
.
- To keep things simple, the demo application is configured through its deployment (
deployment.yaml
) via its environment variables. Edit this file and adjust the following values:
env:
# set it to the client id retrieved in the identity M2M step
- name: ZEEBE_CLIENT_ID
value: "yourClientID"
#### The following values are not intended to be modified, but here's an explanation of their uses
# depending on your Keycloak location, you may need to edit the authorization server URL
- name: ZEEBE_AUTHORIZATION_SERVER_URL
value: http://camunda-platform-keycloak.camunda-platform.svc.cluster.local/auth/realms/camunda-platform/protocol/openid-connect/token
# This is the address of the Zeebe broker service, you should not have to change it
- name: ZEEBE_CLIENT_BROKER_GATEWAY_ADDRESS
value: "camunda-platform-zeebe-gateway.camunda-platform.svc.cluster.local:26500"
# For demonstration purposes, we disable encryption. Do not do this in production
- name: ZEEBE_CLIENT_SECURITY_PLAINTEXT
value: "true"
# What will the token be used for?
- name: ZEEBE_TOKEN_AUDIENCE
value: zeebe-api
Note: In Kubernetes, services are accessible through the following composition (ref: Kubernetes Services):
<service name>.<namespace>.svc.<clustername>.local
- Once the configuration is edited, all that's left is to deploy the resources. We will start by injecting the secret used by the Zeebe broker.
- Inject the client secret. This command will apply the
secrets.yaml
manifest and inject the sensitive secret value usingsed
.
- Inject the client secret. This command will apply the
CLIENT_SECRET="<your secret from the previous step>"
CLIENT_SECRET_B64=$(echo -n "$CLIENT_SECRET" | base64)
sed -e "s|CLIENT_SECRET_B64|$CLIENT_SECRET_B64|g" secrets.yaml | kubectl apply -f -
- Apply other Kubernetes manifests:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
For EKS, ensure your cluster is properly configured to handle LoadBalancer services used in the ingress.
- Watch the status of the deployment:
kubectl get pods --watch
e.g.:
NAME READY STATUS RESTARTS AGE
payment-example-process-application-6d99cdc9cd-c7lmt 1/1 Running 0 2m15s
You should observe that the application is ready. Now, you can watch the logs (please replace with the name of the pod):
kubectl logs payment-example-process-application-6d99cdc9cd-c7lmt -f
- You should be able to access the application through the host you have set in the ingress using plain HTTP:
http://your-host.dv
- You should observe on the dashboard Operate the payment business process appearing. The application has loaded it automatically.
The README.md explains how to interact with the application.
Now, let's simulate the payment using the previously configured ingress:
curl -L "http://your-host.dv/start" -X "POST" -H "Content-Type: application/json" -d "{\"customerId\": \"cust50\", \"orderTotal\": 67.50, \"cardNumber\": \"1234 4567\", \"cvc\": \"123\", \"expiryDate\": \"12/24\"}"
If you observe the pod logs, you should see the application's reaction:
kubectl logs payment-example-process-application-6d99cdc9cd-c7lmt
e.g:
2024-03-19T17:33:25.389Z INFO 1 --- [ main] i.c.z.s.c.jobhandling.JobWorkerManager : . Starting Zeebe worker: ZeebeWorkerValue{type='creditCardCharging', name='creditCardHandler#handle', timeout=-1, maxJobsActive=-1, requestTimeout=-1, pollInterval=-1, autoComplete=false, fetchVariables=[], enabled=true, methodInfo=io.camunda.zeebe.spring.client.bean.MethodInfo@6587305a}
2024-03-19T17:33:25.390Z INFO 1 --- [ main] i.c.z.s.c.jobhandling.JobWorkerManager : . Starting Zeebe worker: ZeebeWorkerValue{type='customerCreditHandling', name='customerCreditHandler#handle', timeout=-1, maxJobsActive=-1, requestTimeout=-1, pollInterval=-1, autoComplete=true, fetchVariables=[], enabled=true, methodInfo=io.camunda.zeebe.spring.client.bean.MethodInfo@3f81621c}
2024-03-19T17:33:25.596Z INFO 1 --- [ main] c.c.c.w.PaymentProcessApplication : Started PaymentProcessApplication in 32.188 seconds (process running for 34.883)
2024-03-19T17:45:14.270Z INFO 1 --- [nio-8080-exec-5] c.c.c.w.rest.StartFormRestController : Starting process `paymentProcess` with variables: {customerId=cust50, orderTotal=67.5, cardNumber=1234 4567, cvc=123, expiryDate=12/24}
2024-03-19T17:45:14.455Z INFO 1 --- [pool-2-thread
-1] c.c.c.w.worker.CustomerCreditHandler : Handling customer credit for process instance 2251799813697282
2024-03-19T17:45:14.461Z INFO 1 --- [pool-2-thread-1] c.c.c.w.service.CustomerService : customer cust50 has credit of 50.0
2024-03-19T17:45:14.462Z INFO 1 --- [pool-2-thread-1] c.c.c.w.service.CustomerService : charged 50.0 from the credit, open amount is 17.5
2024-03-19T17:45:14.562Z INFO 1 --- [pool-2-thread-1] c.c.c.w.worker.CreditCardHandler : Handling credit card payment for process instance
2024-03-19T17:45:14.563Z INFO 1 --- [pool-2-thread-1] c.c.c.w.service.CreditCardService : charging card 1234 4567 that expires on 12/24 and has cvc 123 with amount of 17.5
2024-03-19T17:45:14.564Z INFO 1 --- [pool-2-thread-1] c.c.c.w.service.CreditCardService : payment
Similarly, by going to the dashboard Operate, you should observe the completed process.
It is possible that due to isolation policies, the "c8-payment-demo" namespace may not be able to reach the services in the "c8" namespace. In this case, you can:
- Adjust the policy to allow it.
- Modify the configuration of the demo application to use the ingress instead of the service.