Apache Karaf & Docker

Apache Karaf 4.2.1 has been released two weeks ago. It’s a major upgrade on the Karaf 4.2.x series, bringing lot of fixes, improvements and new features. Especially:

  • Better support of Java 9, 10 & 11
  • Sets of examples directly available in the Karaf distribution, allowing you to easily starts with Karaf
  • Better KarafTestSupport allowing you to easily creates your own integration tests.

One of new interesting feature added in Apache Karaf 4.2.1 is the support of Docker.

Docker is a great system container platform. Mixing Docker (system container) and Apache Karaf (application container) together gives a great flexibility and very powerful approach for your applications and ecosystem. You decide of the provisioning approach you want to adopt:

  • a static approach using Karaf static profile directly running in Docker
  • a dynamic approach with a regular Karaf distribution running in Docker

Apache Karaf 4.2.1 brings two tools around Docker:

  1. Build tooling (scripts) allows you to easily create Docker Karaf image and use it to create Docker containers.
  2. A dockerfeature that you can install in a running Apache Karaf instance, allowing you to manipulate Docker directly from Karaf.

Creating Docker Karaf images

Since Apache Karaf 4.2.1, the Apache Karaf assembly provides new Docker scripts: https://github.com/apache/karaf/tree/master/assemblies/docker.

In order to use it, you need:

  • docker installed
  • docker-compose installed

Now, Apache Karaf provides a build.sh script to easily create your Karaf image. The generated image is based on the official Java Alpine (OpenJDK 8) image.

The build.sh script is pretty easy to use.

Alternatively, you can also use the “classic” docker build command. Apache Karaf assembly/docker folder provides the Dockerfile used by docker build.

Let’s take a look on build.sh. This script is convenient as:

  • you can create a Docker image based on a local Karaf distribution you create or an official Karaf distribution available on Apache mirror.
  • you can provide an image name.
  • you can provide a Karaf version (when you create the Docker image using an official Karaf distribution).

Docker image using Karaf vanilla distribution

The first classic usage of build.sh is to create a Docker image using an official Karaf distribution (“vanilla”) available on Apache mirrors.

For instance, we want to create a Docker image using Apache Karaf 4.2.1. We can simply do:

$> assembly/docker/build.sh --from-release --karaf-version 4.2.1 --image-name karaf<br/>Downloading apache-karaf-4.2.1.tar.gz from http://mirrors.standaloneinstaller.com/apache/karaf/4.2.1/
Sending build context to Docker daemon  22.06MB
Step 1/10 : FROM java:8-jre-alpine
8-jre-alpine: Pulling from library/java
709515475419: Pull complete 
38a1c0aaa6fd: Pull complete 
cd134db5e982: Pull complete 
Digest: sha256:6a8cbe4335d1a5711a52912b684e30d6dbfab681a6733440ff7241b05a5deefd
Status: Downloaded newer image for java:8-jre-alpine
 ---> fdc893b19a14
Step 2/10 : ENV KARAF_INSTALL_PATH=/opt
 ---> Running in dadbe0fbb5d7
Removing intermediate container dadbe0fbb5d7
 ---> 4d14737dd37d
Step 3/10 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf
 ---> Running in b146df1354a1
Removing intermediate container b146df1354a1
 ---> 27f504e74175
Step 4/10 : ENV PATH $PATH:$KARAF_HOME/bin
 ---> Running in f7e262e47d31
Removing intermediate container f7e262e47d31
 ---> aeb7757dee5c
Step 5/10 : ARG karaf_dist=NOT_SET
 ---> Running in d3d0ac0b66d8
Removing intermediate container d3d0ac0b66d8
 ---> 1f9456e1edfd
Step 6/10 : ADD $karaf_dist $KARAF_INSTALL_PATH
 ---> 828362c5401a
Step 7/10 : RUN set -x &amp;&amp;   ln -s $KARAF_INSTALL_PATH/apache-karaf* $KARAF_HOME
 ---> Running in f59a3e5139cd
ln -s /opt/apache-karaf-4.2.1 /opt/apache-karaf
Removing intermediate container f59a3e5139cd
---> b05d4af0df6b
Step 8/10 : COPY docker-entrypoint.sh /
---> 003ed84df94f
Step 9/10 : EXPOSE 8101 1099 44444 8181
---> Running in 35bceaa38fe9
Removing intermediate container 35bceaa38fe9
---> 439a85a3d6f0
Step 10/10 : ENTRYPOINT ["/docker-entrypoint.sh"]
---> Running in fa51274dbbf3
Removing intermediate container fa51274dbbf3
---> e595c47809e5
Successfully built e595c47809e5
Successfully tagged karaf:latest

We can see that build.sh automatically downloaded the Apache Karaf 4.2.1 distribution from a Apache mirror.  It also retrieves the Alpine Java base Docker image.

