This guide describes how to deploy a MigratoryData cluster with Azure Kubernetes Service (AKS) and using Azure Event Hub as a MigratoryData backplane.

Before reading this document, it is recommended to read Apache Kafka as a MigratoryData backplane to enable IoT applications with millions of devices.

Prerequisites
  • A Microsoft Azure account
  • Azure CLI
  • Kubectl tool

Variables

Let’s use the following variables for this tutorial:

export RESOURCE_GROUP=rg-migratorydata
export AKS_CLUSTER=aks-migratorydata
export EVENTHUBS_NAMESPACE=evhns-migratorydata
export EVENTHUBS_TOPIC=vehicles

Create an AKS cluster

Let’s create a Kubernetes cluster with at least three and at most five nodes.

# login to AKS
az login

# create the resource group
az group create --name $RESOURCE_GROUP --location eastus

# create the cluster and enable the cluster autoscaling
az aks create \
  --resource-group $RESOURCE_GROUP \
  --name $AKS_CLUSTER \
  --node-count 3 \
  --vm-set-type VirtualMachineScaleSets \
  --generate-ssh-keys \
  --load-balancer-sku standard \
  --enable-cluster-autoscaler \
  --min-count 3 \
  --max-count 5

Connect to the AKS cluster and check if the AKS nodes are ready

# connect to the AKS cluster
az aks get-credentials \
--resource-group $RESOURCE_GROUP \
--name $AKS_CLUSTER

# check if the nodes of the cluster are up
kubectl get nodes

Create an Azure Event Hubs topic

To create a Kafka topic on Azure Event Hubs, run the following commands:

# create a namespace into the Event Hubs
az eventhubs namespace create --name $EVENTHUBS_NAMESPACE \
--resource-group $RESOURCE_GROUP -l eastus

# create the Kafka topic
az eventhubs eventhub create --name $EVENTHUBS_TOPIC \
--resource-group $RESOURCE_GROUP \
--namespace-name $EVENTHUBS_NAMESPACE

Authenticate to Azure Event Hubs with SASL using JAAS

# fetch the Event Hubs rule/policy and pick the value of the "name" 
# attribute from the JSON response of the following command
az eventhubs namespace authorization-rule list \
--resource-group $RESOURCE_GROUP \
--namespace-name $EVENTHUBS_NAMESPACE

# suppose the policy picked above is RootManageSharedAccessKey, 
# then pick the value of the attribute "primaryConnectionString" 
# from the JSON response of the following command
az eventhubs namespace authorization-rule keys list \
--resource-group $RESOURCE_GROUP \
--namespace-name $EVENTHUBS_NAMESPACE \
--name RootManageSharedAccessKey

The value of the attribute primaryConnectionString picked from the response of the last command should look as follows:

Endpoint=sb://evhns-migratorydata.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxxxxxxxxxxxxx

Therefore, the JAAS config to authenticate to Azure Event Hubs with SASL should look as follows:

KafkaClient {
        org.apache.kafka.common.security.plain.PlainLoginModule required
        username="$ConnectionString"
        password="Endpoint=sb://evhns-migratorydata.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxxxxxxxxxxxxx";
};

Copy the JAAS config to a file, say jaas.config. We will need this configuration later to connect a Kafka consumer and producer to the Azure Event Hubs with SASL.

Create secret from the JAAS config file

Because the JAAS config file obtained in the previous step must be included in the pod configuration, we should create a secret from jaas.config which will be mounted as a volume in Kubernetes.

kubectl create secret generic migratory-secret --from-file=jaas.config

Deploy MigratoryData cluster

Copy the following Kubernetes configuration to a file, say cluster.yaml and edit it by replacing the variables EVENTHUBS_NAMESPACE and EVENTHUBS_TOPIC with the values defined or obtained above:

apiVersion: v1
kind: Service
metadata:
  name: migratorydata-cs
  labels:
    app: migratorydata
spec:
  type: LoadBalancer
  ports:
    - name: client-port
      port: 80
      protocol: TCP
      targetPort: 8800
  selector:
    app: migratorydata
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: migratorydata
spec:
  selector:
    matchLabels:
      app: migratorydata
  replicas: 3
  template:
    metadata:
      labels:
        app: migratorydata
    spec:
      containers:
      - name: migratorydata-cluster
        imagePullPolicy: Always
        image: migratorydata/server:latest
        volumeMounts:
        - name: migratory-secret
          mountPath: "/migratorydata/secrets/jaas.config"
          subPath: jaas.config
          readOnly: true
        env:
          - name: MIGRATORYDATA_EXTRA_OPTS
            value: "-DMemory=128MB \
              -DLogLevel=INFO \
              -DX.ConnectionOffload=true \
              -DClusterEngine=kafka"
          - name: MIGRATORYDATA_KAFKA_EXTRA_OPTS
            value: "-Dbootstrap.servers=$EVENTHUBS_NAMESPACE.servicebus.windows.net:9093 \
              -Dtopics=$EVENTHUBS_TOPIC \
              -Dsecurity.protocol=SASL_SSL \
              -Dsasl.mechanism=PLAIN
              -Djava.security.auth.login.config=/migratorydata/secrets/jaas.config"
          - name: MIGRATORYDATA_JAVA_EXTRA_OPTS
            value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
        resources:
          requests:
            memory: "256Mi"
            cpu: "0.5"
        ports:
          - name: client-port
            containerPort: 8800
        readinessProbe:
          tcpSocket:
            port: 8800
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          tcpSocket:
            port: 8800
          initialDelaySeconds: 10
          periodSeconds: 5
      volumes:
      - name: migratory-secret
        secret:
          secretName: migratory-secret

