Joery Vreijsen – 12 December 2016
1353 words in about 6 minutes

Karaf on Docker creates an OSGi environment which you can easily bootstrap and is also highly scalable. This post explains how to setup a Docker environment for a custom Karaf Distribution.

The power of Apache Karaf lays in the easy deployment of your ActiveMQ Broker, your CXF webservices, or your own Apache Camel routes. For this post we are going to set up our own simple camel-route using a Karaf Custom Distribution, which we will dockerize!

Docker is a great way to setup easy maintainable and highly scalable environments, if you want to learn more about Docker read these awesome blog posts, also by The Guild!

social media karaf docker

1. Create a Karaf Custom Distribution

To quickly create a Custom Karaf Distribution we can use the following Maven archetype.

1
2
3
4
5
6
7
8
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.karaf.archetypes \
  -DarchetypeArtifactId=karaf-assembly-archetype \
  -DarchetypeVersion=4.0.7 \
  -DgroupId=nl.theguild \
  -DartifactId=karaf-distro \
  -Dversion=1.0-SNAPSHOT \
  -Dpackage=nl.theguild.karaf-docker

This will give you a new folder karaf-distro where your new pom.xml is located. When we go to that folder we can compile our distribution with mvn clean install.

When we execute bin/karaf in our newly generated target/assembly directory we see our very own Karaf custom distribution.

1
2
3
4
5
6
7
8
9
10
11
12
13
        __ __                  ____
       / //_/____ __________ _/ __/
      / ,<  / __ `/ ___/ __ `/ /_
     / /| |/ /_/ / /  / /_/ / __/
    /_/ |_|\__,_/_/   \__,_/_/

  Apache Karaf (4.0.7)

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()>

1.1 Add the resources

Our custom distribution is quite clean, it has nothing much just the standard package that Karaf provides you. As we look in the pom.xml the archetype generated for us we see some resource paths defined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resources>
  <resource>
    <directory>src/main/resources</directory>
    <filtering>false</filtering>
    <includes>
      <include>**/*</include>
    </includes>
  </resource>
  <resource>
    <directory>src/main/filtered-resources</directory>
    <filtering>true</filtering>
    <includes>
      <include>**/*</include>
    </includes>
  </resource>
</resources>

We want to add the Camel features to our Karaf distribution on startup so we have to define it as a resource. All resource files that have Maven property values that have to be filtered/replaced, should go in src/main/filtered-resources/etc. So our org.apache.karaf.features.cfg file that contains our startup features, goes in this folder.

The file will look something like this:

1
2
3
4
5
featuresRepositories=mvn:org.apache.camel.karaf/apache-camel/2.17.4/xml/features, \
    mvn:org.apache.karaf.features/enterprise/4.0.7/xml/features, \
    mvn:org.apache.karaf.features/spring/4.0.7/xml/features

featuresBoot=management, shell-compat, camel

1.2 Add an Apache Camel route

Now that we have installed all the necessary dependencies we can actually start building our camel route!

For our example we are going to use a simple file-to-file route. The Camel route will look -something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
           http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

  <camelContext id="context-1" xmlns="http://camel.apache.org/schema/blueprint">
    <route id="route-1">
      <from uri="file:/tmp/in?autoCreate=true&amp;charset=utf-8"/>
      <to uri="file:/tmp/out?autoCreate=true&amp;charset=utf-8&amp;fileExist=Override"/>
    </route>
  </camelContext>

</blueprint>

You can test the camel-route by placing the xml in the target/assembly/deploy folder and placing a file in /tmp/in on your machine, to watch it get transferred to /tmp/out .

Another possibility is to create the src/main/resources/deploy folder in your project and place the camel-route there. The contents of this folder will get transferred to the deploy folder of Karaf at startup.

2. Create a Dockerfile

Now that we have our very own custom Karaf distribution we want to run it inside a Docker container. All we have to do in order to make this happen, is create a Dockerfile which builds our distribution.

Let’s break this down bit by bit, since Karaf runs on Java we want our image to be built from the Java 8 SDK base image.

1
FROM java:8-jdk