Now, we can see the image in our Docker:

$> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
karaf              latest              e595c47809e5        4 minutes ago       133MB
java                8-jre-alpine        fdc893b19a14        18 months ago       108MB

We can run a Docker container using our karaf image.

First way to do that is to use the “classic” docker run command:

$> docker run -p 1099:1099 -p 8101:8101 -t --name mycontainer karaf
Starting Apache Karaf
karaf: Ignoring predefined value for KARAF_HOME
Apache Karaf (4.2.1)
Hit '' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '' or type 'system:shutdown' or 'logout' to shutdown Karaf.
karaf@root()>

We can see the container running:

$> docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                 NAMES
477b3cec3b76        karaf             "/docker-entrypoint.…"   3 minutes ago       Up 3 minutes        0.0.0.0:1099->1099/tcp, 8181/tcp, 0.0.0.0:8101->8101/tcp, 44444/tcp   mycontainer

When you use docker run you have to remember to expose the network ports using the -p option. When you expose 8101 you can connect to the Karaf running inside the Docker container using ssh or Karaf bin/client.

An alternative to docker run (or docker start) is to use docker-compose. Apache Karaf now provides docker-compose.yml to simplify the way to starting and stopping Apache Karaf in docker.

To start Apache Karaf Docker image, you can do:

$> docker-compose run karaf
Starting Apache Karaf
karaf: Ignoring predefined value for KARAF_HOME...

The behavior is exactly the same as using docker run however, the ports binding is configured in Karaf docker-compose.yml.

You can also start in daemon mode (without the shell console) using:

$> docker-compose up
Creating docker_karaf_1 … 
Creating docker_karaf_1 … done
Attaching to docker_karaf_1
karaf_1  | Starting Apache Karaf

You can stop an existing container using

$> docker-compose kill

Docker image with custom Karaf distribution

You have created your own Karaf custom distribution based and you want to create a Docker image with it ? Very simple with build.sh script ! You just have to provide the archive of your custom distribution.

$> ./build.sh --from-local-dist --archive /home/jbonofre/my-karaf.tar.gz --image-name my-karaf
Using karaf dist: /home/jbonofre/my-karaf-1.0.0.tar.gz
Sending build context to Docker daemon  22.11MB
Step 1/10 : FROM java:8-jre-alpine
 ---> fdc893b19a14
Step 2/10 : ENV KARAF_INSTALL_PATH=/opt
 ---> Running in 620e32d745a9
Removing intermediate container 620e32d745a9
 ---> f773a2c25001
Step 3/10 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf
 ---> Running in 2445bbea610d
Removing intermediate container 2445bbea610d
 ---> 1974f31ada89
Step 4/10 : ENV PATH $PATH:$KARAF_HOME/bin
 ---> Running in 20b5bc409c29
Removing intermediate container 20b5bc409c29
 ---> 3e73907f5b14
Step 5/10 : ARG karaf_dist=NOT_SET
 ---> Running in e7603a1c7117
Removing intermediate container e7603a1c7117
 ---> 5de9504336cc
Step 6/10 : ADD $karaf_dist $KARAF_INSTALL_PATH
 ---> 1eba7c16ffd0
Step 7/10 : RUN set -x &&   ln -s $KARAF_INSTALL_PATH/apache-karaf $KARAF_HOME
 ---> Running in d3c685bd2bcd
ln -s /opt/apache-karaf-4.2.2-SNAPSHOT /opt/apache-karaf
Removing intermediate container d3c685bd2bcd
---> 1e9306813e59
Step 8/10 : COPY docker-entrypoint.sh /
---> e28f024ad48e
Step 9/10 : EXPOSE 8101 1099 44444 8181
---> Running in 960627ec7597
Removing intermediate container 960627ec7597
---> 9a3d6017498d
Step 10/10 : ENTRYPOINT ["/docker-entrypoint.sh"]
---> Running in 9d7fc90c19c8
Removing intermediate container 9d7fc90c19c8
---> 98ee7d5ef992
Successfully built 98ee7d5ef992
Successfully tagged my-karaf:latest

We now have a Karaf Docker image with our custom distribution:

$> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
my-karaf            latest              98ee7d5ef992        About a minute ago   133MB
java                8-jre-alpine        fdc893b19a14        18 months ago        108MB

It’s exactly the same as before to start a Docker container using docker run or docker-compose.

Official Apache Karaf Docker images

I’m preparing a pull request to DockerLib to add official Apache Karaf Docker images based on these scripts.

Karaf docker feature

assembly/docker/build.sh is not the only new stuff in Apache Karaf 4.2.1.

A new docker feature has been added too, allowing you to interact with Docker directly from a running Apache Karaf instance.

Prerequisites

