<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Burhan Shaikh</title>
    <description>The latest articles on DEV Community by Burhan Shaikh (@burhanshaikh).</description>
    <link>https://dev.to/burhanshaikh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F116088%2F38fe1053-3079-4696-8e25-476974a9910a.jpg</url>
      <title>DEV Community: Burhan Shaikh</title>
      <link>https://dev.to/burhanshaikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/burhanshaikh"/>
    <language>en</language>
    <item>
      <title>Tuning EMQX to Scale to One Million Concurrent Connection on Kubernetes</title>
      <dc:creator>Burhan Shaikh</dc:creator>
      <pubDate>Thu, 11 May 2023 06:23:51 +0000</pubDate>
      <link>https://dev.to/infracloud/tuning-emqx-to-scale-to-one-million-concurrent-connection-on-kubernetes-g9f</link>
      <guid>https://dev.to/infracloud/tuning-emqx-to-scale-to-one-million-concurrent-connection-on-kubernetes-g9f</guid>
      <description>&lt;p&gt;When building an IoT-based service, we need to implement a messaging mechanism that transmits data collected by the IoT devices to a hub or a server. That mechanism is known as a messaging protocol. A messaging protocol is a set of rules and formats that are agreed upon among entities that want to communicate with each other.&lt;/p&gt;

&lt;p&gt;When dealing with IoT, one of the first things that come to mind is the limited processing, networking, and storage capabilities these devices operate with. These constraints make it challenging to implement a messaging protocol to facilitate communication.&lt;br&gt;
Ideally, we should use a protocol that takes all these issues into consideration.&lt;/p&gt;

&lt;p&gt;Enter MQTT. A messaging protocol that is lightweight, efficient, and reliable, designed specifically for the low-power and low-bandwidth environments of IoT devices.&lt;br&gt;
It has become the go-to choice for developers building services on top of devices that require fast, real-time communication and efficient use of limited resources.&lt;/p&gt;

&lt;p&gt;As the clients grow, scaling becomes a concern even for a service running on a protocol as efficient as MQTT. To ensure clients can enjoy seamless connectivity, it's essential to optimize the infrastructure, components, and processes of the MQTT Broker itself.&lt;/p&gt;

&lt;p&gt;There are multiple brokers available like &lt;a href="https://vernemq.com/intro/index.html"&gt;VerneMQ&lt;/a&gt;, &lt;a href="https://www.hivemq.com/"&gt;HiveMQ&lt;/a&gt;, &lt;a href="https://github.com/eclipse/mosquitto"&gt;Mosquitto&lt;/a&gt;.&lt;br&gt;
For this blog post, we will be using the &lt;a href="https://www.emqx.com/en/products/emqx"&gt;open source version of EMQX&lt;/a&gt; as our MQTT Broker of choice, as it is widely used in the industry thanks to its high scalability, reliability, and performance.&lt;/p&gt;

&lt;p&gt;In this post, we'll dive deeper into how we could run EMQX on Kubernetes on a scale of up to 1 million connections and the challenges associated with it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is MQTT?
&lt;/h2&gt;

&lt;p&gt;Messaging is a mechanism that allows systems to exchange information asynchronously and reliably without being tightly coupled to one another.&lt;/p&gt;

&lt;p&gt;Some of the widely used protocols are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc6455"&gt;WebSocket&lt;/a&gt; - powers live chats. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webrtc.org/"&gt;WebRTC&lt;/a&gt; - enables real-time communication between browsers and devices.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://xmpp.org/"&gt;XMPP&lt;/a&gt; - allows for cross-platform communication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is another protocol that has been designed for Machine to Machine communication, especially for IoT devices that operate with resource and network constraints and that is MQTT.&lt;/p&gt;

&lt;p&gt;MQTT uses a &lt;a href="https://aws.amazon.com/pub-sub-messaging/"&gt;Pub/Sub model&lt;/a&gt; and offers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Small code footprint&lt;/li&gt;
&lt;li&gt;Compressible binary format for message payloads&lt;/li&gt;
&lt;li&gt;Reduced device active time for sending and receiving messages&lt;/li&gt;
&lt;li&gt;Quality of Service (QoS) levels&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why run MQTT on Kubernetes?
&lt;/h2&gt;

