Apache Karaf : approche statique & dynamique

Apache Karaf : approche statique & dynamique

Apache Karaf™ n’est pas seulement un serveur, il peut être aussi utilisé de manière « statique ». La plupart des utilisateurs connaissent la distribution « standard ». Découvrez les autres approches de la solution.

Adaptation de l’article :  «Apache Karaf dynamic and static approach, docker and kubernetes» du blog Nanthrax écrit par Jean-baptiste Onofré (Technical Advisor)

yupiik-apache-karaf

L'approche dynamique (distribution "standard")

Vous pouvez télécharger la distribution « standard » ou « minimal » Apache Karaf™ ainsi vous obtenez une distribution dite dynamique. Par « dynamique », nous entendons qu’au démarrage nous lançons le runtime Karaf. Plus tard, vous pouvez y déployer des applications. La résolution est effectuée au moment de l’exécution, du déploiement. C’est une approche « conteneur d’application » (comme Apache Tomcat entre autre).

Vous pouvez créer votre propre runtime Karaf (en utilisant des fonctions de démarrage par exemple), où le conteneur démarre un ensemble d’applications.

Cependant ce n’est pas l’unique approche ! Apache Karaf est complètement est un runtime d’application polymorphe complet. Il peut prendre différentes formes pour répondre à vos attentes, utiliser des cas d’utilisation et répondre aux besoins devops.

L'approche statique (immuable)

Vous pouvez utiliser une approche dite statique avec Apache Karaf™.  Pour les utilisateurs de Spring-Boot, nous sommes sur une configuration similaire particulièrement pratique sur Docker et Cloud. La résolution se fait en temps réel lors de la construction.

Cette approche est légère, autonome – immuable et supporte toutes les fonctionnalités Karaf ! De nouveaux outils peuvent générer directement des fichiers docker ou même des images de fichiers docker.

Nous allons nous concentrer sur : comment créer une application s’exécutant dans Karaf « static » runtime ? 

L’élaboration des solutions ci-dessous sont des réponses aux demandes faites sur le GitHub de Jean-Baptiste Onofré.

Application

C’est à cet endroit que se trouve votre code d’entreprise. C’est une application régulière du Karaf.

Pour la démonstration, nous créerons un Servlet très simple. Le code, il utilise SCR pour exposer le Servlet en tant que service.

package org.apache.karaf.examples.docker;

import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component(
        property = { "alias=/servlet-example", "servlet-name=Example"}
)
public class ExampleServlet extends HttpServlet implements Servlet {

    private final static Logger LOGGER = LoggerFactory.getLogger(ExampleServlet.class);

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        LOGGER.info("Client " + request.getRemoteAddr() + " request received on " + request.getRequestURL());

        try (PrintWriter writer = response.getWriter()) {
            writer.println("<html>");
            writer.println("<head>");
            writer.println("<title>Example</title>");
            writer.println("</head>");
            writer.println("<body align='center'>");
            writer.println("<h1>Example Servlet</h1>");
            writer.println("</body>");
            writer.println("</html>");
        }
    }

}

Cette application est conditionnée sous forme de bundle défini dans pom.xml.

Comme nous avons besoin d’un XML de fonctionnalités à packager dans Karaf, nous utilisons karaf-maven-plugin features-generate-descriptor pour créer automatiquement le feature.xml :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.apache.karaf.examples</groupId>
        <artifactId>karaf-docker-example</artifactId>
        <version>4.3.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>karaf-docker-example-app</artifactId>
    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.cmpn</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.karaf.tooling</groupId>
                <artifactId>karaf-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>features-generate-descriptor</goal>
                        </goals>
                        <configuration>
                            <includeProjectArtifact>true</includeProjectArtifact>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Les fonctionnalités XML ont été générées dans le dossier cible et attachées au projet Maven.

Et voilà ! Nous avons un module d’application (qui peut être un service/micro-service), qui peut être déployé dans un conteneur Karaf (approche dynamique) ou dans un runtime Karaf « statique ».

