Apache Camel y Casandra en un cluster Kubernetes
Introducción
Eclipse JKube 1.0.0 se lanzará el nueve de septiembre de 2020. Tal como he comentado en publicaciones anteriores, JKube es el sucesor del ahora obsoleto Fabric8 Maven Plugin (FMP). En estos momentos, nuestro principal objetivo es migrar a los actuales usuarios de Fabric8 al nuevo proyecto. Alineados con esta estrategia, hemos comenzado a crear Pull Requests en aquellos repositorios que están empleando FMP en la actualidad para reemplazar dicha dependencia.
En esta publicación os enseñaré como ejecutar el ejemplo (actualizado a JKube) de Apache Camel, Cassandra running on Kubernetes, y como éste debería de comportarse. Este artículo está basado en el artículo original escrito por el gran Andrea Cosentino en 2016.
Poniendo a punto un cluster de Apache Cassandra en Kubernetes
El primer paso que debemos hacer es clonar el projecto de ejemplos de Apache Camel y navegar al directorio del ejemplo:
1git clone git@github.com:apache/camel-examples.git
2cd camel-examples/examples/camel-example-cassandra-kubernetes
En esta publicación os explicaré como ejecutar todo esto empleando Minikube. Del mismo modo, el ejemplo puede lanzarse en cualquier otro cluster compatible con Kubernetes.
Para arrancar el cluster de Apache Cassandra necesitamos desplegar los dos ficheros YAML que encontraremos en el directorio src/main/resources/jkube
:
1kubectl create -f src/main/resources/jkube/cassandra-service.yaml
2kubectl create -f src/main/resources/jkube/cassandra-statefulset.yaml
Después de unos minutos, los Pods definidos en el StatefulSet del cluster habrán arrancado. Podemos comprobar su estado ejecutando el siguiente comando:
1$ kubectl get pods
2NAME READY STATUS RESTARTS AGE
3cassandra-0 1/1 Running 0 3m35s
4cassandra-1 1/1 Running 0 98s
Cuando los dos Pods devuelvan el status Running
, el cluster de Apache Cassandra estará listo para su uso.
Apache Camel example
El ejemplo es una aplicación muy sencilla que emplea Apache Camel para definir una única ruta (route
) que trazará determinada información cada 5 segundos.
Java classes
El proyecto contiene 2 clases Java. La clase CqlPopulateBean
creará un Keyspace y una tabla en el cluster Cassandra que hemos desplegado en el paso anterior. Además añadirá 2 entradas en dicha tabla, todo ello cuando el método populate()
se invoque.
La clase RowProcessor
es una implementación de Apache Camel Processor que transformará la lista de filas obtenida por la consulta CQL en un String más amigable que podremos trazar.
Camel Context
A continuación podéis observar un extracto del fichero camel-context.xml
:
1<bean id="populate" class="org.apache.camel.example.kubernetes.jkube.CqlPopulateBean" init-method="populate"/>
2<bean id="rowProcessor" class="org.apache.camel.example.kubernetes.jkube.RowProcessor"/>
3<camelContext xmlns="http://camel.apache.org/schema/spring" depends-on="populate">
4<route id="cassandra-route">
5 <from uri="timer:foo?period=5000"/>
6 <to uri="cql://cassandra/test?datacenter=DC1-K8Demo&cql=SELECT * FROM users;&consistencyLevel=quorum" />
7 <process ref="rowProcessor"/>
8 <log message="Query result set [${body}]"/>
9</route>
10</camelContext>
El fichero define 2 beans y una única ruta.
El primer bean (populate
) se instanciará cuando arranque la aplicación, invocándose además el método populate
. Tal como os he descrito, esto añadirá 2 nuevas filas a la tabla users
.
El segundo bean (rowProcessor
) es una instancia de la clase RowProcessor
que también he detallado en el paso anterior.
Por último, podemos observar la definición de la ruta (cassandra-route
). La ruta se inicia por un temporizador programado para ejecutarse cada 5 segundos. A continuación se ejecuta una consulta CQL que lista todos los users
del Keyspace test
en el datacenter DC1-K8Demo
. El listado de resultados se procesa por el processor bean y el String resultante finalmente se traza.
Ejecutando el ejemplo
Ahora que ya conocemos todos los componentes de la aplicación de ejemplo podemos probarla.
Si vas a probar la aplicación en Minikube, primero tendrás que compartir el Docker Daemon con el sistema anfitrión ejecutando: eval $(minikube docker-env)
Nota: En caso que quieras ejecutar la aplicación en un cluster externo, necesitarás publicar la imagen Docker a un registry accesible por el cluster para que la imagen se pueda descargar.
Ya estamos listos para desplegar la aplicación, simplemente tenemos que ejecutar el siguiente comando:
1mvn clean -Pkubernetes-install k8s:deploy
El comando anterior empaquetará la aplicación, construirá la imagen Docker, creará los manifiestos de configuración de recursos Kubernetes y los desplegará en Minikube. Como podéis ver, el uso de Eclipse JKube facilita mucho todas estas tareas para el desarrollador.
A continuación podéis ver un extracto del pom.xml
con la configuración de Kubernetes Maven Plugin (JKube):
1<plugin>
2 <groupId>org.eclipse.jkube</groupId>
3 <artifactId>kubernetes-maven-plugin</artifactId>
4 <version>${jkube-version}</version>
5 <executions>
6 <execution>
7 <goals>
8 <goal>resource</goal>
9 <goal>build</goal>
10 </goals>
11 </execution>
12 </executions>
13</plugin>
Podéis observar que el plugin no requiere demasiada configuración. En este caso, la única configuración es para la ejecución del plugin que vincula los goals k8s:resource
y k8s:build
a la fase install de Maven.
Unos instantes después de ejecutar el comando, el Pod definido en el Deployment debería de estar arrancado.
1$ kubectl get pods
2NAME READY STATUS RESTARTS AGE
3camel-example-cassandra-kubernetes-644d4959b6-574nk 1/1 Running 0 78s
4cassandra-0 1/1 Running 0 32m
5cassandra-1 1/1 Running 0 30m
Ahora podemos extraer las trazas de la aplicación ejecutando el siguiente comando Maven, también proporcionado por JKube:
1mvn -Pkubernetes-install k8s:log
Si todo ha ido correctamente, podremos ver un output similar a este:
1GuavaCompatibility - Detected Guava >= 19 in the classpath, using modern compatibility layer
2ClockFactory - Using native clock to generate timestamps.
3NettyUtil - Did not find Netty's native epoll transport in the classpath, defaulting to NIO.
4DCAwareRoundRobinPolicy - Using data-center name 'DC1-K8Demo' for DCAwareRoundRobinPolicy (if this is incorrect, please provide the correct datacenter name with DCAwareRoundRobinPolicy constructor)
5Cluster - New Cassandra host cassandra/172.17.0.7:9042 added
6Cluster - New Cassandra host cassandra/172.17.0.6:9042 added
7CqlPopulateBean - Cassandra was populated with sample values for test.users table
8LRUCacheFactory - Detected and using LRUCacheFactory: camel-caffeine-lrucache
9AbstractCamelContext - Apache Camel 3.5.0-SNAPSHOT (camel-1) is starting
10AbstractCamelContext - StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
11AbstractCamelContext - Using HealthCheck: camel-health
12DefaultMavenCoordinates - DataStax Java driver for Apache Cassandra(R) (com.datastax.oss:java-driver-core) version 4.8.0
13Clock - Using native clock for microsecond precision
14InternalRouteStartupManager - Route: cassandra-route started and consuming from: timer://foo
15AbstractCamelContext - Total 1 routes, of which 1 are started
16AbstractCamelContext - Apache Camel 3.5.0-SNAPSHOT (camel-1) started in 4.435 seconds
17BaseMainSupport - Using properties from: classpath:application.properties;optional=true
18DefaultRoutesCollector - No additional Camel XML routes discovered from: classpath:camel/*.xml
19DefaultRoutesCollector - No additional Camel XML route templates discovered from: classpath:camel-template/*.xml
20DefaultRoutesCollector - No additional Camel XML rests discovered from: classpath:camel-rest/*.xml
21cassandra-route - Query result set [1-oscerd,2-not-a-bot]
22cassandra-route - Query result set [1-oscerd,2-not-a-bot]
En las últimas entradas del registro podemos ver las filas insertadas mediante la clase CqlPopulateBean
y que ahora se han extraído usado CQL y transformado empleando el RowProcessor
.
Conclusión
En esta publicación os he descrito el comportamiento del ejemplo Apache Camel Cassandra Kubernetes. Esta es una aplicación muy sencilla que muestra la potencia de Apache Camel, Apache Cassandra y Eclipse JKube empleados de forma combinada. Arquitecturas mucho más complejas para solucionar problemas más realistas podrían construirse perfectamente por encima de las premisas mostradas en este ejemplo.