&lt;p&gt;Running MQTT on Kubernetes offers a wide range of benefits such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single point of contact:&lt;/strong&gt; Clients have a single endpoint to connect to and the load gets distributed across the broker pods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fault tolerance:&lt;/strong&gt; Even if some of the broker pods/nodes go down, there are others available to keep your service running reliably. The client does not need to take any corrective action, they will automatically get connected to another broker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intelligent scaling:&lt;/strong&gt; Add and remove brokers on the fly based on events and/or traffic/resource utilization. This helps you to cater to demand and optimize your spending.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Self healing and efficient administration:&lt;/strong&gt; A broker usually serves hundreds of IoT devices and at such a scale, a self-healing platform like Kubernetes reduces the overhead of repetitive manual administration and improves efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What is EMQX?
&lt;/h2&gt;

&lt;p&gt;EMQX is one of the most widely used MQTT Platforms in the industry. It has the capability to support millions of connections and offers sub-millisecond latency. &lt;/p&gt;

&lt;p&gt;It offers distributed MQTT brokers that enable us to use it natively on Kubernetes. It is essential for the broker to be distributed to be deployed on Kubernetes because a client’s connection could end up on any of the broker pods and messages would need  to be forwarded among them so that they are properly delivered to the relevant subscribers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XhO2P-tZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fies3ngh2ijl5oiq84mb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XhO2P-tZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fies3ngh2ijl5oiq84mb.png" alt="EMQX broker cluster" width="800" height="484"&gt;&lt;/a&gt;&lt;br&gt;
EMQX broker cluster maintains two tables and a tree:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subscription table:&lt;/strong&gt; Topics are mapped to the subscribers and the table exists only on the broker node to which the subscriber is connected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing table:&lt;/strong&gt; Topics are mapped to the node to which the subscriber is connected to. Every broker node maintains a copy of this table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topic tree:&lt;/strong&gt; Every broker node maintains a topic tree representing the relation among the topics, subscribers and broker nodes.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Running EMQX on Kubernetes
&lt;/h2&gt;

&lt;p&gt;The first step is to spin up a Kubernetes Cluster. To ensure optimal performance, it is essential to select the right machine size for our cluster’s worker nodes onto which the broker pods will be deployed.&lt;/p&gt;

&lt;p&gt;The decision to select the right machine size comes down to several factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of clients connected to the broker.&lt;/li&gt;
&lt;li&gt;Message throughput - Number of published and subscribed messages processed by the broker per second.&lt;/li&gt;
&lt;li&gt;Quality of service - Higher the QoS, the more resources are consumed.&lt;/li&gt;
&lt;li&gt;Size of the payload - The larger the size of the payload to be sent over MQTT the more resource is the utilization.&lt;/li&gt;
&lt;li&gt;Number of topics - As more topics get added to the route table resource, utilization also increases. Distributed EMQX uses Route Tables to route messages to the right nodes in order to deliver messages to the subscriber.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the simplest ways out is to use &lt;a href="https://www.emqx.com/en/server-estimate"&gt;EMQX Server Estimate calculator&lt;/a&gt;. This calculator has been developed and tested according to the above-mentioned parameters. You will need to feed the number of clients (expected) that will connect to the broker and the throughput (number of messages sent and received) in a second.&lt;/p&gt;

&lt;p&gt;Let’s consider that for our use case, we need 16 CPU Cores and 32 GB of Memory. &lt;br&gt;
Instead of running a single node of this size, we could divide it among multiple nodes and then deploy multiple EMQX broker pods on it. This would introduce fault tolerance and we can scale in and scale out both pods and nodes depending on the usage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can use the &lt;a href="https://learnk8s.io/kubernetes-instance-calculator"&gt;Learnk8s Kubernetes Instance Calculator&lt;/a&gt; to see which instance type will offer you better efficiency by feeding the broker pod resource requests to the calculator. &lt;/li&gt;
&lt;li&gt;You will however need to test this out by performing load tests to see how well the broker performs under stress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To Install the EMQX Broker on Kubernetes we can use its &lt;a href="https://github.com/emqx/emqx/tree/master/deploy/charts/emqx"&gt;Helm Chart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are using Amazon EKS. You could use any other Kubernetes cluster as long as it allows you to modify &lt;code&gt;sysctl&lt;/code&gt; parameters or to run a user data script of sysctl commands on the nodes and also allows you to add kubelet parameters to the cluster bootstrap script in order to whitelist them.&lt;br&gt;
Most of the major cloud providers allow such configuration in some way or the other. We have specified a couple of examples later in this blog post.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tuning for 1 Million Concurrent Connections
&lt;/h2&gt;