L'assemblage runtime

Le runtime « statique » va assembler et packager un ensemble de modules d’application. Vous n’avez qu’à choisir les modules que vous souhaitez. L’assemblage est un simple fichier pom.xml contenant les étapes suivantes :

  1. assembly crée le système de fichiers d’exécution
  2. archive package le système d’exécution (généré par assembly) comme un zip et une archive tar.gz
  3. dockerfile crée un Dockerfile clé en main avec votre runtime
  4. docker peut utiliser directement Docker pour créer une image de docker en utilisant le dockerfile généré. Nous effectuons cette étape uniquement si le profil du docker est activé.

Ici le pom.xml complet

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
       <groupId>org.apache.karaf.examples</groupId>
        <artifactId>karaf-docker-example</artifactId>
        <version>4.3.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>karaf-docker-example-dist</artifactId>
    <packaging>pom</packaging>

    <dependencies>
        <dependency>
            <groupId>org.apache.karaf.features</groupId>
            <artifactId>static</artifactId>
            <type>kar</type>
        </dependency>

        <dependency>
            <groupId>org.apache.karaf.features</groupId>
            <artifactId>standard</artifactId>
            <classifier>features</classifier>
            <type>xml</type>
        </dependency>

        <dependency>
            <groupId>org.apache.karaf.services</groupId>
            <artifactId>org.apache.karaf.services.staticcm</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.karaf.examples</groupId>
            <artifactId>karaf-docker-example-app</artifactId>
            <type>xml</type>
            <classifier>features</classifier>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.karaf.tooling</groupId>
                <artifactId>karaf-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>process-resources</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>assembly</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>package</id>
                        <goals>
                            <goal>archive</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>dockerfile</id>
                        <goals>
                            <goal>dockerfile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <startupFeatures>
                        <startupFeature>static-framework</startupFeature>
                        <startupFeature>scr</startupFeature>
                        <startupFeature>http-whiteboard</startupFeature>
                        <startupFeature>karaf-docker-example-app</startupFeature>
                    </startupFeatures>
                    <framework>static</framework>
                    <useReferenceUrls>true</useReferenceUrls>
                    <environment>static</environment>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>docker</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.karaf.tooling</groupId>
                        <artifactId>karaf-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>docker-image</id>
                                <goals>
                                    <goal>docker</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

</project>

Ce que nous soulignons de ce pom.xml :

  • la fonction startupFeatures contient le static-framework qui est le runtime « statique » de base. Ensuite, nous ajoutons les caractéristiques requises pour notre application : scr et http-whiteboard. Enfin, nous ajoutons également « notre » fonctionnalité d’application générée : karaf-docker-exemple-app.
  • L’environment est static, ce qui signifie que la résolution est effectuée au moment de la compilation.
  • Le useReferenceUrls désactive le support Maven et utilise directement le jar/resources qui se trouve dans le dossier runtime du system.

Nous construisons maintenant le projet avec une simple mvn clean install.

Dans le dossier dist/target, nous pouvons trouver :

  • le répertoire assembly contenant le système de fichiers d’exécution
  • les archives zip et tar.gz
  • le Dockerfile prêt à l’emploi pour le runtime, y compris votre application
$ ls target
assembly
Dockerfile
karaf-docker-example-dist-4.3.0-SNAPSHOT.tar.gz
karaf-docker-example-dist-4.3.0-SNAPSHOT.zip

Nous pouvons utiliser localement le runtime assembly ou d’archivage.

Utilisons l’archive tar.gz. D’abord nous l’extrayons :

$ tar zxvf karaf-docker-example-dist-4.3.0-SNAPSHOT.tar.gz
$ cd karaf-docker-example-dist-4.3.0-SNAPSHOT/

Nous allons dans le répertoire bin et nous utilisons karaf run pour démarrer le runtime

$ cd bin
$ ./karaf run
Mar 21, 2019 4:47:16 PM org.apache.karaf.main.Main launch
INFO: Installing and starting initial bundles
Mar 21, 2019 4:47:16 PM org.apache.karaf.main.Main launch
INFO: All initial bundles installed and set to start
Mar 21, 2019 4:47:16 PM org.apache.karaf.main.Main$KarafLockCallback lockAcquired
INFO: Lock acquired. Setting startlevel to 100
16:47:17.002 INFO  [FelixStartLevel] Logging initialized @892ms to org.eclipse.jetty.util.log.Slf4jLog
16:47:17.014 INFO  [FelixStartLevel] EventAdmin support is not available, no servlet events will be posted!
16:47:17.015 INFO  [FelixStartLevel] LogService support enabled, log events will be created.
16:47:17.016 INFO  [FelixStartLevel] Pax Web started
16:47:17.282 INFO  [paxweb-config-1-thread-1] No ALPN class available
16:47:17.282 INFO  [paxweb-config-1-thread-1] HTTP/2 not available, creating standard ServerConnector for Http
16:47:17.299 INFO  [paxweb-config-1-thread-1] Pax Web available at [0.0.0.0]:[8181]
16:47:17.304 INFO  [paxweb-config-1-thread-1] Binding bundle: [org.ops4j.pax.web.pax-web-extender-whiteboard [48]] to http service
16:47:17.316 INFO  [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.examples.karaf-docker-example-app [15]] to http service
16:47:17.329 INFO  [paxweb-config-1-thread-1] will add org.apache.jasper.servlet.JasperInitializer to ServletContainerInitializers
16:47:17.330 INFO  [paxweb-config-1-thread-1] Skipt org.apache.jasper.servlet.JasperInitializer, because specialized handler will be present
16:47:17.330 INFO  [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer to ServletContainerInitializers
16:47:17.383 INFO  [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer
16:47:17.383 INFO  [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer to ServletContainerInitializers
16:47:17.383 INFO  [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer
16:47:17.422 INFO  [paxweb-config-1-thread-1] registering context DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default], with context-name: 
16:47:17.436 INFO  [paxweb-config-1-thread-1] registering JasperInitializer
16:47:17.466 INFO  [paxweb-config-1-thread-1] No DecoratedObjectFactory provided, using new org.eclipse.jetty.util.DecoratedObjectFactory[decorators=1]
16:47:17.540 INFO  [paxweb-config-1-thread-1] DefaultSessionIdManager workerName=node0
16:47:17.540 INFO  [paxweb-config-1-thread-1] No SessionScavenger set, using defaults
16:47:17.541 INFO  [paxweb-config-1-thread-1] node0 Scavenging every 600000ms
16:47:17.551 INFO  [paxweb-config-1-thread-1] Started HttpServiceContext{httpContext=DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default]}
16:47:17.557 INFO  [paxweb-config-1-thread-1] jetty-9.4.12.v20180830; built: 2018-08-30T13:59:14.071Z; git: 27208684755d94a92186989f695db2d7b21ebc51; jvm 1.8.0_181-b13
16:47:17.602 INFO  [paxweb-config-1-thread-1] Started default@26472f57{HTTP/1.1,[http/1.1]}{0.0.0.0:8181}
16:47:17.602 INFO  [paxweb-config-1-thread-1] Started @1500ms
16:47:17.605 INFO  [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.http.core [16]] to http service

Nous pouvons voir le runtime qui s’exécute dans notre application.

Dans un navigateur, vous pouvez accéder à l’exemple de servlet sur http://localhost:8181/servlet-example.

Ensuite, nous pouvons voir dans le journal :

16:48:19.132 INFO  [qtp1285811510-36] Client 0:0:0:0:0:0:0:1 request received on http://localhost:8181/servlet-example

Désormais, notre runtime fonctionne en local, nous utilisons la forme docker de celui-ci.

Découvrez prochainement la suite de cet article – Apache Karaf : Docker, Kubernetes

Bloqué dans vos roadmaps ?

Vous souhaitez former vos équipes ?

fr_FRFrançais
en_GBEnglish (UK) fr_FRFrançais