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.06MBStep 1/10 : FROM java:8-jre-alpine8-jre-alpine: Pulling from library/java709515475419: Pull complete 38a1c0aaa6fd: Pull complete cd134db5e982: Pull complete Digest: sha256:6a8cbe4335d1a5711a52912b684e30d6dbfab681a6733440ff7241b05a5deefdStatus: Downloaded newer image for java:8-jre-alpine ---> fdc893b19a14Step 2/10 : ENV KARAF_INSTALL_PATH=/opt ---> Running in dadbe0fbb5d7Removing intermediate container dadbe0fbb5d7 ---> 4d14737dd37dStep 3/10 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf ---> Running in b146df1354a1Removing intermediate container b146df1354a1 ---> 27f504e74175Step 4/10 : ENV PATH $PATH:$KARAF_HOME/bin ---> Running in f7e262e47d31Removing intermediate container f7e262e47d31 ---> aeb7757dee5cStep 5/10 : ARG karaf_dist=NOT_SET ---> Running in d3d0ac0b66d8Removing intermediate container d3d0ac0b66d8 ---> 1f9456e1edfdStep 6/10 : ADD $karaf_dist $KARAF_INSTALL_PATH ---> 828362c5401aStep 7/10 : RUN set -x &amp;&amp;   ln -s $KARAF_INSTALL_PATH/apache-karaf* $KARAF_HOME ---> Running in f59a3e5139cdln -s /opt/apache-karaf-4.2.1 /opt/apache-karafRemoving intermediate container f59a3e5139cd---> b05d4af0df6bStep 8/10 : COPY docker-entrypoint.sh /---> 003ed84df94fStep 9/10 : EXPOSE 8101 1099 44444 8181---> Running in 35bceaa38fe9Removing intermediate container 35bceaa38fe9---> 439a85a3d6f0Step 10/10 : ENTRYPOINT ["/docker-entrypoint.sh"]---> Running in fa51274dbbf3Removing intermediate container fa51274dbbf3---> e595c47809e5Successfully built e595c47809e5Successfully 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 imagesREPOSITORY          TAG                 IMAGE ID            CREATED             SIZEkaraf              latest              e595c47809e5        4 minutes ago       133MBjava                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 karafStarting Apache Karafkaraf: Ignoring predefined value for KARAF_HOMEApache Karaf (4.2.1)Hit '' for a list of available commandsand '[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 -aCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                 NAMES477b3cec3b76        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 karafStarting Apache Karafkaraf: 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 upCreating docker_karaf_1 … Creating docker_karaf_1 … doneAttaching to docker_karaf_1karaf_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-karafUsing karaf dist: /home/jbonofre/my-karaf-1.0.0.tar.gzSending build context to Docker daemon  22.11MBStep 1/10 : FROM java:8-jre-alpine ---> fdc893b19a14Step 2/10 : ENV KARAF_INSTALL_PATH=/opt ---> Running in 620e32d745a9Removing intermediate container 620e32d745a9 ---> f773a2c25001Step 3/10 : ENV KARAF_HOME $KARAF_INSTALL_PATH/apache-karaf ---> Running in 2445bbea610dRemoving intermediate container 2445bbea610d ---> 1974f31ada89Step 4/10 : ENV PATH $PATH:$KARAF_HOME/bin ---> Running in 20b5bc409c29Removing intermediate container 20b5bc409c29 ---> 3e73907f5b14Step 5/10 : ARG karaf_dist=NOT_SET ---> Running in e7603a1c7117Removing intermediate container e7603a1c7117 ---> 5de9504336ccStep 6/10 : ADD $karaf_dist $KARAF_INSTALL_PATH ---> 1eba7c16ffd0Step 7/10 : RUN set -x &&   ln -s $KARAF_INSTALL_PATH/apache-karaf $KARAF_HOME ---> Running in d3c685bd2bcdln -s /opt/apache-karaf-4.2.2-SNAPSHOT /opt/apache-karafRemoving intermediate container d3c685bd2bcd---> 1e9306813e59Step 8/10 : COPY docker-entrypoint.sh /---> e28f024ad48eStep 9/10 : EXPOSE 8101 1099 44444 8181---> Running in 960627ec7597Removing intermediate container 960627ec7597---> 9a3d6017498dStep 10/10 : ENTRYPOINT ["/docker-entrypoint.sh"]---> Running in 9d7fc90c19c8Removing intermediate container 9d7fc90c19c8---> 98ee7d5ef992Successfully built 98ee7d5ef992Successfully tagged my-karaf:latest

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

$> docker imagesREPOSITORY          TAG                 IMAGE ID            CREATED              SIZEmy-karaf            latest              98ee7d5ef992        About a minute ago   133MBjava                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.tomlsept. 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-cesept. 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: 0Debug: falseDriver: overlay2ExecutionDriver: nullIPv4Forwarding: trueImages: 1IndexServerAddress: https://index.docker.io/v1/InitPath: nullInitSha1: nullKernelVersion: 4.15.0-33-genericMemoryLimit: trueNEventsListener: falseNFd: 20NGoroutines: 33SwapLimit: 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:versionVersion: 17.12.1-ceOs: linuxKernel version: 4.15.0-33-genericGo version: go1.10.1Git commit: 7390fc6Arch: amd64API version: 1.35Build time: 2018-02-28T17:46:05.000000000+00:00Experimental: 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 elasticsearchName                                │ 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:imagesId                                                                      │ 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 -aId                                                               │ 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 -aId                                                               │ 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 psCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                                              NAMESd3f3feb99f7f        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 !

Comments

Popular posts from this blog

Quarkus and "meta" extension

Getting started with Apache Karaf Minho

Apache Karaf Minho and OpenTelemetry