&lt;p&gt;When we want our EMQX system to support 1 million concurrent connections, we will need to tune different system components to make this happen. Using out-of-the-box configuration might not help us reach this scale.&lt;/p&gt;

&lt;p&gt;Here are the following components that need calibration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Linux Kernel&lt;/li&gt;
&lt;li&gt;Erlang VM&lt;/li&gt;
&lt;li&gt;The Broker&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Linux Kernel Tuning
&lt;/h3&gt;

&lt;p&gt;Tuning the kernel level parameters is not very straightforward to do so on a managed Kubernetes service especially when sysctl commands are not supported out of the box - as it should be for security reasons.&lt;/p&gt;

&lt;p&gt;EMQX recommend's to &lt;a href="https://www.emqx.io/docs/en/v4.4/tutorial/tune.html#linux-kernel-tuning"&gt;calibrate these parameters&lt;/a&gt;. They can improve the performance of any broker and not just EMQX.&lt;/p&gt;

&lt;p&gt;We will be going through how to configure them but first let’s define them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sockets IPC API used in Linux TCP/IP connections uses file descriptors. Therefore, the number of opened &lt;a href="https://www.baeldung.com/linux/limit-file-descriptors"&gt;file descriptors&lt;/a&gt; is one of the first limits we may face.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Sets the maximum number of file handles allowed by the kernel&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.file-max&lt;span class="o"&gt;=&lt;/span&gt;2097152

  &lt;span class="c"&gt;# Sets the maximum number of open file descriptors that a process can have&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.nr_open&lt;span class="o"&gt;=&lt;/span&gt;2097152
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;MQTT runs on TCP so we will need to modify several of its parameters as well.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Sets the maximum number of connections that can be queued for acceptance by the kernel. &lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.somaxconn&lt;span class="o"&gt;=&lt;/span&gt;32768

  &lt;span class="c"&gt;# Sets the maximum number of SYN requests that can be queued by the kernel&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_max_syn_backlog&lt;span class="o"&gt;=&lt;/span&gt;16384

  &lt;span class="c"&gt;# Setting the minimum, default and maximum size of TCP Buffer&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_rmem&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1024 4096 16777216'&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_wmem&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1024 4096 16777216'&lt;/span&gt;

  &lt;span class="c"&gt;# Setting Parameters for TCP Connection Tracking&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.netfilter.nf_conntrack_tcp_timeout_time_wait&lt;span class="o"&gt;=&lt;/span&gt;30

  &lt;span class="c"&gt;# Controls the maximum number of entries in the TCP time-wait bucket table&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_max_tw_buckets&lt;span class="o"&gt;=&lt;/span&gt;1048576

  &lt;span class="c"&gt;# Controls Timeout for FIN-WAIT-2 Sockets:&lt;/span&gt;
  sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_fin_timeout&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that we have specified the kernel parameters that need calibration, we need an approach to implement this when running our broker on Kubernetes.&lt;/p&gt;

&lt;p&gt;In the container world &lt;code&gt;sysctls&lt;/code&gt; are classified into two types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Non-Namespaced:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The ones that should be set on node/global level and the same values are used across all processes running on the node, be it containerized or non-containerized. They cannot be set on a container/pod level.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some of them can still be set via a container on the node level but it will require elevated privileges and the setting can impact the entire node. This is not recommended from a stability and security point of view.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; Namespaced: The ones that can be independently set on a container level&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kubernetes further categorizes sysctl calls into two:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Safe: sysctl calls that are unlikely to cause any system instability or security issues when modified and do not influence other pods running on the node.&lt;/li&gt;
&lt;li&gt;Unsafe: sysctl calls that have the potential to cause system instability or security issues and can affect other workloads running on the machine. Some kernel parameters can even allow a container to consume excessive system resources leading to a denial of service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/#safe-and-unsafe-sysctls"&gt;read more about sysctls here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s list the sysctls that we can set on the node level, basically the non-namespaced sysctls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.file-max&lt;span class="o"&gt;=&lt;/span&gt;2097152
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; fs.nr_open&lt;span class="o"&gt;=&lt;/span&gt;2097152
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the namespaced sysctls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.somaxconn&lt;span class="o"&gt;=&lt;/span&gt;65536
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_max_syn_backlog&lt;span class="o"&gt;=&lt;/span&gt;16384
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_rmem&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1024 4096 16777216'&lt;/span&gt;
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_wmem&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1024 4096 16777216'&lt;/span&gt;
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.netfilter.nf_conntrack_tcp_timeout_time_wait&lt;span class="o"&gt;=&lt;/span&gt;30
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_max_tw_buckets&lt;span class="o"&gt;=&lt;/span&gt;1048576
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.tcp_fin_timeout&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every network namespace gets its parameter values from the init namespace. A container has its own network namespace. This is why setting the &lt;code&gt;net.* sysctls&lt;/code&gt; on the node level will configure them on the root network namespace and not reflect inside of the container. &lt;/p&gt;

&lt;p&gt;There are some more namespaced sysctls that will improve the performance but because of an &lt;a href="https://github.com/moby/moby/issues/42282#issuecomment-817748865"&gt;active issue&lt;/a&gt; we are not able to set them on the container level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sets the size of the backlog queue for the network device     &lt;/span&gt;
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.netdev_max_backlog&lt;span class="o"&gt;=&lt;/span&gt;16384

&lt;span class="c"&gt;# Amount of memory that is allocated for storing incoming and outgoing  data for a socket&lt;/span&gt;
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.rmem_default&lt;span class="o"&gt;=&lt;/span&gt;262144
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.wmem_default&lt;span class="o"&gt;=&lt;/span&gt;262144

&lt;span class="c"&gt;# Setting the maximum amount of memory for the socket buffers&lt;/span&gt;
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.rmem_max&lt;span class="o"&gt;=&lt;/span&gt;16777216
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.wmem_max&lt;span class="o"&gt;=&lt;/span&gt;16777216
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.optmem_max&lt;span class="o"&gt;=&lt;/span&gt;16777216
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Setting the non-namespaced sysctls
&lt;/h4&gt;

&lt;p&gt;We will run the sysctl commands at the time of node creation before it is registered as a worker node for the Kubernetes cluster. &lt;br&gt;
This way we can calibrate the kernel parameters consistently across the nodes at scale.&lt;/p&gt;

&lt;p&gt;We will need to set the sysctl commands in the &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/19.7.0/submodules/_user_data#input_pre_bootstrap_user_data"&gt;pre_bootstrap_user_data&lt;/a&gt; of self-managed Amazon EKS node groups via Terraform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;self_managed_node_groups = {
  worker-a = {
    name                                = "worker-a"
    key_name                            = "worker-a"
    min_size                            = 1
    max_size                            = 4
    desired_size                        = 2
    create_launch_template              = true
    enable_bootstrap_user_data          = true
    pre_bootstrap_user_data             = &amp;lt;&amp;lt;-EOT
    sysctl -w fs.file-max=2097152
    sysctl -w fs.nr_open=2097152
    EOT
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Azure Kubernetes Service (AKS) supports customizing the Linux kernel parameters via the &lt;a href="https://learn.microsoft.com/en-us/azure/aks/custom-node-configuration#linux-os-custom-configuration"&gt;Linux OS Custom Configuration&lt;/a&gt;. &lt;br&gt;
If using Terraform we need to specify these parameters under the &lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster_node_pool#linux_os_config"&gt;linux_os_config&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Google Kubernetes Engine (GKE) allows the same via the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/node-system-config"&gt;Node System Config&lt;/a&gt;.&lt;br&gt;
If using Terraform we need to specify the parameters under node pool -&amp;gt; &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_node_pool#node_config"&gt;node_config&lt;/a&gt; -&amp;gt; &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster#linux_node_config"&gt;linux_node_config&lt;/a&gt;. &lt;/p&gt;
&lt;h4&gt;
  
  
  Setting namespaced sysctls
&lt;/h4&gt;

&lt;p&gt;Most of the sysctl commands that we want to use fall under the unsafe category and have been disabled by default by Kubernetes.&lt;/p&gt;

&lt;p&gt;To enable unsafe sysctl calls on Kubernetes we need to whitelist them by adding them to the kubelet start script, and this is to be done per node group basis.&lt;/p&gt;

&lt;p&gt;It is a good practice to restrict these changes only to specific nodes and schedule the workloads that need these parameters enabled onto them. In our case, we should only schedule EMQX pods on these nodes.&lt;br&gt;
This will help reduce the surface for an attack. &lt;br&gt;
&lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/"&gt;Taints and Tolerations&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity"&gt;Node Affinity&lt;/a&gt; will help us implement this practice.&lt;/p&gt;

&lt;p&gt;We need to pass these kernel parameters with a flag &lt;code&gt;--allowed-unsafe-sysctls&lt;/code&gt; to the kubelet.&lt;br&gt;
On Amazon EKS we need to pass them under the &lt;code&gt;--kubelet-extra-args&lt;/code&gt; flag.&lt;br&gt;
Using Terraform we can specify these parameters in the self-managed node group’s &lt;code&gt;bootstrap_extra_args&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_group_2 = {
      name                  = "venus"
      min_size              = 1
      max_size              = 5
      desired_size          = 2
      bootstrap_extra_args  = "--kubelet-extra-args '--allowed-unsafe-sysctls \"net.core.somaxconn,net.ipv4,
      tcp_max_syn_backlog,net.ipv4.tcp_rmem,net.ipv4.tcp_wmem,net.netfilter.nf_conntrack_tcp_timeout_time_wait,
      net.ipv4.tcp_max_tw_buckets,net.ipv4.tcp_fin_timeout\"'"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On AKS we can specify them using &lt;a href="https://learn.microsoft.com/en-us/azure/aks/custom-node-configuration#linux-kubelet-custom-configuration"&gt;Linux Kubelet Custom Configuration&lt;/a&gt;. If you are using Terraform &lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster_node_pool#kubelet_config"&gt;kubelet_config&lt;/a&gt; block has an attribute &lt;code&gt;allowed_unsafe_sysctls&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once these are whitelisted we will have to set them via the &lt;code&gt;values.yaml&lt;/code&gt; file under &lt;a href="https://github.com/emqx/emqx/blob/fb3c0c1fe9c599e661370f787bde1054a91e6e6a/deploy/charts/emqx/values.yaml#LL221"&gt;podSecurityContext&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;sysctls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.core.somaxconn&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;32768"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.tcp_max_syn_backlog&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16384"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.tcp_rmem&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1024 4096 &lt;/span&gt;&lt;span class="m"&gt;16777216&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.tcp_wmem&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1024 4096 &lt;/span&gt;&lt;span class="m"&gt;16777216&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.ip_local_port_range&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;”1000 65535”&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.tcp_max_tw_buckets&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1048576"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.ipv4.tcp_fin_timeout&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;net.netfilter.nf_conntrack_tcp_timeout_time_wait&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;30"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that only namespaced sysctls are configurable via the pod securityContext within Kubernetes.&lt;/p&gt;

&lt;p&gt;If you are using a Kubernetes version that still uses Pod Security Policies, you will need to create a pod security policy and &lt;a href="https://v1-23.docs.kubernetes.io/docs/concepts/security/pod-security-policy/#sysctl"&gt;explicitly specify the unsafe sysctls&lt;/a&gt; for the pod to be able to run those sysctl commands.&lt;/p&gt;

&lt;p&gt;If the Kubernetes version supports the Pod Security Admission and Pod Security Standards and the default settings are &lt;code&gt;enforce: privileged&lt;/code&gt; then you do not need to do anything. Otherwise, you can &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/enforce-standards-namespace-labels/#requiring-the-baseline-pod-security-standard-with-namespace-labels"&gt;configure the pod security standards&lt;/a&gt; on the namespace level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Erlang VM Tuning
&lt;/h3&gt;

&lt;p&gt;EMQX Broker is based on the &lt;a href="https://www.erlang.org/"&gt;Erlang/OTP platform&lt;/a&gt; that runs on &lt;a href="https://www.erlang.org/blog/a-brief-beam-primer/"&gt;Erlang VM&lt;/a&gt; (think on the lines of Java Virtual Machine (JVM). We have to optimize its parameters to ensure that the broker can handle a large number of concurrent connections and processes without running out of resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## Erlang Process Limit&lt;/span&gt;
node.process_limit &lt;span class="o"&gt;=&lt;/span&gt; 2097152

&lt;span class="c"&gt;## Sets the maximum number of simultaneously existing ports for this system&lt;/span&gt;
node.max_ports &lt;span class="o"&gt;=&lt;/span&gt; 2097152
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we are running EMQX Brokers as pods, we have to configure the Erlang VM parameters on the container level. &lt;br&gt;
Given that we are deploying the brokers via &lt;a href="https://github.com/emqx/emqx/tree/master/deploy/charts/emqx"&gt;Helm Chart&lt;/a&gt; we can set these parameters via the &lt;code&gt;values.yaml&lt;/code&gt; file under &lt;a href="https://github.com/emqx/emqx/blob/fb3c0c1fe9c599e661370f787bde1054a91e6e6a/deploy/charts/emqx/values.yaml#L116"&gt;emqxConfig&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;emqxConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Other configuration…&lt;/span&gt;
  &lt;span class="c1"&gt;# Erlang Process Limit&lt;/span&gt;
  &lt;span class="na"&gt;EMQX_NODE__PROCESS_LIMIT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2097152&lt;/span&gt;
  &lt;span class="c1"&gt;# Sets the maximum number of simultaneously existing ports for this system&lt;/span&gt;
  &lt;span class="na"&gt;EMQX_NODE__MAX_PORTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2097152&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  EMQX Broker Tuning
&lt;/h3&gt;

&lt;p&gt;We need to configure the broker’s external TCP listener’s configuration to increase the number of TCP connections it can accept and also specify the number of acceptor threads that the listener should use to handle incoming connection requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Other configuration…&lt;/span&gt;
&lt;span class="na"&gt;EMQX_LISTENER__TCP__EXTERNAL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0:1883"&lt;/span&gt;
&lt;span class="na"&gt;EMQX_LISTENER__TCP__EXTERNAL__ACCEPTORS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;
&lt;span class="na"&gt;EMQX_LISTENER__TCP__EXTERNAL__MAX_CONNECTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These also need to be specified under the &lt;a href="https://github.com/emqx/emqx/blob/fb3c0c1fe9c599e661370f787bde1054a91e6e6a/deploy/charts/emqx/values.yaml#L116"&gt;emqxConfig&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the EMQX Broker’s Performance
&lt;/h2&gt;

&lt;p&gt;Now that you have performed the tuning, it’s time to test the broker’s performance.&lt;/p&gt;

&lt;p&gt;We will be using the &lt;a href="https://github.com/emqx/emqtt-bench"&gt;emqtt-bench&lt;/a&gt; tool for testing. There are many MQTT benchmarking tools available such as &lt;a href="https://github.com/krylovsk/mqtt-benchmark"&gt;mqtt-benchmark&lt;/a&gt; and &lt;a href="https://www.hivemq.com/mqtt-toolbox/"&gt;others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Testing a broker with 1 million concurrent connections is not a simple task. It is recommended to create multiple test machines, perform tuning of these client machines to support a large number of connections and then start sending out MQTT connections to the broker from these clients.&lt;/p&gt;

&lt;p&gt;You can also run fewer stress-testing machines with better configuration and use multiple network cards to send out more connections from a single machine.&lt;br&gt;
For reference - &lt;a href="https://www.emqx.io/docs/en/v4.4/tutorial/benchmark.html#device-and-deployment-topology"&gt;device-and-deployment-topology&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tuning to be performed on every client machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.ip_local_port_range&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"500 65535"&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;1000000 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /proc/sys/fs/nr_open
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 100000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to have 1 million concurrent connections in total.&lt;br&gt;
Let’s start with 50 thousand connections from each stress testing machine or if you have configured your machine to use several network cards you can fire off 50k connections from each network card.&lt;/p&gt;

&lt;p&gt;Refer the &lt;a href="https://github.com/emqx/emqtt-bench#connect-benchmark"&gt;emqtt_bench conn command&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./emqtt_bench conn &lt;span class="nt"&gt;-c&lt;/span&gt; 50000 &lt;span class="nt"&gt;-h&lt;/span&gt; 216.105.138.192 &lt;span class="nt"&gt;-p&lt;/span&gt; 1883 &lt;span class="nt"&gt;-u&lt;/span&gt; myuser &lt;span class="nt"&gt;-P&lt;/span&gt; mypass

44s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;28822 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;659.00/sec
45s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;29485 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;663.00/sec
46s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30147 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;662.00/sec
47s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30797 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;650.00/sec
48s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;31450 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;653.00/sec
49s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32013 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;562.44/sec
50s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32171 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;158.16/sec
51s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32337 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;164.36/sec
52s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32502 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;165.33/sec

2m42s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49554 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;143.71/sec
2m43s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49702 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;147.70/sec
2m44s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49844 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;142.28/sec
2m45s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49986 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;143.29/sec
2m46s connect_succ &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50000 &lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14.00/sec

&lt;span class="c"&gt;# If you have multiple network cards:&lt;/span&gt;
./emqtt_bench conn &lt;span class="nt"&gt;-h&lt;/span&gt; 216.105.138.192 &lt;span class="nt"&gt;-p&lt;/span&gt; 1883 &lt;span class="nt"&gt;-c&lt;/span&gt; 50000 &lt;span class="nt"&gt;--ifaddr&lt;/span&gt; 192.168.0.100
./emqtt_bench conn &lt;span class="nt"&gt;-h&lt;/span&gt; 216.105.138.192 &lt;span class="nt"&gt;-p&lt;/span&gt; 1883 &lt;span class="nt"&gt;-c&lt;/span&gt; 50000 &lt;span class="nt"&gt;--ifaddr&lt;/span&gt; 192.168.0.101
./emqtt_bench conn &lt;span class="nt"&gt;-h&lt;/span&gt; 216.105.138.192 &lt;span class="nt"&gt;-p&lt;/span&gt; 1883 &lt;span class="nt"&gt;-c&lt;/span&gt; 50000 &lt;span class="nt"&gt;--ifaddr&lt;/span&gt; 192.168.0.102
&lt;span class="c"&gt;# So on and so forth……&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can monitor the current number of connections via the EMQX Dashboard by port forwarding the EMQX pod at &lt;code&gt;http://localhost:18083/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7l6ItmKV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsogedkorctv2cas0sqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7l6ItmKV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsogedkorctv2cas0sqo.png" alt="EMQX Dashboard" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “Connections count” shows the current active connections on each EMQX Broker Pod and it keeps on growing during the testing.&lt;/p&gt;

&lt;p&gt;You can also run this command inside the emqx pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/emqx_ctl listeners
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have Prometheus configured with push-gateway then you can use &lt;a href="https://github.com/emqx/emqx-prometheus"&gt;emqx prometheus plugin&lt;/a&gt; to push data to the push-gateway which is then pulled by the Prometheus server.&lt;br&gt;
This will also help you monitor the EMQX stats such as the number of active connections on your dashboards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tMYMBVi8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0yaut7a36gn3ysxwhd4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tMYMBVi8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0yaut7a36gn3ysxwhd4i.png" alt="EMQX Metrics" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We talked about MQTT and the benefits of running it on Kubernetes. We used EMQX as the MQTT platform of choice and delved into the nitty-gritty of scaling it to handle up to 1 million concurrent connections. We also discussed how to configure test machines to simulate these high loads and benchmark the performance of the brokers.&lt;/p&gt;

&lt;p&gt;By following the steps outlined in this post, you can ensure that your EMQX brokers are optimized to handle the demands of your MQTT-based applications and deliver the performance and reliability that your users expect.&lt;/p&gt;

&lt;p&gt;We would like to hear your perspective on this. Please feel free to reach out to &lt;a href="https://www.linkedin.com/in/tushar-purohit1994/"&gt;Tushar&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/burhanshaikh/"&gt;Burhan&lt;/a&gt; to start a discussion.&lt;/p&gt;

&lt;p&gt;Looking for help with building your DevOps strategy or want to outsource DevOps to the experts? Learn why so many startups &amp;amp; enterprises consider us as one of the best &lt;a href="https://www.infracloud.io/devops-consulting-services/"&gt;DevOps consulting &amp;amp; services companies&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emqx</category>
      <category>kubernetes</category>
      <category>mqtt</category>
    </item>
    <item>
      <title>Vardhaman Bot</title>
      <dc:creator>Burhan Shaikh</dc:creator>
      <pubDate>Fri, 22 May 2020 13:43:33 +0000</pubDate>
      <link>https://dev.to/burhanshaikh/vardhaman-bot-e7m</link>
      <guid>https://dev.to/burhanshaikh/vardhaman-bot-e7m</guid>
      <description>&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;I am &lt;a href="https://www.linkedin.com/in/burhanshaikh"&gt;Burhan&lt;/a&gt; &amp;amp; I am about to graduate with a B.Tech in Computer Science and Engineering from Vardhaman College of Engineering, Hyderabad, India.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vardhaman Bot
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JCA3zEMU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il18nqzakrhxbm3ely62.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JCA3zEMU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il18nqzakrhxbm3ely62.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I along with my batchmates &lt;a href="https://github.com/Pothulapati"&gt;Tarun&lt;/a&gt;, &lt;a href="https://github.com/pravan"&gt;Pravan&lt;/a&gt;, &lt;a href="https://github.com/KhalidRoshan"&gt;Khalid&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/shreyaasriv/"&gt;Shreya&lt;/a&gt; built a chatbot for Vardhaman College of Engineering. &lt;/p&gt;

&lt;p&gt;Vardhaman Bot helps students keep track of their attendance(We have a strict 75% attendance rule(yup, you read that right so a tracker comes handy), exam scores, GPA, Semester Results and important notices from the college.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built a chatbot and not an app
&lt;/h2&gt;

&lt;p&gt;Current students are millennials and instead of bringing them to our platform we wanted to go where they already are that is Facebook Messenger. &lt;/p&gt;

&lt;p&gt;Chatbots ideally align with the expectation of the millennials and the ways they are used to communicate with the world around them. Interacting with a chatbot is fun, it feels new and innovative when compared to navigating through an app.&lt;/p&gt;

&lt;p&gt;They bring the following to the table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immediate gratification&lt;/li&gt;
&lt;li&gt;A chance to get acquainted with new technologies&lt;/li&gt;
&lt;li&gt;Convenience and Connection&lt;/li&gt;
&lt;li&gt;Engaging in dialog&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Link to Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vWogaON8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-28d89282e0daa1e2496205e2f218a44c755b0dd6536bbadf5ed5a44a7ca54716.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/burhanshaikh"&gt;
        burhanshaikh
      &lt;/a&gt; / &lt;a href="https://github.com/burhanshaikh/vardhamanbot"&gt;
        vardhamanbot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is a Rasa NLU based chatbot created by Utor AI that functions as a personal assistant to the students of Vardhaman College of Engineering.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Vardhaman Bot&lt;/h1&gt;
&lt;p&gt;This is a Rasa NLU based chatbot created by Utor AI that functions as a personal assistant to the students of Vardhaman College of Engineering.&lt;/p&gt;
&lt;h2&gt;
Installing Dependencies and Running the chatbot&lt;/h2&gt;
&lt;p&gt;The utorai/botbase docker image has all necessary dependencies which is the base image for our Dockerfile. So build the docker image and start a container with the docker image you just built and you should be all set.&lt;/p&gt;
&lt;h2&gt;
Contribute&lt;/h2&gt;
&lt;p&gt;Though the core development and maintenance is done by Utor AI, we encourage contributions from everyone.&lt;/p&gt;
&lt;p&gt;To Contribute:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fork the repository to your github account&lt;/li&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;li&gt;The master branch is protected so after you make changes push the code to your forked repository.&lt;/li&gt;
&lt;li&gt;Create a pull request in the utorai/vardhamanbot repository and tag any of the members of Utor AI so that
your edits are reviewed and merged.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/burhanshaikh/vardhamanbot"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  How we built it
&lt;/h2&gt;

&lt;p&gt;Tech Stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programming Language: Python&lt;/li&gt;
&lt;li&gt;Framework: Microsoft Bot Framework, RASA NLU&lt;/li&gt;
&lt;li&gt;Database: Azure Table Storage&lt;/li&gt;
&lt;li&gt;DevOps: Azure Pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building Vardhaman Bot was lot of fun, writing funny replies and stories was the best part. We wanted students to connect with our Bot and they really did!.&lt;/p&gt;

&lt;p&gt;We received lot of positive response from countless students and it really felt satisfying to contribute to something which affects so many people positively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feelings
&lt;/h2&gt;

&lt;p&gt;This team has received national recognition multiple times and I am very much proud of my mates.&lt;br&gt;
As of today &lt;a href="https://www.linkedin.com/in/tpothulapati/"&gt;Tarun&lt;/a&gt; is a Software Engineer at Buoyant IO, &lt;a href="https://www.linkedin.com/in/pravan"&gt;Pravan&lt;/a&gt; is a Data Engineering Intern at Amazon, &lt;a href="https://www.linkedin.com/in/khalidroshan23/"&gt;Khalid&lt;/a&gt; is soon going to start his role as an SDE at EPAM &amp;amp; &lt;a href="https://www.linkedin.com/in/shreyaasriv/"&gt;Shreya&lt;/a&gt; has been doing multiple internships at various companies.&lt;/p&gt;

&lt;p&gt;Coming to me, I got admits from multiple US Universities for MS in Computer Science but unfortunately COVID-19 has badly hindered my chances of starting my course this Fall.&lt;/p&gt;

&lt;p&gt;I am actively looking for new opportunities but the condition at the moment is very bad with companies not hiring and existing offers being revoked. &lt;/p&gt;

&lt;p&gt;माना की मुश्किल घड़ी है पर उम्मीद उससे बड़ी है!&lt;/p&gt;

</description>
      <category>2020devgrad</category>
      <category>octograd2020</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
