This example is a complete example how to easily create a Karaf powered application and package as a docker image, ready to run anyway.
This example provides two kind of distributions:
The project example is composed of three modules:
karaf-docker-example-app
is a regular Karaf application module. For this example, we implement a Servlet. We use the Karaf Maven plugin to automatically create a features XML that we will use for the distribution.karaf-docker-example-static-dist
doesn't contain any code, just a pom.xml
. This module creates a Karaf static distribution (almost immutable) ready to run, provide a turnkey Dockerfile and optionally create the corresponding Docker image.karaf-docker-example-dynamic-dist
doesn't contain any code, just a pom.xml
. This modules creates a Karaf dynamic distribution (applications container) ready to run, provide a turnkey Dockerfile and optionally create the corresponding Docker image.The karaf-docker-example-app
is a regular Karaf application (packaged as a bundle). It uses SCR annotation to register a Servlet
service that the http-whiteboard
feature will deploy.
It's possible to create a bunch of app
modules like this one and include in several runtime.
To include the apps in the runtime (dist), we need a features XML file.
In the example, we simply use the Karaf Maven plugin features-generate-descriptor
goal to automatically create the features XML.
The dist
modules create Karaf runtimes embedding the app.
The dynamic
distribution is the approach used by Apache Karaf vanilla distribution: it's a applications container approach.
You start the Karaf instance with “boot” applications, and then you can (at runtime) deploy new applications in the running instance. You can also update the configuration, undeploy applications, etc at runtime.
We use the karaf-maven-plugin
to create the distribution automatically:
assembly
goal creates the Karaf runtime filesystem, using the dependencies from the pom.xml
archive
goal creates an archive (tar.gz, zip) containing the Karaf runtime filesystemdockerfile
goal creates a Dockerfile ready to create a Karaf Docker imagedocker
goal can take the Dockerfile and directly use docker
to create the Karaf Docker image.On the other hand static
distribution is a completely different approach where the resolution is made at build time.
We use the karaf-maven-plugin
to create the distribution automatically:
assembly
goal creates the Karaf runtime filesystem, using the dependencies from the pom.xml
archive
goal creates an archive (tar.gz, zip) containing the Karaf runtime filesystemdockerfile
goal creates a Dockerfile ready to create a Karaf Docker imagedocker
goal can take the Dockerfile and directly use docker
to create the Karaf Docker image.The difference between dynamic and static distributions is just the configuration of the project and the karaf-maven-plugin
.
The build uses Apache Maven. Simply use:
$ mvn clean install
You will find the runtime in karaf-docker-example-dist/target
folder (tar.gz and zip), and also the Dockerfile
.
You can eventually directly create the Docker image using the docker
profile (assuming docker
is installed on your machine):
$ mvn clean install -Pdocker
Locally you can use karaf-docker-example-static-dist/target/assembly
, with the karaf run
:
$ karaf-docker-example-static-dist/target/assembly/bin/karaf run
You can also use the zip or tar.gz archive:
$ cd karaf-docker-example-static-dist/target $ tar zxvf karaf-docker-example-static-dist-*.tar.gz $ cd karaf-docker-example-static-dist*/bin $ ./karaf run
You can build the docker image using the generated Dockerfile
:
$ cd karaf-docker-example-static-dist/target $ docker build -t mykaraf . Sending build context to Docker daemon 78.08MB Step 1/7 : FROM openjdk:8-jre ---> d60154a7d9b2 Step 2/7 : ENV KARAF_INSTALL_PATH /opt ---> Using cache ---> ff77d7ccbfe3 Step 3/7 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf ---> Using cache ---> 0ebb21305f76 Step 4/7 : ENV PATH $PATH:$KARAF_HOME/bin ---> Using cache ---> 31bbe4c15205 Step 5/7 : COPY assembly $KARAF_HOME ---> Using cache ---> a9a88b63244e Step 6/7 : EXPOSE 8101 1099 44444 8181 ---> Using cache ---> 274550b95b48 Step 7/7 : CMD ["karaf", "run"] ---> Using cache ---> 7dbc9b63c058 Successfully built 7dbc9b63c058 Successfully tagged mykaraf:latest
If you used the docker
profile (-Pdocker
) during the Maven build, you have the karaf
docker image:
$ cd karaf-docker-example-static-dist $ mvn clean install -Pdocker
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE karaf-docker-example-static-dist latest 226223b45024 6 seconds ago 463MB
You can use the docker image very easily (wherever you want):
$ docker run --name mykaraf -p 8181:8181 mykaraf
You can start the docker container in daemon mode:
$ docker run --name mykaraf -p 8181:8181 -d mykaraf
We can see our docker container running:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d5f14a025286 karaf "karaf run" 4 minutes ago Up 4 minutes 1099/tcp, 8101/tcp, 8181/tcp, 44444/tcp mykaraf
Then you can see the logs using docker logs
:
$ docker logs mykaraf karaf: Ignoring predefined value for KARAF_HOME Mar 21, 2019 11:09:54 AM org.apache.karaf.main.Main launch INFO: Installing and starting initial bundles Mar 21, 2019 11:09:54 AM org.apache.karaf.main.Main launch INFO: All initial bundles installed and set to start Mar 21, 2019 11:09:54 AM org.apache.karaf.main.Main$KarafLockCallback lockAcquired INFO: Lock acquired. Setting startlevel to 100 11:09:54.871 INFO [FelixStartLevel] Logging initialized @811ms to org.eclipse.jetty.util.log.Slf4jLog 11:09:54.882 INFO [FelixStartLevel] EventAdmin support is not available, no servlet events will be posted! 11:09:54.883 INFO [FelixStartLevel] LogService support enabled, log events will be created. 11:09:54.885 INFO [FelixStartLevel] Pax Web started 11:09:55.110 INFO [paxweb-config-1-thread-1] No ALPN class available 11:09:55.111 INFO [paxweb-config-1-thread-1] HTTP/2 not available, creating standard ServerConnector for Http 11:09:55.127 INFO [paxweb-config-1-thread-1] Pax Web available at [0.0.0.0]:[8181] 11:09:55.131 INFO [paxweb-config-1-thread-1] Binding bundle: [org.ops4j.pax.web.pax-web-extender-whiteboard [48]] to http service 11:09:55.143 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.examples.karaf-docker-example-app [15]] to http service 11:09:55.153 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer to ServletContainerInitializers 11:09:55.154 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer 11:09:55.154 INFO [paxweb-config-1-thread-1] will add org.apache.jasper.servlet.JasperInitializer to ServletContainerInitializers 11:09:55.155 INFO [paxweb-config-1-thread-1] Skipt org.apache.jasper.servlet.JasperInitializer, because specialized handler will be present 11:09:55.155 INFO [paxweb-config-1-thread-1] will add org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer to ServletContainerInitializers 11:09:55.207 INFO [paxweb-config-1-thread-1] added ServletContainerInitializer: org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer 11:09:55.241 INFO [paxweb-config-1-thread-1] registering context DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default], with context-name: 11:09:55.251 INFO [paxweb-config-1-thread-1] registering JasperInitializer 11:09:55.282 INFO [paxweb-config-1-thread-1] No DecoratedObjectFactory provided, using new org.eclipse.jetty.util.DecoratedObjectFactory[decorators=1] 11:09:55.354 INFO [paxweb-config-1-thread-1] DefaultSessionIdManager workerName=node0 11:09:55.354 INFO [paxweb-config-1-thread-1] No SessionScavenger set, using defaults 11:09:55.355 INFO [paxweb-config-1-thread-1] node0 Scavenging every 600000ms 11:09:55.364 INFO [paxweb-config-1-thread-1] Started HttpServiceContext{httpContext=DefaultHttpContext [bundle=org.apache.karaf.examples.karaf-docker-example-app [15], contextID=default]} 11:09:55.369 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 11:09:55.403 INFO [paxweb-config-1-thread-1] Started default@437b18f2{HTTP/1.1,[http/1.1]}{0.0.0.0:8181} 11:09:55.403 INFO [paxweb-config-1-thread-1] Started @1347ms 11:09:55.405 INFO [paxweb-config-1-thread-1] Binding bundle: [org.apache.karaf.http.core [16]] to http service
Now, in a web brower, you can test the Servlet using [http://localhost:8181/servlet-example].
You can push this docker image wherever you want, on a cloud provider for instance.
We can see the logs updated:
$ docker logs mykaraf ... 11:23:39.064 INFO [qtp810811468-37] Client 172.17.0.1 request received on http://localhost:8181/servlet-example
Locally you can use karaf-docker-example-dynamic-dist/target/assembly
, with the karaf
:
$ karaf-docker-example-dynamic-dist/target/assembly/bin/karaf
You can also use the zip or tar.gz archive:
$ cd karaf-docker-example-dynamic-dist/target $ tar zxvf karaf-docker-example-dynamic-dist-*.tar.gz $ cd karaf-docker-example-dynamic-dist*/bin $ ./karaf
You can build the docker image using the generated Dockerfile
:
$ cd karaf-docker-example-dynamic-dist/target $ docker build -t mykaraf . Sending build context to Docker daemon 102.5MB Step 1/7 : FROM openjdk:8-jre ---> 19c48cc84cc6 Step 2/7 : ENV KARAF_INSTALL_PATH /opt ---> Running in 8a9a42db2395 Removing intermediate container 8a9a42db2395 ---> 45f002fb0def Step 3/7 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf ---> Running in c25e2a10d78e Removing intermediate container c25e2a10d78e ---> ce1f0d7bdb0a Step 4/7 : ENV PATH $PATH:$KARAF_HOME/bin ---> Running in 0fd126ff3a5f Removing intermediate container 0fd126ff3a5f ---> 3ac6aaa5b072 Step 5/7 : COPY assembly $KARAF_HOME ---> 9a6a003d9845 Step 6/7 : EXPOSE 8101 1099 44444 8181 ---> Running in 8e24a41487fb Removing intermediate container 8e24a41487fb ---> 5643c771527c Step 7/7 : CMD ["karaf"] ---> Running in 0e35ac21467d Removing intermediate container 0e35ac21467d ---> d0cbfa94125c Successfully built d0cbfa94125c Successfully tagged mykaraf:latest
If you used the docker
profile (-Pdocker
) during the Maven build, you have the karaf
docker image:
$ cd karaf-docker-example-dynamic-dist $ mvn clean install -Pdocker
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE karaf-docker-example-dynamic-dist latest 38628803d8b2 3 seconds ago 479MB
You can use the docker image very easily (wherever you want):
$ docker run -i -t --name mykaraf -p 8181:8181 mykaraf karaf: Ignoring predefined value for KARAF_HOME __ __ ____ / //_/____ __________ _/ __/ / ,< / __ `/ ___/ __ `/ /_ / /| |/ /_/ / / / /_/ / __/ /_/ |_|\__,_/_/ \__,_/_/ Apache Karaf (4.3.0-SNAPSHOT) Hit '<tab>' for a list of available commands and '[cmd] --help' for help on a specific command. Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf. karaf@root()>
We started here with the shell console. You can directly type Karaf shell commands here and interact the running Karaf instance.
karaf server
.Now, in a web brower, you can test the Servlet using [http://localhost:8181/servlet-example].
we can see the request performed on our running Karaf instance:
karaf@root()> log:display 08:43:36.497 INFO [qtp1056947824-99] Client 172.17.0.1 request received on http://localhost:8181/servlet-example