Skip to content

rotty3000/osgi-config-aff

Repository files navigation

OSGi Configuration Admin / Kubernetes Integration Prototype

CI Build

This project is an experiment to produce the smallest and fastest Docker image that contains a complete OSGi runtime focusing on OSGi Configuration Admin with live, volume mounted ConfigMaps and Secrets.

Use ConfigMaps or Secrets files to mount Apache Felix FileInstall compatible config (.cfg|.config) files to the /app/configs directory.

The Apache Felix Interpolation plugin is also configured to use the /app/configs directory and stands ready to replace placeholders found in your configurations with the values found there. The interpolation plugin also supports multiple directories using comma-separated syntax.

Note You can dynamically install additional bundles by placing them into the /app/configs directory if you wish. But remember the JVM has been reduced using jlink. So the dependencies of certain bundles may not be satisfied by parts of the JDK that have been omitted, resulting in them not resolving.

Basic Use

Start the container using a basic invocation:

docker run rotty3000/config-osgi-k8s-demo:latest

Build the Docker image

First you must build the maven project:

mvn clean verify

Then, to build the image, execute:

docker build \
	-t <tag> \
	--build-arg EXECUTABLE_JAR=<path_to_executable_jar> \
	--build-arg MODULE_NAME=<module_name> .

The Dockerfile in this project is intended to be reusable and so there are a number of ARGs defined that control it's execution:

  • EXECUTABLE_JAR - (required) Path to executable jar.
  • MODULE_NAME - (required) Module name of the executable jar.
  • EXTRA_MODULES - (optional) Specify any additional JDK modules to add to the calculated set of modules. For example: jdk.jdwp.agent.

Here's an example execution using some of the arguments:

docker build \
  --build-arg EXECUTABLE_JAR=target/exec.jar \
  --build-arg MODULE_NAME=com.github.rotty3000.osgi.config.aff \
  --pull --rm -f Dockerfile -t config-osgi-k8s-demo .

Run the image (with local gogo shell access)

To run the container with gogo shell access, expose port 11311:

docker run \
  -it -p 11311:11311 \
  --rm --name config-osgi-k8s-demo \
  config-osgi-k8s-demo:latest

You can connect with a telnet client from localhost:

]$ telnet localhost 11311
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
g!

Logging

The container contains the OSGi Log Service (1.4) with Logback & SLF4J API which is configured using a default logback.xml which can be overridden by a ConfigMap at /app/bin/logback.xml.

Here's a more thorough run example using the sample files in the local configs directory and the logback.xml that turns up the configuration infra log levels.

docker run \
  -it -p 11311:11311 \
  --rm --name config-osgi-k8s-demo \
  -v "$(pwd)/logback.xml:/app/bin/logback.xml" \
  -v "$(pwd)/configs:/app/configs" \
  config-osgi-k8s-demo:latest

Edit the config files to observe live updates.

Debugging/Tuning the JVM

The JAVA_OPTS environment variable is ready for passing JVM options for either debugging or tuning purposes.

To enable debugging you have to:

  • pass appropriate JAVA_OPTS environment variable which enable debugging
  • expose the port you defined for debugging (here 8000)

Here's a local invocation using the docker run command:

docker run \
  -it -p 11311:11311 -p 8000:8000 \
  --rm --name config-osgi-k8s-demo \
  -v "$(pwd)/configs:/app/configs" \
  -v "$(pwd)/logback.xml:/app/bin/logback.xml" \
  -e JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8000" \
  config-osgi-k8s-demo

Minikube (Optional)

If you wish to use the image in Minikube, make sure to execute the following in the same terminal before building.

eval $(minikube docker-env)

ConfigMap Example

Consider the following Kubernetes Config Map:

apiVersion: v1
kind: ConfigMap
metadata:
  name: osgi-configmap-demo
data:
  # property-like keys; each key maps to a simple value
  player_initial_lives: "3"

  # FileInstall Config Files
  game.pid.config: |
  player.initial.lives="$[env:player_initial_lives]"
  player.maximum-lives="5"

Now let's consider the following Pod definition:

apiVersion: v1
kind: Pod
metadata:
  name: osgi-configmap-demo-pod
spec:
  containers:
  - image: config-osgi-k8s-demo
    name: osgi-configmap-demo-pod-demo
    imagePullPolicy: Never
    resources:
    limits:
      cpu: "250m"
      memory: "128Mi"
    env:
    # Define an environment variable
    - name: player_initial_lives
      valueFrom:
      configMapKeyRef:
        name: osgi-configmap-demo # The ConfigMap this value comes from.
        key: player_initial_lives # The key to fetch.
    volumeMounts:
    - name: osgi-config         # Mount the volume with this name
    mountPath: "/app/configs" # Mount it this path
    readOnly: true
    - name: logback-config
    mountPath: "/app/bin"
    readOnly: true
  volumes:
  # You set volumes at the Pod level, then mount them into containers inside that Pod
  - name: osgi-config
    configMap:
    # Provide the name of the ConfigMap you want to mount.
    name: osgi-configmap-demo
    # An array of keys from the ConfigMap to create as files
    # These are the OSGi FileInstall configuration files to be mounted
    items:
    - key: "game.pid.config"
      path: "game.pid.config"
  - name: logback-config
    configMap:
    name: logback-configmap-demo
    items:
    - key: "logback.xml"
      path: "logback.xml"

About

A complete prototype integrating OSGi Configuration Admin with Kubernetes

Resources

License

Stars

Watchers

Forks

Packages

No packages published