Axon Server SE

This section is split into 3 sub-sections.

Docker Image

Axon provides a ready to use Axon Server SE image. The image is built using a compact image from Google’s “distroless” base images at the gcr.io repository, in this case “gcr.io/distroless/java:11”.

To run the provided image the command below can be executed. This starts Axon Server SE in a docker container with exposes the HTTP (8024) and GRPC (8124) ports to the host.

$ docker run -d --name axonserver  -p 8024:8024 -p 8124:8124 axoniq/axonserver

A quick verification of the running docker container can be done by querying the REST API that is available to retrieve configuration information for a running Axon Server instance.

$ curl -s http://localhost:8024/v1/public/me

This displays the information below:

{
  "authentication": false,
  "clustered": false,
  "ssl": false,
  "adminNode": true,
  "developmentMode": false,
  "storageContextNames": [
    "default"
  ],
  "contextNames": [
    "default"
  ],
  "httpPort": 8024,
  "grpcPort": 8124,
  "internalHostName": null,
  "grpcInternalPort": 0,
  "name": "{axon-server-name}",
  "hostName": "{axon-server-hostname}"
}

The application is installed in the root with a minimal properties file which is depicted below. The “/data” and “/eventdata” directories are created as volumes, and their data will be accessible on the local filesystem somewhere in Docker’s temporary storage tree.

axoniq.axonserver.snapshot.storage=/eventdata
axoniq.axonserver.controldb-path=/data
axoniq.axonserver.pid-file-location=/data
logging.file=/data/axonserver.log
logging.file.max-history=10
logging.file.max-size=10MB

Customization

The directory locations for the volumes can be specified as per your requirements. The image also has a third directory "/config" which is not marked as a volume. This gives you the capability to have an "axonserver.properties" file which can be placed in this location to override the above mentioned settings as well as add new properties similar to a local install.

Assuming that you have a directory "axonserverse**"** which will be the designated location for your volumes and configuration information.

We will first create the sub-directories for the volumes/configuration. We will also add a couple of custom properties (name/hostname) to the axonserver.properties file which will be placed in the config sub-directory. As stated above, you can add additional properties to control the configuration.

$ mkdir -p axonserverse/data axonserverse/events axonserverse/config
$ (
> echo axoniq.axonserver.name=axonserver
> echo axoniq.axonserver.hostname=localhost
> ) > axonserverse/config/axonserver.proprties

To start the container with the customizations done above, the following command can be executed:

docker run -d --rm --name axonserver -p 8024:8024 -p 8124:8124 -v `pwd`/axonserverse/data:/data -v `pwd`/axonserverse/events:/eventdata -v `pwd`/axonserverse/config:/config axoniq/axonserver

Now if you query the API (utilizing the “curl” command depicted above) it will show that it is running with name “axonserver” and hostname “localhost”. Also the data directory will contain the ControlDB file, PID file, and a copy of the log output. The “events” directory will have the event and snapshot data.

This completes a basic setup of the Axon Server SE Docker image with implementation of customizations.

Docker Compose

Running Axon Server SE in docker-compose helps address more complex requirements around distributed scenarios. The following file will help start Axon Server SE with “./data”, “./events”, and “./config” mounted as volumes and the config directory is actually Read-Only.

This again assumes that you have a directory "axonserverse**"** which will be the designated location for your volumes and configuration information.

version: '3.3'
services:
  axonserver:
    image: axoniq/axonserver
    hostname: axonserver
    volumes:
      - axonserver-data:/data
      - axonserver-events:/eventdata
      - axonserver-config:/config:ro
    ports:
      - '8024:8024'
      - '8124:8124'
      - '8224:8224'
    networks:
      - axon-demo

volumes:
  axonserver-data:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ${PWD}/axonserverse/data
  axonserver-events:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ${PWD}/axonserverse/events
  axonserver-config:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ${PWD}/axonserverse/config

networks:
  axon-demo:

Starting the Axon Server SE using the docker-compose command is depicted below.

$ docker-compose up
Creating network "docker-compose_axon-demo" with the default driver
Creating volume "docker-compose_axonserver-data" with local driver
Creating volume "docker-compose_axonserver-events" with local driver
Creating volume "docker-compose_axonserver-config" with local driver
Creating docker-compose_axonserver_1 ... done
Attaching to docker-compose_axonserver_1
axonserver_1  |      _                     ____
axonserver_1  |     / \   __  _____  _ __ / ___|  ___ _ ____   _____ _ __
axonserver_1  |    / _ \  \ \/ / _ \| '_ \\___ \ / _ \ '__\ \ / / _ \ '__|
axonserver_1  |   / ___ \  >  < (_) | | | |___) |  __/ |   \ V /  __/ |
axonserver_1  |  /_/   \_\/_/\_\___/|_| |_|____/ \___|_|    \_/ \___|_|
axonserver_1  |  Standard Edition                        Powered by AxonIQ

Kubernetes

An Axon Server SE instance has a clear and persistent identity, in that it saves identifying information about itself in the controlDB. Also, if it is used as an event store, the context’s events will be stored on disk as well, essentially Axon Server SE is a stateful application.

In the context of Kubernetes that means we want to bind every Axon Server deployment to its own storage volumes, and also to a predictable network identity. Kubernetes provides us with a StatefulSet deployment class which does just that i.e. StatefulSets represent a set of Pods with unique, persistent identities and stable hostnames that is maintained regardless of where they are scheduled.

A sample YAML descriptor is depicted below which defines a StatefulSet for Axon Server and two Services (axon-server-gui / axonserver) to provide access to the HTTP and gRPC ports.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: axonserver
  labels:
    app: axonserver
spec:
  serviceName: axonserver
  replicas: 1
  selector:
    matchLabels:
      app: axonserver
  template:
    metadata:
      labels:
        app: axonserver
    spec:
      containers:
      - name: axonserver
        image: axoniq/axonserver
        imagePullPolicy: Always
        ports:
        - name: grpc
          containerPort: 8124
          protocol: TCP
        - name: gui
          containerPort: 8024
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /data
        - name: events
          mountPath: /eventdata
        readinessProbe:
          httpGet:
            path: /actuator/info
            port: 8024
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 1
          failureThreshold: 30
        livenessProbe:
          httpGet:
            path: /actuator/info
            port: 8024
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 3
  volumeClaimTemplates:
    - metadata:
        name: events
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 5Gi
    - metadata:
        name: data
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
  name: axonserver-gui
  labels:
    app: axonserver
spec:
  ports:
  - name: gui
    port: 8024
    targetPort: 8024
  selector:
    app: axonserver
  type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
  name: axonserver
  labels:
    app: axonserver
spec:
  ports:
  - name: grpc
    port: 8124
    targetPort: 8124
  clusterIP: None
  selector:
    app: axonserver
---

Important to note here is that this is a pretty basic descriptor in the sense that it does not have any settings for the amount of memory and/or cpu to reserve for Axon Server SE which you may want to do for long-running deployments.

To deploy, you would need a Kubernetes cluster and access to the kubectl utility to help control these clusters. For a development Kubernetes cluster, it is recommended to use minikube or Red Hat CodeReady Containers which installs a Red Hat OpenShift Kubernetes cluster on your laptop. For production it is recommended to use a managed service like AWS EKS / Google's GKE or Azure's AKS.

The first step would be to create a separate namespace for Axon Server SE.

$ kubectl create ns ${axonserverse-ns}
namespace/running-axon-server created

The next step would be to deploy to the cluster.

$ kubectl apply -f axonserver.yml -n ${axonserverse-ns}
statefulset.apps/axonserver created
service/axonserver-gui created
service/axonserver created

This completes a basic setup to help install Axon Server SE on Kubernetes.

Last updated