The Apache Karaf docker feature interacts with Docker using tcp. You have to enable it by passing an option to the Docker daemon:

/usr/bin/dockerd -H fd:// -H tcp://localhost:2375

For instance, on a Ubuntu system, you can configure the Docker daemon in /lib/systemd/system/docker.service file, changing the ExecStart property:

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://localhost:2375

You have to restart the Docker daemon after you changed the docker.service file:

>$ service docker restart

You can check the status of the Docker:

$> service docker status
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2018-09-11 11:30:08 CEST; 3min 7s ago
     Docs: https://docs.docker.com
 Main PID: 31978 (dockerd)
    Tasks: 38
   CGroup: /system.slice/docker.service
           ├─31978 /usr/bin/dockerd -H fd:// -H tcp://localhost:2375
           └─32004 docker-containerd --config /var/run/docker/containerd/containerd.toml
sept. 11 11:30:07 precision dockerd[31978]: time="2018-09-11T11:30:07.569933812+02:00" level=warning msg="Your kernel does not support cgroup rt period"
sept. 11 11:30:07 precision dockerd[31978]: time="2018-09-11T11:30:07.569949221+02:00" level=warning msg="Your kernel does not support cgroup rt runtime"
sept. 11 11:30:07 precision dockerd[31978]: time="2018-09-11T11:30:07.570903883+02:00" level=info msg="Loading containers: start."
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.258633531+02:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.289844901+02:00" level=info msg="Loading containers: done."
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.360892312+02:00" level=info msg="Docker daemon" commit=7390fc6 graphdriver(s)=overlay2 version=17.12.1-ce
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.361071172+02:00" level=info msg="Daemon has completed initialization"
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.365804009+02:00" level=info msg="API listen on 127.0.0.1:2375"
sept. 11 11:30:08 precision dockerd[31978]: time="2018-09-11T11:30:08.365826737+02:00" level=info msg="API listen on /var/run/docker.sock"
sept. 11 11:30:08 precision systemd[1]: Started Docker Application Container Engine.

This Docker service can be used locally or remotely by the Karaf Docker feature.

Installing Karaf Docker feature

It’s really easy to install the docker feature on a running Apache Karaf instance:

karaf@root()> feature:install docker

Once docker feature is installed, you have new docker:* shell commands available in Karaf.

You can use docker:info to get some details about the Docker service:

karaf@root()> docker:info 
Containers: 0
Debug: false
Driver: overlay2
ExecutionDriver: null
IPv4Forwarding: true
Images: 1
IndexServerAddress: https://index.docker.io/v1/
InitPath: null
InitSha1: null
KernelVersion: 4.15.0-33-generic
MemoryLimit: true
NEventsListener: false
NFd: 20
NGoroutines: 33
SwapLimit: false

By default, Karaf Docker uses http://localhost:2375 URL to interfact with Docker. If you want to connect to another Docker service (remote for instance), you can use the --url option:

karaf@root()> docker:info --url http://localhost:2375

docker:version command provides some details about the Docker service you are using:

karaf@root()> docker:version
Version: 17.12.1-ce
Os: linux
Kernel version: 4.15.0-33-generic
Go version: go1.10.1
Git commit: 7390fc6
Arch: amd64
API version: 1.35
Build time: 2018-02-28T17:46:05.000000000+00:00
Experimental: null

Any Docker action can be performed using the Karaf shell command, or directly in the DockerMBean provided by Karaf.

Use case: elasticsearch docker with http proxy

The Apache Karaf documentation describes all docker:* commands: http://karaf.apache.org/manual/latest/#_docker.

In this blog, I would like to show a first use case of Karaf Docker feature: manipulating Docker container directly within Karaf and expose in Karaf.

To illustrate this use case, I gonna use a Docker image providing elasticsearch.

First, let’s search for image directly in Karaf using the docker:search shell commands:

karaf@root()> docker:search elasticsearch
Name                                │ Description                                                                                          │ Automated │ Official │ Star Count
────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────┼───────────
elasticsearch                       │ Elasticsearch is a powerful open source search and analytics engine that makes data easy to explore. │ false     │ true     │ 3027
...

Let’s pull the elasticsearch image using the docker:pull command:

karaf@root()> docker:pull -v elasticsearch

We can see the images available in the Docker service using docker:images command:

karaf@root()> docker:images
Id                                                                      │ RepoTags               │ Created    │ Labels │ Size      │ Virtual Size
────────────────────────────────────────────────────────────────────────┼────────────────────────┼────────────┼────────┼───────────┼─────────────
sha256:362c5cb1669b144eef89d39a6e9153c546c14923e10fde17701920615c0e2add │ [elasticsearch:latest] │ 1536162308 │        │ 485920188 │ 485920188

