In der Java-Welt ist das Integrations-Framework Apache Camel bereits ein alter Hase. Die vielfältigen Adapterkomponenten lassen kaum Wünsche offen, wenn es darum geht ein (Fremd-)System oder eine bestimmte Schnittstelle zu integrieren. Für den Einsatz in Serverless-Umgebungen auf einem Kubernetes-Cluster gibt es nun eine native Integration, die die Entwicklung von Camel-Routen vereinfachen soll.
Dieser Artikel soll einen ersten Einblick in das Camel-Unterprojekt Camel K geben.
Was ist Camel K ?
Begriffsdefinition
Camel K (gelegentlich auch Camel-K oder camel-k geschrieben) ist ein leichtgewichtiges Integrations-Framework auf Basis von Apache Camel, dass nativ auf Kubernetes läuft und für Serverless und Microservice-Architekturen entwickelt wurde.
Aus Entwickler-Sicht wird nur die eigentliche Camel-Route
erstellt und diese z.B. als Groovy, Java etc. Datei deployt. Aktuell (Stand: 18.03.2021) werden 7 unterschiedliche Sprachen unterstützt. Dazu zählen:
- Java
- JShell
- XML
- YAML
- Groovy
- Kotlin
- JavaScript
Installation von Camel K
Die Installation von Camel K ist in der camel-k Dokumentation beschrieben. Im folgenden soll die Installation auf Minikube erläutert werden.
Installation am Beispiel von Minikube
Nach dem Download kann man Minikube auf VirtualBox wie folgt starten:
minikube start --driver virtualbox
Danach muss noch das Registry-Addon aktiviert werden:
minikube addons enable registry
Nachdem Minikube nun gestartet ist, kümmern wir uns um die Installation der kamel CLI. Damit wird dann auch Camel K in Kubernetes installiert. Nachdem die kamel CLI
eingerichtet ist, können wir mit kamel install
den camel-k-operator
installieren. Dieser kümmert sich um die eigentliche Ausführung der Camel-Routen in Kubernetes.
Mit den folgenden Befehlen legen wir einen neuen Namespace (camel-basic) für unser nachfolgendes Beispiel an und installieren darin Camel K:
kubectl create namespace camel-basic
kubectl config set-context --current --namespace=camel-basic
kamel install
TIP | Der Camel-K-Operator wird nur für den jeweils gewählten Kubernetes-Namespace installiert. Wenn ein neuer Namespace angelegt wird, dann muss der kamel install -Befehl erneut ausgeführt werden.Die Installation in einem Kubernetes Cluster wird über den Befehl kamel install --cluster-setup durchgeführt. In dem Fall wird die Installation über eine Custom Resource Definitions (CRD) im Cluster eingerichtet und benötigt entsprechend Admin-Rechte. |
Der erste Start kann etwas dauern. Evtl. fehlt auch noch ein Integration-Kit. Das kann man mit dem Befehl
kamel kit create default
nachholen. Der Name default
ist hier willkürlich gewählt.
Hello-World-Beispiel
Nun können wir unsere erste Camel-Route in Kubernetes starten. Ein einfaches HelloWorld
-Beispiel kann z.B. so aussehen:
from('timer:tick?period=3000') .setBody().constant('Hello world from Camel K') .to('log:info')
Die Bespiel-Route kann mit dem Befehl kamel init hello-world.groovy
angelegt werden. Danach kann man mit dem Befehl
kamel run hello-world.groovy
die Camel-Route in Kubernetes deployen. Dabei werden folgende Aufgaben vom Kubernetes-Cluster automatisch übernommen:
- Auflösung aller benötigten Dependencies
- Erstellung der benötigten Deployment Konfiguration
- Start des jeweiligen Pods für die Ausführung der Camel-Route
Die benötigten Dependencies werden anhand der verwendeten Adapter in der Camel-Route automatisch ermittelt.
Mit dem Befehl kamel get
können alle aktuell ausgeführten Integrations
abgefragt werden:
$ kamel get
NAME PHASE KIT
helloworld Running kit-c15hl5gfc8h01ii2rung
Verwendung des DEV-Modes
Gerade am Anfang der Entwicklung ist es von Vorteil ein schnelles Feedback über mögliche Fehler beim Start der Anwendung zu bekommen. Dazu bietet Camel K den sog. DEV-Mode. Die Camel-Integration wird dazu mit dem Parameter --dev
gestartet.
Beispiel: Basic.java
import org.apache.camel.builder.RouteBuilder; public class Basic extends RouteBuilder { @Override public void configure() throws Exception { from("timer:java?period=1000") .setHeader("example") .constant("java") .setBody() .simple("Hello World! Camel K route written in ${header.example}.") .to("log:info"); } }
Das obige Beispiel lässt sich mit kamel run Basic.java --dev
im DEV-Mode starten. Während man auf der Konsole die Log-Ausgabe sieht kann man den Source-Code abändern (DEV-Loop). Er wird dann automatisch neu deployt. Der Name der Integration
wird automatisch aus dem Klassennamen abgeleitet und in ein „lower-case“ umgewandelt. Durch Verwendung des dev
-Modes bekommt man direkt Feedback, ob alle Abhängigkeiten korrekt aufgelöst werden konnten.
Logs abrufen
Ohne den Start im DEV-Mode kehrt der Command-Prompt direkt zurück und man kann sich die Logs des ausgeführten Pods anschauen:
kamel logs basic [1] 2021-03-18 09:29:27,895 INFO [info] (Camel (camel-1) thread #0 - timer://java) Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello World! Camel K route written in java.]
Mit kamel delete basic
kann man die Integration wieder löschen.
Architektur
Programmiermodell
Das Programmiermodell zielt auf die einfache Definition von Camel-Routen und deren Betrieb als Serverless-Funktionen (aka Functions
) ab. Dabei dient Kubernetes/OpenShift als Laufzeitumgebung dieser Funktionen, die in diesem Fall durch Camel-Routen
repräsentiert werden.
Die Entwicklung der eigentlichen Camel-Routen steht im Vordergrund der Entwicklung. Details rund um Build- und Deploymentmanagement verlagert sich in die Laufzeitumgebung.
Integrations
Camel K Anwendungen werden Integrations
genannt. Diese Integrations haben einen Lifecycle. Sie können z.B. gestartet und wieder gelöscht werden. Alle Aufgaben werden über das CLI-Tool kamel
erledigt.
Components
Components sind die Camel-Adapter, die in Camel-Routen verwendet werden. Sie können entweder programmatisch in der Route oder mit Hilfe von Properties konfiguriert werden.
Konfiguration
Properties von Integrationen können in Properties-Dateien oder in ConfigMaps bzw. Secrets ausgelagert werden. Der jeweilige Wert wird dann in der Camel-Route über eine Expression ermittelt.
Beispiel: props.groovy
from('timer:props?period=1000').log('{{my.message}}')
Eine entsprechende ConfigMap für den Wert von my.message
könnte dann so aussehen:
my-config.yaml
apiVersion: v1 kind: ConfigMap metadata: name: my-config data: application.properties: | my.message=Hello World logging.level.org.apache.camel=DEBUG
Die ConfigMap muss dann erst in Kubernetes angelegt werden und kann anschließend beim Start der Integration
referenziert werden.
kubectl apply -f my-config.yaml kamel run --configmap=my-config props.groovy
Plattform Konzepte
Das folgende Diagramm gibt einen groben Überlick über die Architektur:
Die wesentlichen Plattform-Elemente setzen sich aus folgenden Komponenten zusammen:
- Operator
- Runtime
- Traits
Operator
Die Camel-K-Integration in Kubernetes erfolgt über einen Kubernetes-Operator. Operators sind Erweiterungen in Kubernetes, die sich auf Custom Resources als Extension der Kubernetes API beziehen.
Durch den Aufruf von kamel install
wird der camel-k-operator
in dem gewählen Kubernetes-Namespace installiert. Mit dieser Installation können Integrations
im Kubernetes-Namespace gestartet werden.
Runtime
Die Runtime kümmmert sich um die eigentliche Ausführung der Integrations
in Kubernetes. Die Default-Runtime ist Quarkus.
Traits
Über Traits werden „High Level Features“ in Camel K realisiert. Im Wesentlichen ist damit die Integration von Third-Party-Frameworks gemeint, wie z.B. Knative, OpenAPI, Prometheus etc, oder Basis-Funktionen der Laufzeitumgebung, z.B. Routen und Services in Kubernetes bzw. OpenShift.
Traits haben Profile
und Eigentschaften
. Profile besagen auf welcher Laufzeitumgebung der jeweilige Trait anwendbar ist, z.B. Kubernetes
oder OpenShift
.
Eigenschaften eines Traits werden über Properties konfiguriert, mit der weitere individuelle Einstellungen möglich sind. Alle Traits haben beispielweise die Eigenschaft enabled
. Das Property folgt dem Namensschema TRAIT_NAME.enabled=true/false
, also z.B. istio.enabled=true
. Einige Traits sind standardmäßig aktiviert. Beispielsweise ist der Dependency Trait ein Platform Trait, der standardmäßig auf den Plattformen Kubernetes, Knative und OpenShift aktiviert ist.
Aktuell gibt es 31 Traits (Stand: 18.03.2021).
Abgrenzung zu Apache Camel
High-Level-Abstraktionen mit Kamelets
Kamelets (Kamel route snippets) basieren auf einem neuem Konzept von Adaptern, um auf externe Systeme zuzugreifen. Alle technischen Details werden dabei in einem Kamelet gekapselt und ermöglichen dadurch eine relativ einfache Verwendung in den Camel Routen. Alle Low-Level-Details werden so in wiederverwendbare Komponenten verpackt.
Ein Kamelet besteht aus einer Source
und einer Sink
, also der Definition einer Quelle und eines Ziels von einem externen System.
Verwendung von Kamelets in Camel Routen
Kamelets können wir normale Camel-Adapter in Routen verwendet werden. Dabei wird der kamelet
-Prefix als Adapter-Name verwendet. Beispiel:
from("kamelet:telegram-text-source?botToken=XXXXYYYY") .to("kamelet:my-company-log-sink/mynamedconfig")
Nach dem Prefix folgt der Name des Kamelets mit anschließenden Konfigurationsparametern.
Aufbau eines Kamelets
Ein Kamelet ist technisch gesehen eine Kubernetes Ressource. In ihrer Konfiguration im YAML-Format ist auch ein Routen-Template enthalten über die grundlegende Funktionsweise des Kamelets. Gekennzeichnet sind die Konfigurationsdateien durch das folgende Namensschema: yourkamelet.kamelet.yaml
.
Über optionale Kamelet Bindings
kann eine „Brücke“ zu anderen Integrationstechnologien, wie z.B. Knative hergestellt werden.
Weitere Details finden sich im Kamelets User Guide.
Testing
Tests mit YAKS
In der offiziellen Dokumentation findet man keinen Hinweis darauf, wie man Camel K Integrations
testen kann. Es ist aber zu erwarten, dass es eine Unterstützung für das Testing geben wird.
Laut dem Issue-Tracker von Camel K in GitHub wurde bereits danach gefragt. Voraussichtlich wird es eine offizielle Unterstützung auf Basis von YAKS geben, das wiederum auf dem Citrus-Framework basiert und eine Erweiterung für Cloud Native BDD Testing
auf der Kubernetes-Plattform darstellt.
YAKS Tests werden als BBD-Features auf Basis der Gherkin-Syntax geschrieben und geben eine definierte Struktur vor, wie sie auch in Cucumber-Tests verwendet wird.
Wie Camel K
selbst muss vor der ersten Testausführung der YAKS Operator
installiert werden. Das erfolgt, wie auch die spätere Testausführung, über die YAKS CLI.
Sobald die YAKS CLI
installiert ist, kann man den Operator mittels
yaks install
installieren. Anschließend können wir z.B. einen einfachen HelloWorld
-Test ausführen:
helloworld.feature
Feature: Hello Scenario: Print hello message Given print 'Hello from YAKS!'
Die erfolgreiche Installation kann mittels kubectl get customresourcedefinitions -l app=yaks
überprüft werden.
Mit
yaks test helloworld.feature
können wir nun unseren Test ausführen. Der Befehl erstellt als Ergebnis ein _output
-Verzeichnis mit den Testergebnisssen (helloworld.json
und junit-reports.xml
).
Die Tests selbst werden als ConfigMap
im jeweiligen Namespace in Kubernetes gespeichert.
CI/CD-Pipelines mit Tekton
CI/CD-Pipelines werden in Kubernetes über Tekton realisiert. Über die Tekton CLI kann man einzelne Tasks auf der Konsole starten.
Tasks sind einzelne Steps in einer Tekton-Pipeline. Dies ist ein einfaches HelloWorld-Beispiel:
task-hello.yaml
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: hello spec: steps: - name: hello image: ubuntu command: - echo args: - "Hello World!"
Mit dem Befehl kubectl apply -f task-hello.yaml
wird der Task angelegt und mit tkn task start hello
ausgeführt. Das Ergebnis kann man sich mit tkn taskrun logs --last -f
anzeigen lassen.
$ tkn taskrun logs --last -f [hello] Hello World!
Tekton Dashboard
Es gibt auch ein Tekton Dashboard, in dem man sich die Pipelines (ähnlich zu Jenkins) anzeigen lassen kann.
Mit folgenden Befehlen wird das Dashboard in Minikube installiert und über einen Proxy lokal zugreifbar gemacht:
kubectl apply --filename https://github.com/tektoncd/dashboard/releases/latest/download/tekton-dashboard-release.yaml kubectl proxy --port=8080
Anschließend kann das Dashboard im Browser unter http://localhost:8080/api/v1/namespaces/tekton-pipelines/services/tekton-dashboard:http/proxy aufgerufen werden. Unter Tasks
bzw. TaskRuns
kann man sich den Task anzeigen lassen.
Über einen Camel K Integration Task für Tekton kann Camel K direkt in Tekton Pipelines verwendet werden.
Unterschiede zwischen Camel und Camel K
Wenn eine Serverless-Architektur als Zielplattformumgebung gewünscht ist, so muss an dieser Stelle auf die umfrangreichen Integrationsmöglichkeiten von Apache Camel nicht verzichtet werden. Im Vergleich zu Apache Camel liegt hier allerdings der Fokus auf der eigentlichen Camel-Route mit deren Integration in die Kubernetes-Laufzeitumgebung sowie der Einbindung und Nutzung entsprechender Serverless- und Microservices-Frameworks, wie z.B. Knative und Quarkus.
Die klassischen Camel-Anwendungen werden üblicherweise als Binärartefakt deployt. Beispielsweise kann man eine Spring-Boot-Anwendung mit Camel aufsetzen und das FAT-JAR-Artefakt direkt starten oder in einem Docker-Container gekapselt betreiben. Das „Drumherum“ passiert also lokal auf dem Entwicker-Rechner (oder entsprechend in der CI/CD-System). Im Vergleich dazu beinhalten die Camel K Anwendungen „einfach“ nur die Camel-Routen-Definitionen als einzelne Datei im jeweils gewünschen Format (z.B. Groovy, Java, XML etc.“‹).
Im Endeffekt verlagern sich die Build- und Deployment-Aufgaben eher in Richtung Plattform und werden von dieser automatisch übernommen.
Im Folgenden sollen die Vor- und Nachteile von Camel K kurz zusammengefasst werden:
Vorteile von Camel K
- Source-Code auf das Nötigste (aka Camel-Route, Properties etc.) beschränkt
- automatische Ermittlung der Abhängigkeiten
- automatisches Build- und Deployment nach Kubernetes mit
kamel run...
- automatische Anlage von Services, Routen und ConfigMaps etc.
- kein separates Dockerfile oder Source-2-Image(S2I)-Build notwendig
- DEV-Mode propagiert automatisch alle Änderungen auch in den laufenden Pod
- Servlerless-Ansatz (in Verbindung mit Knative oder OpenShift Serverless)
- kurze Startzeiten
- Ressourcen werden geschont, wenn sie nicht benötigt werden
- geringer Speicherverbrauch
- Ausführung bei Bedarf
Nachteile von Camel K
- Verlagerung der Komplexität in die Laufzeitumgebung
- höhere Abhängigkeit zur Laufzeitumgebung während der Entwicklung
- Fehlersuche kann evtl. recht mühsam werden
- Konfigurationsdateien sind ggf. „verstreut“ im System (Abbildung von Properties in ConfigMaps)
- (noch) relativ neu
- Einige Teile von Camel K oder Knative befinden sich noch im Alpha-Stadium
- Dokumentation (noch) unvollständig
- setzt grundsätzlich Kubernetes/OpenShift als Laufzeitumgebung voraus
Observability
Monitoring
Das Camel K Monitoring wird über eine Integration mit Prometheus unterstützt. Dafür gibt es einen speziellen Prometheus Operator. Das Projekt befindet sich allerdings noch im Beta-Status (Stand: 16.03.2021).
Der Prometheus Operator
wird analog wie der camel k operator
als Ressource in einem Kubernetes-Namespace installiert. Alle Applikationen, die in dem Namespace laufen, in der auch der Prometheus Operator läuft, können damit überwacht werden.
Beim der Installation des camel k operator
kann das Monitoring auch direkt für alle Integrationen aktiviert werden:
kamel install --monitoring=true --monitoring-port=8888
Metriken
Mit dem Prometheus Trait wird der Prometheus-kompatible Endpunkt konfiguriert.
Mittels der Camel Quarkus MicroProfile Metrik Extension werden die Metriken im OpenMetric
-Format bereitgestellt. Neben JVM und OS-bezogenen Metriken gibt es ein paar Camel K spezifische Metriken. Dazu zählen z.B.
- Anzahl an Routen
- Anzahl an ausgeführten Routen
- gesamte Anzahl an erfolgreich ausgeführten Exchanges
- gesamte Anzahl an fehlerhaft ausgeführten Exchanges
- …
Weitere Details siehe Camel K Integration Monitoring.
Logging
Camel K verwendet Log4j 2 als Logging-Framework. Der Logging Level kann über Properties angepasst werden. Der folgende Befehlt setzt z.B. das Log-Level auf DEBUG
:
kamel run --property logging.level.org.apache.camel=DEBUG helloworld.groovy
Service Tracing
Es gibt einen Tracing Trait, der Tracing-Informationen an einen OpenTracing-kompatiblen Collector schicken kann und auch Jaeger unterstützt. Der Tracing Trait muss separat aktiviert und konfiguriert werden, z.B.
kamel run --trait tracing.enabled=true --trait tracing.auto=true helloworld.groovy
Tooling
Extensions für Visual Studio Code
Für Visual Studio Code gibt es das Tooling for Apache Camel K by Red Hat. Damit kann man neue Camel-K-Dateien anlegen und auch deployen bzw. löschen lassen.
In einem Explorer-Fenster werden die Camel-K-Integrations angezeigt.
Fazit
Mit Camel K wurde das beliebte Apache Camel Framework auf die Kubernetes Plattform portiert und fungiert damit als leichtgewichtige ESB-Alternative, das die Vorzüge dieser Plattform versucht bestmöglich auszunutzen. Es positioniert sich als Integrations-Framework für Serverless- und Microservices-Architekturen und integriert dabei Frameworks, die die gleiche Laufzeitumgebung zum Ziel haben (z.B. Quarkus, Knative etc.).
Wer sich eine Serverless-Architektur zunutze machen will, der sollte einen Blick auf Camel K werfen. Durch den geringen Ressourcen-Footprint, den Serverless-Anwendungen mit sich bringen, lassen sich durch den Einsatz gerade in Cloud-Umgebungen Kosten sparen und in On-Premises-Umgebungen zumindest Ressourcen effizienter nutzen.
Da das Framework noch relativ jung ist und viele Artefakte noch im Alpha-Status sind, sollte ein Einsatz in Produktivumgebungen noch mit Bedacht gewählt werden. Es ist aber zu erwarten, dass sich dieses Risiko im Laufe der Zeit minimieren wird. Zumindest erfreut sich das Projekt einer aktiven Community.
Da Camel K auf Kubernetes aufsetzt, sollte klar werden, dass ein lauffähiges Kubernetes-Cluster eine notwendige Voraussetzung für den erfolgreichen Einsatz ist. Das ist kein leichtes Unterfangen; hier sollte man sich im Klaren sein, dass der produktive Betrieb eines Kubernetes-Clusters eine durchaus technische Herausforderung sein kann. Dafür muss das benötigte Wissen im Unternehmen vorhanden sein. Selbst wenn man bei dem Betrieb auf einen Cloud-Provider ausweicht (z.B. AWS EKS oder Azure AKS), sollte der Betrieb eines Kubernetes-Clusters nicht unterschätzt werden.
Zwar vereinfacht sich die lokale Entwicklung durch Fokussierung auf die reinen Camel-Routen, allerdings verschiebt sich dadurch auch die Komplexität im Hinblick auf die Konfigurationseinstellungen in Richtung des Kubernetes-Clusters selbst. Hier sind einige Konfigurationen vorzunehmen, um alle benötigten Voraussetzungen in Kubernetes zu erfüllen (z.B. Installation und Konfiguration des Camel K Operators, Aktivierung von Traits, Properties etc.). Im Testing-Bereich gibt es erste Ansätze für Integration-Testing mit Citrus und YAKS. Für Unit-Tests muss allerdings auf die „klassische“ Vorgehensweise zurückgegriffen werden.
Alles in Allem hat Camel K
das Potenzial für eine leichtgewichtige ESB-Alternative. Es bleibt abzuwarten, wie sich das Framework weiterentwickelt und auf welche Akzeptanz es in Zukunft in der Praxis stoßen wird.
Links: