Most of the Karaf users know the Apache Karaf “standard” distribution.
Apache Karaf dynamic approach (“standard” distribution)
You can download Apache Karaf “standard” or “minimal” distributions, you download a “dynamic” distribution.
By “dynamic”, it means that you start the Karaf runtime and, later, you deploy applications in it.
The resolution is performed at runtime, at deployment time. It’s a “application container” approach (like Apache Tomcat, …).
You can create your own custom Karaf runtime (using boot features for instance), where the container starts a set of applications at bootstrap.
However, it’s not the only approach ! Apache Karaf is a complete polymorphic application runtime, meaning that it can take different form to match your expectations, use cases and devops requirements.
Apache Karaf static approach (likely immutable)
You can use a “static” approach with Apache Karaf. It’s similar to kind of spring-boot bootstrap and especially very convenient used with docker and on the cloud.
The resolution is made at build time, predictable.
This approach is super light, standalone/immutable while supporting all Karaf features !
We also have new tools coming that can directly generate dockerfile or even docker images.
In this blog, I will show how to create a application running in Karaf “static” runtime.
As I have some work in progress, this blog is based on the current pull request: https://github.com/apache/karaf/pull/789.
Your application
That’s where actually your business code will be located. It’s a regular Karaf application.
For the demo, I’m creating a very simple Servlet
.
The code is pretty simple: it uses SCR to expose the Servlet
as a 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>"); } } }
This application is packaged as a bundle as defined in the pom.xml
. As we need a features XML to package in Karaf, we use the karaf-maven-plugin
features-generate-descriptor
goal to automatically create the 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>
The features XML has been generated in the target
folder and attached to the Maven project.
That’s it ! We now have an application module (that could be a service/micro-service as well) that you can either deploy in the regular Karaf container (dynamic approach) or a Karaf “static” runtime.
The runtime assembly
The “static” runtime will assemble and package a set of application modules. You just pick the modules you want.
The assembly is a simple pom.xml
containing the following steps:
assembly
creates the runtime filesystemarchive
packages the runtime filesystem (generated byassembly
) as a zip and tar.gz archivedockerfile
creates a turnkeyDockerfile
with your runtimedocker
optionally directly use Docker to create a docker image using the generatedDockerfile
. We do this step only if thedocker
profile is enabled.
Here’s the complete pom.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-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>
We can note in this pom.xml
:
- the
startupFeatures
contains thestatic-framework
which is the core “static” runtime. Then we add the prerequisite features for our application:scr
andhttp-whiteboard
. Finally, we also add “our” generated application feature:karaf-docker-example-app
. - the
environment
isstatic
meaning that the resolution is performed at build time - the
useReferenceUrls
disable Maven support and directly use the jar/resources populated in the runtimesystem
folder.
We now build the project with a simple mvn clean install
.
In the dist/target
folder, we can find:
- the
assembly
directory containing the runtime filesystem - the zip and tar.gz archives
- the ready to use
Dockerfile
for the runtime including your application
$ ls target assembly Dockerfile karaf-docker-example-dist-4.3.0-SNAPSHOT.tar.gz karaf-docker-example-dist-4.3.0-SNAPSHOT.zip
We can locally use the assembly
or archive runtime.
Let’s use the tar.gz archive.
First we extract the tar.gz:
$ tar zxvf karaf-docker-example-dist-4.3.0-SNAPSHOT.tar.gz $ cd karaf-docker-example-dist-4.3.0-SNAPSHOT/
We go into the bin
folder and we can use karaf run
to start the 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
We can see the runtime starting with our application.
In a browser, you can access the example servlet on http://localhost:8181/servlet-example.
Then, we can see in the log:
16:48:19.132 INFO [qtp1285811510-36] Client 0:0:0:0:0:0:0:1 request received on http://localhost:8181/servlet-example
Our runtime is running locally, now, let’s use the docker form of our runtime.
Docker
If you have Docker installed on your machine (the machine where you build), you can use the docker
profile to directly create the Docker image:
$ mvn clean install -Pdocker
If you don’t have docker on your machine, the build at least creates a Dockerfile
. By default, the Docker image name is karaf
, but you can pass the image name using the imageName
configuration:
<configuration> <imageName>${project.artifactId}</imageName> </configuration>
You can use this Dockerfile
(and the whole target
) folder to create the Docker image with:
$ cd target $ docker build -t mykaraf . Sending build context to Docker daemon 57.41MB Step 1/7 : FROM openjdk:8-jre ---> d60154a7d9b2 Step 2/7 : ENV KARAF_INSTALL_PATH /opt ---> Running in 9518c5e2141e Removing intermediate container 9518c5e2141e ---> c49033d75fef Step 3/7 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf ---> Running in 6a8f314162ea Removing intermediate container 6a8f314162ea ---> 6bd1124f27c9 Step 4/7 : ENV PATH $PATH:$KARAF_HOME/bin ---> Running in ab00f87fda1d Removing intermediate container ab00f87fda1d ---> cfa06b1e5bce Step 5/7 : COPY assembly $KARAF_HOME ---> c74c5a3adda3 Step 6/7 : EXPOSE 8101 1099 44444 8181 ---> Running in 667de77413bc Removing intermediate container 667de77413bc ---> ee720e290d7f Step 7/7 : CMD ["karaf", "run"] ---> Running in d283a0c53d93 Removing intermediate container d283a0c53d93 ---> 23eb3c781a39 Successfully built 23eb3c781a39 Successfully tagged mykaraf:latest
You have a Docker image ready:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mykaraf latest 23eb3c781a39 26 seconds ago 463MB
If you used the docker
profile, you have a karaf
Docker image ready:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE karaf latest f12b3148c33e 3 seconds ago 463MB
Now, you can run a Docker container using this image:
$ docker run --name mykaraf -p 8181:8181 karaf karaf: Ignoring predefined value for KARAF_HOME Mar 21, 2019 3:55:03 PM org.apache.karaf.main.Main launch INFO: Installing and starting initial bundles Mar 21, 2019 3:55:03 PM org.apache.karaf.main.Main launch INFO: All initial bundles installed and set to start Mar 21, 2019 3:55:03 PM org.apache.karaf.main.Main$KarafLockCallback lockAcquired INFO: Lock acquired. Setting startlevel to 100 15:55:04.287 INFO [FelixStartLevel] Logging initialized @957ms to org.eclipse.jetty.util.log.Slf4jLog 15:55:04.298 INFO [FelixStartLevel] EventAdmin support is not available, no servlet events will be posted! 15:55:04.299 INFO [FelixStartLevel] LogService support enabled, log events will be created. 15:55:04.301 INFO [FelixStartLevel] Pax Web started 15:55:04.515 INFO [paxweb-config-1-thread-1] No ALPN class available 15:55:04.515 INFO [paxweb-config-1-thread-1] HTTP/2 not available, creating standard ServerConnector for Http 15:55:04.531 INFO [paxweb-config-1-thread-1] Pax Web available at [0.0.0.0]:[8181] 15:55:04.536 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.http.core [16]] to http service 15:55:04.552 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.examples.karaf-docker-example-app [15]] to http service 15:55:04.564 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer to ServletContainerInitializers 15:55:04.564 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer 15:55:04.565 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer to ServletContainerInitializers 15:55:04.618 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer 15:55:04.619 INFO [paxweb-config-1-thread-1] will add org.apache.jasper.servlet.JasperInitializer to ServletContainerInitializers 15:55:04.619 INFO [paxweb-config-1-thread-1] Skipt org.apache.jasper.servlet.JasperInitializer, because specialized handler will be present 15:55:04.655 INFO [paxweb-config-1-thread-1] registering context DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default], with context-name: 15:55:04.671 INFO [paxweb-config-1-thread-1] registering JasperInitializer 15:55:04.716 INFO [paxweb-config-1-thread-1] No DecoratedObjectFactory provided, using new org.eclipse.jetty.util.DecoratedObjectFactory[decorators=1] 15:55:04.801 INFO [paxweb-config-1-thread-1] DefaultSessionIdManager workerName=node0 15:55:04.802 INFO [paxweb-config-1-thread-1] No SessionScavenger set, using defaults 15:55:04.803 INFO [paxweb-config-1-thread-1] node0 Scavenging every 600000ms 15:55:04.814 INFO [paxweb-config-1-thread-1] Started HttpServiceContext{httpContext=DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default]} 15:55:04.820 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-8u181-b13-2~deb9u1-b13 15:55:04.864 INFO [paxweb-config-1-thread-1] Started default@28e475cc{HTTP/1.1,[http/1.1]}{0.0.0.0:8181} 15:55:04.865 INFO [paxweb-config-1-thread-1] Started @1539ms 15:55:04.867 INFO [paxweb-config-1-thread-1] Binding bundle: [org.ops4j.pax.web.pax-web-extender-whiteboard [48]] to http service
By default, the runtime executes in foreground. We can use -d
to run in daemon mode:
$ docker run --name mykaraf -p 8181:8181 -d karaf c05645357cd17a0828ef7acaf619071cc3c94f316ca605217890371c0c1e4ab0
We can see our container running:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c05645357cd1 karaf "karaf run" 27 seconds ago Up 27 seconds 1099/tcp, 8101/tcp, 44444/tcp, 0.0.0.0:8181->8181/tcp mykaraf
We can see the log of our container:
$ docker logs mykaraf docker logs mykaraf karaf: Ignoring predefined value for KARAF_HOME Mar 21, 2019 3:56:45 PM org.apache.karaf.main.Main launch INFO: Installing and starting initial bundles Mar 21, 2019 3:56:45 PM org.apache.karaf.main.Main launch INFO: All initial bundles installed and set to start Mar 21, 2019 3:56:45 PM org.apache.karaf.main.Main$KarafLockCallback lockAcquired INFO: Lock acquired. Setting startlevel to 100 15:56:45.831 INFO [FelixStartLevel] Logging initialized @946ms to org.eclipse.jetty.util.log.Slf4jLog 15:56:45.844 INFO [FelixStartLevel] EventAdmin support is not available, no servlet events will be posted! 15:56:45.845 INFO [FelixStartLevel] LogService support enabled, log events will be created. 15:56:45.847 INFO [FelixStartLevel] Pax Web started 15:56:46.055 INFO [paxweb-config-1-thread-1] No ALPN class available 15:56:46.055 INFO [paxweb-config-1-thread-1] HTTP/2 not available, creating standard ServerConnector for Http 15:56:46.071 INFO [paxweb-config-1-thread-1] Pax Web available at [0.0.0.0]:[8181] 15:56:46.075 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.examples.karaf-docker-example-app [15]] to http service 15:56:46.093 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer to ServletContainerInitializers 15:56:46.093 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer 15:56:46.094 INFO [paxweb-config-1-thread-1] will add org.apache.jasper.servlet.JasperInitializer to ServletContainerInitializers 15:56:46.094 INFO [paxweb-config-1-thread-1] Skipt org.apache.jasper.servlet.JasperInitializer, because specialized handler will be present 15:56:46.094 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer to ServletContainerInitializers 15:56:46.132 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer 15:56:46.163 INFO [paxweb-config-1-thread-1] registering context DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default], with context-name: 15:56:46.174 INFO [paxweb-config-1-thread-1] registering JasperInitializer 15:56:46.203 INFO [paxweb-config-1-thread-1] No DecoratedObjectFactory provided, using new org.eclipse.jetty.util.DecoratedObjectFactory[decorators=1] 15:56:46.272 INFO [paxweb-config-1-thread-1] DefaultSessionIdManager workerName=node0 15:56:46.273 INFO [paxweb-config-1-thread-1] No SessionScavenger set, using defaults 15:56:46.274 INFO [paxweb-config-1-thread-1] node0 Scavenging every 660000ms 15:56:46.284 INFO [paxweb-config-1-thread-1] Started HttpServiceContext{httpContext=DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default]} 15:56:46.289 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-8u181-b13-2~deb9u1-b13 15:56:46.324 INFO [paxweb-config-1-thread-1] Started default@28e475cc{HTTP/1.1,[http/1.1]}{0.0.0.0:8181} 15:56:46.324 INFO [paxweb-config-1-thread-1] Started @1444ms 15:56:46.326 INFO [paxweb-config-1-thread-1] Binding bundle: [org.ops4j.pax.web.pax-web-extender-whiteboard [48]] to http service 15:56:46.328 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.http.core [16]] to http service
You can now access to http://localhost:8181/servlet-example in your browser.
Then you see the logs updated in the Docker container:
$ docker logs mykaraf ... 15:58:24.068 INFO [qtp117150641-37] Client 172.17.0.1 request received on http://localhost:8181/servlet-example
We can stop our Docker container:
$ docker stop mykaraf mykaraf
Running on AWS with Kubernetes
Now that we have our Docker image ready, we can push to AWS ECR (Docker container Registry).
First, we create a ECR repository on AWS:
Than, we tag and push our image to AWS ECR (using IAM user):
$ docker tag karaf:latest 295331841498.dkr.ecr.eu-west-1.amazonaws.com/karaf:latest $ aws ecr get-login --no-include-email --region eu-west-1 $ docker push 295331841498.dkr.ecr.eu-west-1.amazonaws.com/karaf:latest
We can now see our Karaf image on ECR:
Now that we have our Docker image on ECR, we can create cluster using it.
Let’s start with a simple ECS cluster.
Using ECS
ECS directly run docker containers (tasks).
We create a ECS cluster:
We add a new task there (Docker container):
We can see the public IP address on the task and so we can use it directly in a browser:
We can see the logs updated:
We can update the service to have multiple containers running:
We can see the service using 5 instances now:
Instead of ECS, you can use Kubernetes on EKS cluster.
Using EKS
First, let’s create the EKS cluster on AWS:
We can now access this cluster using our local kubectl
:
$ aws eks update-kubeconfig ... $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 50m
We now create some nodes in our cluster:
Once the nodes are part of our cluster, we create a POD descriptor with our Karaf image:
apiVersion: v1 kind: Pod metadata: name: karaf-docker-example-dist spec: containers: - name: karaf-docker-example-dist-ctr image: 295331841498.dkr.ecr.eu-west-1.amazonaws.com/karaf:latest resources: limits: memory: "500Mi" requests: memory: "250Mi" command: ["karaf", "run"] args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
Then, we create the POD in the EKS cluster:
$ kubectl create -f karaf-docker-example-dist.yaml pod/karaf-docker-example-dist created
We can see our POD boostrapping on EKS:
$ kubectl get pod NAME READY STATUS RESTARTS AGE karaf-docker-example-dist 0/1 Pending 0 2m
Conclusion
We can see here how Apache Karaf is flexible, supporting two completely different approaches:
- The “dynamic/container” approach (aka “standard” distribution) allows you to start Karaf as a “container” and deploy dynamically at runtime new applications.
- The “static” approach allows you to package all at build time and easily bootstrap your application powered by Karaf.
Your applications are able to run in both mode, it’s just a matter of assembly/packaging/distribution.
We can see here the “polymorphic” part of Apache Karaf, where you can use it on premise, on the cloud, running as a container, running as a bootstrapper, for small to large production platform.
In the coming releases, we will work to provide even better tooling for both dev and devops.
Stay tuned !