Now, we can create a container using the elasticsearch image and control it directly from Karaf:

karaf@root()> docker:create --cmd '/docker-entrypoint.sh' --tty --image elasticsearch --exposedPort 9200/tcp --hostPortBinding 9200 elasticsearch

We can see our elasticsearch container available with docker:ps command:

karaf@root()> docker:ps -a
Id                                                               │ Names           │ Command                                     │ Created    │ Image         │ Image ID                                                                │ Status  │ State   │ Ports │ Size │ Size Root
─────────────────────────────────────────────────────────────────┼─────────────────┼─────────────────────────────────────────────┼────────────┼───────────────┼─────────────────────────────────────────────────────────────────────────┼─────────┼─────────┼───────┼──────┼──────────
6db4369b07eb3894034c77ea3e28b08e80ec723a711ee90ff6f1a3917256a57b │ [elasticsearch] │ /docker-entrypoint.sh /docker-entrypoint.sh │ 1536674098 │ elasticsearch │ sha256:362c5cb1669b144eef89d39a6e9153c546c14923e10fde17701920615c0e2add │ Created │ created │       │ 0    │ 0

We start our elasticsearch container:

karaf@root()> docker:start elasticsearch

The elasticsearch instance is running and binding port 9200. You can check that it works fine by accessing to http://localhost:9200/ in your browser.

Now, we want to “hide” direct access to elasticsearch, and access to elasticsearch docker container “via” Karaf. To do that, let’s install the http feature:

karaf@root()> feature:install http

The http feature brings the support of HTTP proxies. So, we can simply add a proxy from Karaf HTTP service to Elasticsearch Docker container:

karaf@root()> http:proxy-add /elasticsearch http://localhost:9200

We can see our HTTP proxy available in Karaf:

karaf@root()> http:proxies 
URL            │ ProxyTo
───────────────┼──────────────────────
/elasticsearch │ http://localhost:9200

Now, we can access elasticsearch “via” Karaf using http://localhost:8181/elasticsearch.

Basically, the Karaf docker feature is a way of interacting with the Docker service as you can do directly with the docker commands.

However, it also provides an unique feature: the provisioning.

Karaf Docker provisioning

You have a running Karaf instance where you install bunch of features, bundles, change configuration, …

You want to create a Docker container using this instance ? Simpy use the docker:provision command. It will create a Docker container with your instance as a container volume, starting it automatically.

Let’s create the container:

karaf@root()> docker:provision mykaraf

We can see a new container mykaraf has been created:

karaf@root()> docker:ps -a
Id                                                               │ Names     │ Command                     │ Created    │ Image             │ Image ID                                                                │ Status  │ State   │ Ports │ Size │ Size Root
─────────────────────────────────────────────────────────────────┼───────────┼─────────────────────────────┼────────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────┼─────────┼─────────┼───────┼──────┼──────────
d3f3feb99f7f63735fed24946b2d4fe76d83742db0cada4c743ba3535cb73ed4 │ [mykaraf] │ /opt/apache-karaf/bin/karaf │ 1536681767 │ java:8-jre-alpine │ sha256:fdc893b19a147681ee764b2edab6c494d60fe99d83b14b8794bbcbc040ec7aa7 │ Created │ created │       │ 0    │ 0

Now, we can start the container, even outside of the Karaf instance:

$> docker start mykaraf

We can see our mykaraf container started:

$> docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                                              NAMES
d3f3feb99f7f        java:8-jre-alpine   "/opt/apache-karaf/b…"   3 minutes ago       Up 1 second         0.0.0.0:1099->1099/tcp, 0.0.0.0:8101->8101/tcp, 0.0.0.0:8181->8181/tcp, 0.0.0.0:44444->44444/tcp   mykaraf

As Karaf Docker automatically created the port binding, we can connect to our Karaf instance running in Docker container using ssh:

$> ssh -p 8101 karaf@localhost
...
karaf@root()>

Conclusion

I hope you will enjoy the new Docker feature available in Apache Karaf 4.2.1. It opens new possibility for devops to build a complete and very flexible ecosystem powered by Apache Karaf.

As usual, feel free to share bugs you might find, improvements, new features or just ideas !

Thanks !

You May Also Like

About the Author: jbonofre

ASF Member, PMC for Apache Karaf, PMC for Apache ServiceMix, PMC for Apache Archiva, PMC for Apache Felix, PMC for Apache Camel, PMC for Apache Syncope, PMC for Apache Beam, PMC for Apache CarbonData, PMC for Apache Bahir, PMC for Apache Brooklyn, PMC for Apache Falcon, PMC for Apache Guacamole, PMC for Apache Lens, Committer for Apache ActiveMQ and much more ! Twitter: jbonofre IRC: jbonofre on #servicemix,#karaf,#camel,#cxf on Freenode