Deploying AxonOps on Kubernetes
This guide explains how to deploy AxonOps components and a Strimzi Kafka cluster on Kubernetes with local (hostPath) storage or shared storage and AxonOps monitoring agent integration.
Quick Start Alternative
For a streamlined deployment experience, you can use our automated installation script that handles the complete setup process. This script automates many of the manual steps described in this guide.
Components Required
Section titled “Components Required”- AxonOps Server - Core management server
- AxonOps Dashboard - Web UI
- AxonOps Timeseries DB - Metrics storage (Cassandra-based)
- AxonOps Search DB - Log and event storage (OpenSearch-based)
- cert-manager (Recommended) - Automatic TLS certificate management
AxonOps Infrastructure Set up
Section titled “AxonOps Infrastructure Set up”Cert Manager
Section titled “Cert Manager”Install cert-manager for automatic TLS certificate management:
helm upgrade --install \ cert-manager oci://quay.io/jetstack/charts/cert-manager \ --version v1.19.1 \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=trueCreate a self-signed cluster issuer if you do not have another issuer to use:
kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: selfsigned-cluster-issuerspec: selfSigned: {}EOFAxonOps Timeseries DB
Section titled “AxonOps Timeseries DB”Create the data directories on the host (user ID 999):
mkdir -p /data/axon-timeserieschown -R 999:999 /data/axon-timeseriesCreate timeseries-values.yaml:
# Optional: Increase heap size for better performanceheapSize: 8192M
# TLS/SSL Configurationtls: # Enable TLS/SSL for Cassandra enabled: true # cert-manager configuration for automatic certificate management certManager: # Enable cert-manager for certificate provisioning enabled: true issuer: name: selfsigned-cluster-issuer
persistence: enabled: false # Disable PVC-based persistence
# Define hostPath volumes using extraVolumesextraVolumes: - name: timeseries-data hostPath: path: /data/axon-timeseries # Host directory path type: DirectoryOrCreate # Creates directory if it doesn't exist
# Mount the hostPath volumes (do not change the paths)extraVolumeMounts: - name: timeseries-data mountPath: /var/lib/cassandra
# Optional: Set resource limitsresources: requests: cpu: 1001m memory: 2Gi limits: cpu: 2000m memory: 4GiInstall:
helm install axondb-timeseries oci://ghcr.io/axonops/charts/axondb-timeseries \ --namespace axonops \ --create-namespace \ -f timeseries-values.yamlAxonOps Search DB
Section titled “AxonOps Search DB”Create the data directory on the host:
mkdir -p /data/axon-searchchown -R 999:999 /data/axon-searchCreate search-values.yaml:
# Large set up may need higher Heap SizeopensearchHeapSize: "8g"
persistence: enabled: false
# Define hostPath volumes using extraVolumesextraVolumes: - name: data hostPath: path: /data/axon-search type: DirectoryOrCreate # Creates directory if it doesn't exist
# Mount the hostPath volumes (do not change the paths)extraVolumeMounts: - name: data mountPath: /var/lib/opensearch
# TLS/SSL Configurationtls: # Enable TLS/SSL enabled: true # cert-manager configuration for automatic certificate management certManager: # Enable cert-manager for certificate provisioning enabled: true issuer: name: selfsigned-cluster-issuerInstall:
helm install axondb-search oci://ghcr.io/axonops/charts/axondb-search \ --namespace axonops \ --create-namespace \ -f search-values.yamlAxonOps Server
Section titled “AxonOps Server”Create axonops-server-secret.yaml:
apiVersion: v1kind: Secretmetadata: name: axon-server-config namespace: axonopstype: OpaquestringData: axon-server.yml: | agents_port: 1888 api_port: 8080 host: 0.0.0.0
search_db: hosts: - https://axondb-search-cluster-master.axonops.svc.cluster.local:9200 username: admin password: change-me skip_verify: true
org_name: example
axon_dash_url: https://axonops.example.com
# Log to stdout for Kubernetes log_file: /dev/stdout tls: mode: disabled auth: enabled: false cql_autocreate_tables: true cql_hosts: - axondb-timeseries-headless.axonops.svc.cluster.local cql_skip_verify: true cql_ssl: true cql_username: cassandra cql_password: change-meNote
Replace the example passwords with real credentials before applying the secret.
The example above sets skip_verify: true, which disables certificate verification
against the search database. To verify the certificate issued by the cluster issuer
instead, mount the CA into the axon-server pod and reference it with ca_file:
search_db: hosts: - https://axondb-search-cluster-master.axonops.svc.cluster.local:9200 username: admin password: change-me ca_file: /etc/axonops/certs/ca.crtWhere the search database requires client certificate authentication, set cert_file
and key_file to the mounted client certificate and private key. All three options
accept a path inside the container, and are also settable as the environment variables
SEARCH_DB_CA_FILE, SEARCH_DB_CERT_FILE, and SEARCH_DB_KEY_FILE. They apply only
when search_db.hosts uses the https:// scheme, and are available from axon-server
2.0.28 onwards.
Note
skip_verify: true overrides ca_file. The two options must not be combined.
Note
The default installation assumes your clients will be all in Kubernetes. If you will have AxonOps clients outside of Kubernetes (external Cassandra or Kafka clusters) you will also need to add an ingress or NodePort configuration.
If you need external access, create axon-server-values.yaml using the following example:
# If you do not have an ingress (Preferred) you can use nodePort by setting# it up hereagentService: type: ClusterIP listenPort: 1888 annotations: {} labels: {} externalIPs: [] # Optional, only used for "type: ClusterIP" clusterIP: "" # Optional, only used for "type: LoadBalancer" loadBalancerIP: "" loadBalancerSourceRanges: [] # Optional, only used for "type: NodePort" nodePort: 0
# For ingress, you can chose between nginx, traefik, etc or the new# HTTPRoute Gateway API: https://gateway-api.sigs.k8s.io/api-types/httproute/apiIngress: enabled: false className: traefik annotations: {} # cert-manager.io/cluster-issuer: "letsencrypt" hosts: - host: api.example.com paths: - path: / pathType: ImplementationSpecific tls: [] # - secretName: api-tls # hosts: # - api.example.com
# HTTPRoute configuration for Gateway API (alternative to Ingress)httpRoute: enabled: false annotations: {} parentRefs: - name: gateway sectionName: http # namespace: default hostnames: - api.example.com rules: - matches: - path: type: PathPrefix value: /Install:
kubectl apply -n axonops -f axonops-server-secret.yaml
helm install axon-server oci://ghcr.io/axonops/charts/axon-server \ --namespace axonops \ --create-namespace \ -f axon-server-values.yaml \ ## IF used --set configurationSecret=axon-server-configAxonOps Dashboard
Section titled “AxonOps Dashboard”Create axonops-dash-values.yaml:
# Configuration for axon-dash applicationconfig: # Axon server URL endpoint axonServerUrl: "http://axon-server-api.axonops.svc.cluster.local:8080"
service: type: ClusterIP port: 3000
# Alternative: NodePort configuration# service:# type: NodePort# ports:# - port: 3000# targetPort: 3000# nodePort: 32000 # The ACTUAL "Host Port" (must be 30000-32767)
ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" # cert-manager.io/cluster-issuer: selfsigned-cluster-issuer hosts: - host: axonops.mycompany.com paths: - path: / pathType: ImplementationSpecific tls: [] # - secretName: axon-dash-tls # hosts: # - axonops.mycompany.comNote
You will need to choose between Ingress (preferred option) or NodePort for external access.
Install:
helm install axon-dash oci://ghcr.io/axonops/charts/axon-dash \ --namespace axonops \ --create-namespace \ -f axonops-dash-values.yamlValidation
Section titled “Validation”Verify all pods are running:
kubectl get pods -n axonopsExpected output:
NAME READY STATUS RESTARTS AGEaxon-dash-847d57885-bmnnl 1/1 Running 0 62saxon-server-0 1/1 Running 0 63saxondb-search-cluster-master-0 1/1 Running 0 3m9saxondb-timeseries-0 1/1 Running 0 4m43sVerify all services are created:
kubectl get svc -n axonopsExpected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEaxon-dash-svc ClusterIP 10.43.72.64 <none> 3000/TCP 89saxon-server-agent ClusterIP 10.43.8.100 <none> 1888/TCP 90saxon-server-api ClusterIP 10.43.237.168 <none> 8080/TCP 90saxondb-search-cluster-master ClusterIP 10.43.120.95 <none> 9200/TCP,9300/TCP,9600/TCP 3m36saxondb-search-cluster-master-headless ClusterIP None <none> 9200/TCP,9300/TCP,9600/TCP 3m36saxondb-timeseries ClusterIP 10.43.16.42 <none> 9042/TCP,7199/TCP 5m10saxondb-timeseries-headless ClusterIP None <none> 9042/TCP 5m10s