The manifest above contains a Service and a Deployment. The Service is used to handle the clients of the cluster over the port 80.

In order to deploy the cluster of MigratoryData on the AKS cluster, run the following command:

$ kubectl apply -f cluster.yaml

Check if the pods are up and running

By running the following command, you can check that the three pods of the example configuration are up and running:

kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
migratorydata-57848575bd-4tnbz   1/1     Running   0          4m32s
migratorydata-57848575bd-gjmld   1/1     Running   0          4m32s
migratorydata-57848575bd-tcbtf   1/1     Running   0          4m32s

and you can check the logs of each cluster member by running a command as follows:

kubectl logs migratorydata-57848575bd-4tnbz

Also, by running the following command, you can check that the service is up and running:

kubectl get svc

NAME                  TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
kubernetes            ClusterIP      10.0.0.1      <none>        443/TCP        37m
migratorydata-cs      LoadBalancer   10.0.90.187   YourExternalIP    80:30546/TCP   5m27s

You should now be able to connect to http://YourExternalIP and run the demo app bundled with the MigratoryData broker.

Scaling MigratoryData on AKS

The stateless nature of the MigratoryData broker using the Azure Event Hubs backplane, where each cluster member is independent from the others, highly simplifies the horizontal scaling on AKS.

Manual Scaling

For example, if the load of your system increases substantially, and supposing your nodes have enough resources available, you can add two new members to the cluster by modifying the replicas field as follows:

kubectl scale deployment migratorydata --replicas=5 

If the load of your system decreases significantly, then you might remove three members from the cluster by modifying the replicas field as follows:

kubectl scale deployment migratorydata --replicas=2

Autoscaling

Manual scaling is practical if the load of your system changes gradually. Otherwise, you can use the autoscaling feature of Kubernetes.

Kubernetes can monitor the load of your system, typically expressed in CPU usage, and scale your MigratoryData cluster up and down by automatically modifying the replicas field.

In the example above, to add one or more new members up to a maximum of 5 cluster members if the CPU usage of the existing members becomes higher than 50%, or remove one or more of the existing members if the CPU usage of the existing members becomes lower than 50%, use the following command:

kubectl autoscale deployment migratorydata --cpu-percent=50 --min=2 --max=5

Now, you can display information about the autoscaler object above using the following command:

kubectl get hpa

and display CPU usage of cluster members with:

kubectl top pods

While testing cluster autoscaling, it is important to be aware that the Kubenetes autoscaler gets the CPU usage periodically from the cluster members, so autoscaling might appear to be not immediate. However, this is normal Kubernetes behavior.

Node Autoscaling

Cloud infrastructure like Azure Kubernetes Service (AKS) can be configured to automatically spin up additional nodes if the resources of the existing nodes are insufficient to create new cluster members. In the example above, we have created the AKS cluster with a minimum of 3 nodes and a maximum of 5 nodes and enabled node autoscaling with the command that we recall here:

# create the cluster and enable the cluster autoscaling
az aks create \
  --resource-group $RESOURCE_GROUP \
  --name $AKS_CLUSTER \
  --node-count 3 \
  --vm-set-type VirtualMachineScaleSets \
  --generate-ssh-keys \
  --load-balancer-sku standard \
  --enable-cluster-autoscaler \
  --min-count 3 \
  --max-count 5

While testing node autoscaling, it is important to be aware that adding or disposing nodes from AKS might take some time. For example, to evict an unused node back to AKS might take up to several minutes. Here are more details about time granularity on AKS cluster node autoscaling:

https://docs.microsoft.com/en-us/azure/aks/cluster-autoscaler#using-the-autoscaler-profile

Node Failure Testing

MigratoryData clustering tolerates node failures provided that at least one node remain up and running. In order to test an AKS node failure, use:

kubectl drain <node-name> --force --delete-local-data --ignore-daemonsets

Then, to start an AKS node, use:

kubectl uncordon <node-name>

Maintenance

If something gets wrong with the commands above, you can remove the MigratoryData cluster with the command below and try again:

kubectl delete -f cluster.yaml

Finally when you don’t need anymore the AKS cluster and the Event Hubs topic for testing, remove the resources allocated with:

az group delete --name $RESOURCE_GROUP --yes --no-wait