We then want to install Maven, and when done we want to clean up our apt-get. We could run this all in a separate RUN layer which Docker would translate in building three layers. The first layer would update our apt-get, the second layer would install Maven, and the third layer would clean things up. But since the previous two layers are already build by Docker this “clean” won’t actually clean the previous build layers. That’s why we want to update, install and clean up our apt-get all in the same layer, so in the end the image is as small as possible.

1
2
3
RUN apt-get update \
  && apt-get -y install maven\
  && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

Now we want to determine in which directory on the Docker container we want our Karaf distribution to live. Let’s take /opt/karaf for that, but we also need to create a temporary directory to store our source in, that will be used to build the distribution.

We set our working directory to the temporary directory and copy our source in.

1
2
3
4
5
6
RUN mkdir /opt/karaf;
RUN mkdir /tmp/src;

WORKDIR /tmp/src/

ADD . .

Now that we have setup our folder structure we can build our distribution. When built, we unpack our generated distribution to the destination folder /opt/karaf and remove our temporary folder where we stored our source code. All in the same layer again.

1
2
3
RUN mvn clean install; \
    tar --strip-components=1 -C /opt/karaf -xzf target/karaf-distro-1.0-SNAPSHOT.tar.gz; \
    rm -rf /tmp/*

Docker allows us to view the logging of containers using the docker logs command however, to make this work we have to make Karaf log to stdout.

1
2
RUN chmod 755 /opt; \
    sed -i "21s/out/stdout/" /opt/karaf/etc/org.ops4j.pax.logging.cfg;

Next we want to volume our deploy folder so we can mount it on our host and keep the data when we stop or remove our container and restart it.

1
VOLUME ["/opt/karaf/deploy"]

Now lets expose the ports that Karaf uses by default, and define our main process of the Docker container. The main process decides when the container should stop or restart, when the main process stops, the container stops.

1
2
3
EXPOSE 1099 8101 44444

CMD ["/opt/karaf/bin/karaf"]

Thats it! When we add up all of the above the Dockerfile will look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
FROM java:8-jdk
MAINTAINER Joery Vreijsen
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64

RUN apt-get update \
  && apt-get -y install maven\
  && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN mkdir /opt/karaf;
RUN mkdir /tmp/src;

WORKDIR /tmp/src/

ADD . .

RUN mvn clean install; \
    tar --strip-components=1 -C /opt/karaf -xzf target/karaf-distro-1.0-SNAPSHOT.tar.gz; \
    rm -rf /tmp/*

RUN chmod 755 /opt; \
    sed -i "21s/out/stdout/" /opt/karaf/etc/org.ops4j.pax.logging.cfg;

VOLUME ["/opt/karaf/deploy"]

EXPOSE 1099 8101 44444

CMD ["/opt/karaf/bin/karaf"]

We can now build our custom distribution with docker build -t . from within our distribution directory.

Note! When building the image, make sure you run mvn clean first so we don’t sent any unnecessary data to our image.

3. Deploy your Apache Karaf container!

We can now run our custom distribution in two different ways, with docker run or docker-compose. For this post i’m going to stick with docker run but if you like a docker-compose example, make sure to check out my git repo karaf-distro-docker

The basic docker run command is docker run but we actually want to give some additional parameters to it. We want our ports to be mapped to our host so we can access them from outside of the container, and we want to specify where docker should mount our /opt/karaf/deploy folder.

This will result in:

1
docker run -v /path/on/host/:/opt/karaf/deploy -p 1099:1099 -p 44444:44444 -p 8101:8101 -d <image_name>

You are good to go! When you now drop your Camel context in the mounted deploy directory on your host machine, your Karaf container will pick it up an run it!

If copying into the mounted deploy directory doesn’t work, try pasting directly into the Docker container using docker exec -it bash. Docker volumes are known to be slow which might make Karaf not picking it up.

Note! To test the camel-route you have to place a file in the /tmp/in folder inside the Docker container.

Thanks for reading and if you have any question leave them in the comments below, and make sure to check out my git repo on http://github.com/vreijsen/karaf-distro-docker


References:

Joery Vreijsen

Software developer using mainly Java & Ruby. Recently fell in love with Docker • Github: vreijsen • Twitter: @JoeryVreijsen