Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

K8ssandra Cassandra Cluster with axon-agent

This section explains how to deploy a K8ssandra-managed Cassandra cluster on Kubernetes with integrated AxonOps monitoring agent.

  • K8ssandra Operator - Kubernetes operator for managing Cassandra clusters
  • cert-manager (Recommended) - Automatic TLS certificate management

This deployment creates a production-ready Cassandra cluster with:

  • Cassandra 5.0.6+ with axon-agent pre-integrated
  • Multi-datacenter support
  • Configurable resource allocation
  • Persistent storage with custom storage classes
  • axon-agent for comprehensive monitoring and management

K8ssandra with AxonOps

  1. Kubernetes cluster (v1.23+)
  2. AxonOps Server deployed and accessible
  3. kubectl configured to access your cluster
  4. Helm 3 installed
  5. AxonOps API key and organization name for agent authentication

Install cert-manager for automatic TLS certificate management:

Terminal window
helm upgrade --install \
cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.19.1 \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true

Add the K8ssandra Helm repository and install the operator:

Terminal window
helm repo add k8ssandra https://helm.k8ssandra.io/stable
helm repo update
helm upgrade --install k8ssandra-operator k8ssandra/k8ssandra-operator \
-n k8ssandra-operator \
--create-namespace \
--set image.tag=v1.29.0

Verify the operator is running:

Terminal window
kubectl get pods -n k8ssandra-operator

Expected output:

NAME READY STATUS RESTARTS AGE
k8ssandra-operator-cass-operator-xxx-xxx 1/1 Running 0 30s
k8ssandra-operator-controller-manager-xxx-xxx 1/1 Running 0 30s

Before deploying, configure these variables according to your environment:

VariableMeaning / What To SetExample Value
CLUSTER_NAMEName of your Cassandra clusteraxonops-k8ssandra-5
NAMESPACEKubernetes namespace for the clusterk8ssandra-operator
CASSANDRA_VERSIONCassandra version to deploy5.0.6
DATACENTER_NAMEName for the datacenterdc1
DATACENTER_SIZENumber of Cassandra nodes3
STORAGE_CLASSStorage class for persistent volumescluster default
STORAGE_SIZESize of storage per node2Gi
HEAP_SIZEJVM heap size (initial and max)1G
CPU_LIMITCPU limit per pod1 or 2000m
MEMORY_LIMITMemory limit per pod2Gi
AXON_AGENT_KEYYour AxonOps API key for authenticationUse a Kubernetes Secret
AXON_AGENT_ORGYour AxonOps organization identifieryour-org-name
AXON_AGENT_SERVER_HOSTDNS name/address of AxonOps serveraxon-server-agent.axonops.svc.cluster.local
AXON_AGENT_SERVER_PORTPort for axon-agent connections1888
AXON_AGENT_TLS_MODEWhether agent uses TLS (true or false)false

Create a file named k8ssandra-cluster.yaml:

apiVersion: k8ssandra.io/v1alpha1
kind: K8ssandraCluster
metadata:
name: ${CLUSTER_NAME}
namespace: ${NAMESPACE}
spec:
cassandra:
serverVersion: "${CASSANDRA_VERSION}"
serverImage: ghcr.io/axonops/k8ssandra/cassandra:${CASSANDRA_VERSION}-v0.1.111-1.2.0
softPodAntiAffinity: true
resources:
limits:
cpu: ${CPU_LIMIT}
memory: ${MEMORY_LIMIT}
requests:
cpu: 1
memory: 1Gi
datacenters:
- metadata:
name: ${DATACENTER_NAME}
size: ${DATACENTER_SIZE}
containers:
- name: cassandra
env:
- name: AXON_AGENT_KEY
value: "${AXON_AGENT_KEY}"
- name: AXON_AGENT_ORG
value: "${AXON_AGENT_ORG}"
- name: AXON_AGENT_SERVER_HOST
value: "${AXON_AGENT_SERVER_HOST}"
- name: AXON_AGENT_SERVER_PORT
value: "${AXON_AGENT_SERVER_PORT}"
- name: AXON_AGENT_TLS_MODE
value: "${AXON_AGENT_TLS_MODE}"
config:
jvmOptions:
heap_initial_size: ${HEAP_SIZE}
heap_max_size: ${HEAP_SIZE}
storageConfig:
cassandraDataVolumeClaimSpec:
storageClassName: ${STORAGE_CLASS}
accessModes:
- ReadWriteOnce
resources:
requests:
storage: ${STORAGE_SIZE}

Here's a complete example for a 3-node cluster:

apiVersion: k8ssandra.io/v1alpha1
kind: K8ssandraCluster
metadata:
name: axonops-k8ssandra-5
namespace: k8ssandra-operator
spec:
cassandra:
serverVersion: "5.0.6"
serverImage: ghcr.io/axonops/k8ssandra/cassandra:5.0.6-v0.1.111-1.2.0
softPodAntiAffinity: true
resources:
limits:
cpu: 2
memory: 12Gi
requests:
cpu: 1
memory: 8Gi
datacenters:
- metadata:
name: dc1
size: 3
containers:
- name: cassandra
env:
- name: AXON_AGENT_KEY
valueFrom:
secretKeyRef:
name: axonops-credentials
key: api-key
- name: AXON_AGENT_ORG
valueFrom:
secretKeyRef:
name: axonops-credentials
key: org
- name: AXON_AGENT_SERVER_HOST
value: "axon-server-agent.axonops.svc.cluster.local"
- name: AXON_AGENT_SERVER_PORT
value: "1888"
- name: AXON_AGENT_TLS_MODE
value: "false"
config:
jvmOptions:
heap_initial_size: 8G
heap_max_size: 8G
storageConfig:
cassandraDataVolumeClaimSpec:
storageClassName: ""
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi

Apply the cluster configuration:

Terminal window
kubectl apply -f k8ssandra-cluster.yaml

Monitor the deployment:

Terminal window
# Watch the K8ssandraCluster status
kubectl get k8ssandracluster -n k8ssandra-operator -w
# Watch pods being created
kubectl get pods -n k8ssandra-operator -w

Check the K8ssandraCluster resource:

Terminal window
kubectl get k8ssandracluster -n k8ssandra-operator

Expected output:

NAME AGE
axonops-k8ssandra-5 5m

Get detailed status:

Terminal window
kubectl describe k8ssandracluster axonops-k8ssandra-5 -n k8ssandra-operator

Verify all Cassandra pods are running:

Terminal window
kubectl get pods -n k8ssandra-operator -l app.kubernetes.io/managed-by=cass-operator

Expected output:

NAME READY STATUS RESTARTS AGE
axonops-k8ssandra-5-dc1-rack1-0 2/2 Running 0 5m
axonops-k8ssandra-5-dc1-rack1-1 2/2 Running 0 4m
axonops-k8ssandra-5-dc1-rack1-2 2/2 Running 0 3m

Verify persistent volume claims are bound:

Terminal window
kubectl get pvc -n k8ssandra-operator

Expected output:

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
server-data-axonops-k8ssandra-5-dc1-rack1-0 Bound pv-xxx 10Gi RWO local-path
server-data-axonops-k8ssandra-5-dc1-rack1-1 Bound pv-xxx 10Gi RWO local-path
server-data-axonops-k8ssandra-5-dc1-rack1-2 Bound pv-xxx 10Gi RWO local-path

Connect to a Cassandra pod and check the cluster status:

Terminal window
kubectl exec -it axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator -c cassandra -- nodetool status

Expected output:

Datacenter: dc1
===============
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns Host ID Rack
UN 10.42.0.10 100 KB 16 33.3% xxx-xxx-xxx-xxx-xxx rack1
UN 10.42.0.11 100 KB 16 33.3% xxx-xxx-xxx-xxx-xxx rack1
UN 10.42.0.12 100 KB 16 33.4% xxx-xxx-xxx-xxx-xxx rack1

Check the Cassandra logs for axon-agent connection:

Terminal window
kubectl logs axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator -c cassandra | grep -i axon

You should see log entries indicating the agent has connected to the AxonOps server.

SettingValueDescription
Cassandra Version5.0.6+Apache Cassandra version with axon-agent
Container Imageghcr.io/axonops/k8ssandra/cassandraPre-built image with axon-agent
Anti-AffinitySoftPrefers to schedule pods on different nodes
Default Replicas3Number of Cassandra nodes per datacenter
resources:
limits:
cpu: 2
memory: 4Gi
requests:
cpu: 1
memory: 2Gi
jvmOptions:
heap_initial_size: 2G
heap_max_size: 2G

Warning

Ensure heap size is set appropriately based on available memory. A general rule is to set the heap to 25-50% of the available RAM, with a maximum of 8GB for optimal GC performance.

To deploy a multi-datacenter cluster:

apiVersion: k8ssandra.io/v1alpha1
kind: K8ssandraCluster
metadata:
name: axonops-k8ssandra-multi-dc
namespace: k8ssandra-operator
spec:
cassandra:
serverVersion: "5.0.6"
serverImage: ghcr.io/axonops/k8ssandra/cassandra:5.0.6-v0.1.111-1.2.0
datacenters:
- metadata:
name: dc1
size: 3
containers:
- name: cassandra
env:
- name: AXON_AGENT_KEY
valueFrom:
secretKeyRef:
name: axonops-credentials
key: api-key
- name: AXON_AGENT_ORG
valueFrom:
secretKeyRef:
name: axonops-credentials
key: org
- name: AXON_AGENT_CLUSTER_NAME
value: "k8ssandra-multi-dc"
storageConfig:
cassandraDataVolumeClaimSpec:
storageClassName: local-path
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
- metadata:
name: dc2
size: 3
containers:
- name: cassandra
env:
- name: AXON_AGENT_KEY
valueFrom:
secretKeyRef:
name: axonops-credentials
key: api-key
- name: AXON_AGENT_ORG
valueFrom:
secretKeyRef:
name: axonops-credentials
key: org
- name: AXON_AGENT_CLUSTER_NAME
value: "k8ssandra-multi-dc"
storageConfig:
cassandraDataVolumeClaimSpec:
storageClassName: local-path
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi

For better security, store AxonOps credentials in a Kubernetes Secret:

Terminal window
kubectl create secret generic axonops-credentials \
-n k8ssandra-operator \
--from-literal=api-key='your-api-key-here' \
--from-literal=org='your-org-name'

Reference the secret in your cluster configuration:

containers:
- name: cassandra
env:
- name: AXON_AGENT_KEY
valueFrom:
secretKeyRef:
name: axonops-credentials
key: api-key
- name: AXON_AGENT_ORG
valueFrom:
secretKeyRef:
name: axonops-credentials
key: org

Forward the CQL port to your local machine:

Terminal window
kubectl port-forward -n k8ssandra-operator svc/axonops-k8ssandra-5-dc1-service 9042:9042

Connect using cqlsh or any CQL client:

Terminal window
cqlsh localhost 9042

For internal cluster access, K8ssandra automatically creates a service:

Terminal window
kubectl get svc -n k8ssandra-operator

Access from within the cluster using:

axonops-k8ssandra-5-dc1-service.k8ssandra-operator.svc.cluster.local:9042

Check the pod events:

Terminal window
kubectl describe pod axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator

Common issues:

  • Insufficient resources: Verify your nodes have enough CPU and memory
  • Storage class not available: Ensure the storage class exists (kubectl get storageclass)
  • Image pull errors: Verify network connectivity and image name

Check Cassandra logs:

Terminal window
kubectl logs axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator -c cassandra

Common issues:

  • Network policies: Ensure pods can communicate on port 7000 (gossip)
  • DNS resolution: Verify pod DNS is working correctly
  • Anti-affinity conflicts: Check if there are enough nodes for the anti-affinity rules
  1. Verify AxonOps server is accessible:
Terminal window
kubectl run test-connection --rm -it --image=busybox -- \
nc -zv axon-server-agent.axonops.svc.cluster.local 1888
  1. Check agent environment variables:
Terminal window
kubectl exec axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator -c cassandra -- env | grep AXON
  1. Check agent logs in the Cassandra container:
Terminal window
kubectl logs axonops-k8ssandra-5-dc1-rack1-0 -n k8ssandra-operator -c cassandra | grep -i "axon\|agent"

If persistent volumes are not binding:

Terminal window
kubectl get pv
kubectl get pvc -n k8ssandra-operator
kubectl describe pvc <pvc-name> -n k8ssandra-operator

Common issues:

  • No available volumes: Create persistent volumes or use dynamic provisioning
  • Storage class mismatch: Verify the storage class name is correct
  • Access mode incompatibility: Ensure your storage supports the requested access mode

To scale up the cluster, update the size field:

Terminal window
kubectl patch k8ssandracluster axonops-k8ssandra-5 -n k8ssandra-operator \
--type merge \
-p '{"spec":{"cassandra":{"datacenters":[{"metadata":{"name":"dc1"},"size":5}]}}}'

Or edit the cluster directly:

Terminal window
kubectl edit k8ssandracluster axonops-k8ssandra-5 -n k8ssandra-operator

Change the size value:

datacenters:
- metadata:
name: dc1
size: 5 # Changed from 3

Warning

Scaling down requires careful consideration. Ensure your replication factor allows for the reduced node count without data loss.

To scale down:

Terminal window
kubectl patch k8ssandracluster axonops-k8ssandra-5 -n k8ssandra-operator \
--type merge \
-p '{"spec":{"cassandra":{"datacenters":[{"metadata":{"name":"dc1"},"size":2}]}}}'

K8ssandra will automatically handle the decommissioning process.

K8ssandra includes Medusa for backup and restore operations. Configure Medusa by adding it to your cluster spec:

spec:
medusa:
storageProperties:
storageProvider: s3
bucketName: k8ssandra-backups
region: us-east-1
storageSecretRef:
name: medusa-s3-credentials

Refer to the K8ssandra Medusa documentation for detailed backup and restore procedures.

To remove the cluster:

Terminal window
# Delete the K8ssandraCluster
kubectl delete k8ssandracluster axonops-k8ssandra-5 -n k8ssandra-operator
# Optionally, delete PVCs (this will delete all data)
kubectl delete pvc -n k8ssandra-operator -l app.kubernetes.io/managed-by=cass-operator
# Uninstall the K8ssandra operator
helm uninstall k8ssandra-operator -n k8ssandra-operator
# Delete the namespace
kubectl delete namespace k8ssandra-operator