Write Kubernetes Native Application Using Camel K: Part 1
Join the DZone community and get the full member experience.
Join For FreeApache Camel K is integration framework based on ubiquitous Apache Camel. Camel K differs from Apache Camel, as Camel K is native to Kubernetes, while Apache Camel runs almost everywhere. But why Camel K is required? The following bullet points makes Camel K more favorable in Kubernetes or an OpenShift Environment:
- Low code and Quick deployment.
- Cloud Native Camel on Kubernetes
- Designed for Serverless and MicroService architecture.
- Can be written in YAML, JAVA, XML, Groovy, Javascript, Kotlin.
- Exchange K-Native Eventing events.
I will try to write a series of Technology blogs on Camel K. This is the first one. This article would be simple but will cover important aspects.
Camel K provides a CLI tool, kamel, which can be run from Linux/Mac shell or Windows OS. It can be download from Camel K release page. It contains a binary named kamel that you should put into system path. For example, if you’re using Linux, you can put kamel in /usr/bin.
Step 1 : Firstly we have to install Camel-k operator with command "kamel install". This commands intall Camel-k operator and customer resources.
I have used Fedora 32 and minikube v1.9.2. So let us start actual work.
xxxxxxxxxx
# start minishift with profile camelkprofile
[chandrashekhar@localhost camel-k-client]$ minikube start -p camelkprofile --cpus=2 --memory='5g'
# set local docker registry to point to minikube docker registry with profile camelkprofile
[chandrashekhar@localhost camel-k-client]$ eval $(minikube -p camelkprofile docker-env)
# enable registry addon
[chandrashekhar@localhost camel-k-client]$ minikube addons enable registry -p camelkprofile
�� The 'registry' addon is enabled
# check kamel in system path
[chandrashekhar@localhost camel-k-client]$ which kamel
/usr/bin/kamel
# create a namespace camelk-namespace, where we will deploy our application.
[chandrashekhar@localhost camel-k-client]$ kubectl create ns camelk-namespace
namespace/camelk-namespace created
# set current namespace context to camelk-namespace with this we don't have to specify namespace for each command
[chandrashekhar@localhost camel-k-client]$ kubectl config set-context $(kubectl config current-context) --namespace=camelk-namespace
Context "camelkprofile" modified.
# currently there are no pods in namespace
[chandrashekhar@localhost camel-k-client]$ kubectl get pods
No resources found in camelk-namespace namespace.
# install camel-k operator now
[chandrashekhar@localhost camel-k-client]$ kamel install
Camel K installed in namespace camelk-namespace
# check pods now, we will find camel-k operator now
[chandrashekhar@localhost camel-k-client]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
camel-k-operator-7df56fc69b-qvldd 1/1 Running 0 7m29s
# at present no integration is installed
[chandrashekhar@localhost camel-k-client]$ kubectl get it
No resources found in camelk-namespace namespace.
# at present no integration platform is installed
[chandrashekhar@localhost camel-k-client]$ kubectl get ip
NAME PHASE
camel-k Ready
# following configmap are there.
[chandrashekhar@localhost camel-k-client]$ kubectl get configmap
NAME DATA AGE
camel-k-lock 0 94s
camel-k-maven-settings 1 93s
[chandrashekhar@localhost camel-k-client]$
Step 2 : Deploy Camel K application.
Let us create a simple Camel K application in Java CamelkPoc.java.
xxxxxxxxxx
import org.apache.camel.builder.RouteBuilder;
public class CamelkPoc extends RouteBuilder {
public void configure() throws Exception {
from("timer:java?period=10000")
.routeId("java")
.setBody()
.simple("Hello Camel K from ${routeId} and {{message}}")
.to("log:info");
}
}
Step 3 : Now, let us run the application in dev mode. In dev mode, we should not close the original terminal. That terminal which is running the application or integration will also undeploy as soon as it's closed. In dev mode, we can change integration at runtime.
xxxxxxxxxx
[chandrashekhar@localhost kamelpoc]$ kamel run CamelkPoc.java -p message="parameter from commandline" --dev
# From different terminal run following command
[chandrashekhar@localhost ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
camel-k-operator-7df56fc69b-qvldd 1/1 Running 0 54m
# Still we see only operator pod but not application/integration pod. This could be because all dependencies of integration/application is being installed. Check operator logs to verify this.
[chandrashekhar@localhost ~]$ kubectl logs -f camel-k-operator-7df56fc69b-qvldd
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/wildfly/common/wildfly-common/1.5.4.Final-format-001/wildfly-common-1.5.4.Final-format-001.jar (307 kB at 15 kB/s)
[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/eclipse/microprofile/context-propagation/microprofile-context-propagation-api/1.0.1/microprofile-context-propagation-api-1.0.1.jar
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/graalvm/sdk/graal-sdk/20.1.0/graal-sdk-20.1.0.jar (536 kB at 25 kB/s)
# Finally after few minutes
[chandrashekhar@localhost ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
camel-k-operator-7df56fc69b-qvldd 1/1 Running 0 65m
camelk-poc-66fd9585cf-lj4rr 1/1 Running 0 62s
# Ensure that integration and integration-platform are in Running and Ready state respectively. If not than might be integration/application is still installing.
[chandrashekhar@localhost ~]$ kubectl get it
NAME PHASE KIT REPLICAS
camelk-poc Running kit-btjjsi5pu87knpm77nmg 1
[chandrashekhar@localhost ~]$ kubectl get ip
NAME PHASE
camel-k Ready
[chandrashekhar@localhost ~]$
# Check logs of application/integration pod.
[chandrashekhar@localhost ~]$ kubectl logs -f camelk-poc-66fd9585cf-lj4rr
2020-09-20 11:58:50.050 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Camel K from java and parameter from commandline]
2020-09-20 11:59:00.051 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Camel K from java and parameter from commandline]
Step 4: Let us now make changes to code at runtime.
xxxxxxxxxx
# Edit text with "Modified" in CamelkPoc.java without stopping any integration or terminal.
.simple("Hello Modified Camel K from ${routeId} and {{message}}")
# We will find a new pod is created but logs of pods will be printed/logged in same terminal where we were earlier monitoring logs.
[chandrashekhar@localhost camel-k-client]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
camel-k-operator-7df56fc69b-qvldd 1/1 Running 0 121m
camelk-poc-cf8dd47b-5z65v 1/1 Running 0 98s
# Check pod logs in same terminal defined in step 3.
[2] 2020-09-20 12:40:26.515 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Modified Camel K from java and parameter from commandline]
[2] 2020-09-20 12:40:36.515 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Modified Camel K from java and parameter from commandline]
Step 5 Camel K Resource Visualization: This application is running in dev mode, and any changes are reflected at runtime. The user or developer is only concerned with their application. Other details like Camel-k Operator and Custom Resource are abstracted away or hidden from the user. Some of these abstract components are described below.
Integration Platform (kubectl get ip): It is a one generic configuration for all the integrations. It is having information like maven's settings.xml from where the maven artifacts are installed. If we note below commands than we find that settings.xml is mounted as configmap camel-k-maven-settings.
xxxxxxxxxx
[chandrashekhar@localhost camel-k-client]$ kubectl get ip camel-k -o yaml|grep -i -A 1 -B 1 settings.xml
configMapKeyRef:
key: settings.xml
name: camel-k-maven-settings
[chandrashekhar@localhost camel-k-client]$ kubectl get configmap|grep settings
camel-k-maven-settings 1 80m
[chandrashekhar@localhost camel-k-client]$ kubectl describe configmap camel-k-maven-settings |head -n 20|tail -n 11
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository></localRepository>
<profiles>
<profile>
<id>maven-settings</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
[chandrashekhar@localhost camel-k-client]$
Integration(kubectl get it): There is one integration for each of our application/integration installed. If we describe this integration than we will find that it contains the code(or camel route) details and also dependencies of application.
xxxxxxxxxx
[chandrashekhar@localhost camel-k-client]$ kubectl get it
NAME PHASE KIT REPLICAS
camelk-poc Running kit-btjjsi5pu87knpm77nmg 1
# Describe integration and we can find code details.
[chandrashekhar@localhost camel-k-client]$ kubectl describe it camelk-poc |grep -A 15 Sources
Sources:
Content: import org.apache.camel.builder.RouteBuilder;
public class CamelkPoc extends RouteBuilder {
@Override
public void configure() throws Exception {
from("timer:java?period=10000")
.routeId("java")
.setBody()
.simple("Hello Camel K from ${routeId} and {{message}}")
.to("log:info");
}
}
Name: CamelkPoc.java
# We can also see camel dependencies involved to run this application.
[chandrashekhar@localhost camel-k-client]$ kubectl describe it camelk-poc |grep -A 5 Dependencies
Dependencies:
camel:log
camel:timer
mvn:org.apache.camel.k/camel-k-loader-java
mvn:org.apache.camel.k/camel-k-runtime-main
Digest: vPcZSWphLbNfrwmlkSMZ9aNDtza8oNWjhfsL7WZYbo0I
[chandrashekhar@localhost camel-k-client]$
# Further this code and application.properties if any are also mounted as configmap.
[chandrashekhar@localhost camel-k-client]$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
camel-k-operator 1/1 1 1 98m
camelk-poc 1/1 1 1 33m
[chandrashekhar@localhost camel-k-client]$
[chandrashekhar@localhost camel-k-client]$ kubectl get deployment camelk-poc -o yaml|grep -A 6 -i "configMap:"
- configMap:
defaultMode: 420
items:
- key: content
path: CamelkPoc.java
name: camelk-poc-source-000
name: i-source-000
- configMap:
defaultMode: 420
items:
- key: application.properties
path: user.properties
name: camelk-poc-user-properties
name: user-properties
[chandrashekhar@localhost camel-k-client]$ kubectl get cm camelk-poc-source-000 -o yaml
apiVersion: v1
data:
content: |
import org.apache.camel.builder.RouteBuilder;
public class CamelkPoc extends RouteBuilder {
@Override
public void configure() throws Exception {
from("timer:java?period=10000")
.routeId("java")
.setBody()
.simple("Hello Camel K from ${routeId} and {{message}}")
.to("log:info");
}
}
kind: ConfigMap
-----
-----
Step 6: Finally, we can close the terminal where we executed CamelkPoc.java in --dev mode. In production mode, we are going to run without --dev mode so that simply closing the terminal doesn't uninstall our integration.
[chandrashekhar@localhost kamelpoc]$ kamel run CamelkPoc.java -p message="test parameter from commandline"
integration "camelk-poc" created
[chandrashekhar@localhost kamelpoc]$ kubectl get it
NAME PHASE KIT REPLICAS
camelk-poc Running kit-btjjsi5pu87knpm77nmg 1
[chandrashekhar@localhost kamelpoc]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
camel-k-operator-7df56fc69b-qvldd 1/1 Running 0 134m
camelk-poc-55df4fb589-5bxl8 1/1 Running 0 24s
[chandrashekhar@localhost kamelpoc]$ kubectl logs -f camelk-poc-55df4fb589-5bxl8
---
---
2020-09-20 12:59:21.162 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Modified 123 Camel K from java and test parameter from commandline]
2020-09-20 12:59:31.139 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Modified 123 Camel K from java and test parameter from commandline]
2020-09-20 12:59:41.139 INFO [Camel (camel-k) thread #0 - timer://java] info - Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Modified 123 Camel K from java and test parameter from commandline]
^C
[chandrashekhar@localhost kamelpoc]$
Thanks so much for reading. I believe this article would have provided you insights about Camel-k and how it works. Stay tune for the next article in this series.
Opinions expressed by DZone contributors are their own.
Comments