<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Replication on Digi Hunch</title><link>https://static.digihunch.com/tag/replication/</link><description>Recent content in Replication on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Thu, 17 Apr 2025 14:03:57 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/replication/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes Storage on Azure 2 of 3 – Portworx</title><link>https://static.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/</link><pubDate>Fri, 12 Aug 2022 15:23:00 -0400</pubDate><guid>https://static.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-k8s-azstorage.webp" alt="Featured image of post Kubernetes Storage on Azure 2 of 3 – Portworx" /&gt;&lt;p class="wp-block-paragraph"&gt;In the previous &lt;a href="https://static.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;post&lt;/a&gt;, we have discussed built-in storage classes on Azure Kubernetes. Further to that, we will examine some third-party software defined storage (SDS) options that are compatible with Azure Kubernetes Service in this post. Then we take Portworx on Azure as an example. Although, these options are specific to Azure, most of the players also have solutions for other managed Kubernetes platforms. Also, the methodology to study storage options remain the same regardless of cloud service provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In fact, I touched on software defined storage (SDS) in the context of general non-containerized workload in a separate &lt;a href="https://static.digihunch.com/2022/07/intro-to-ceph-storage/"&gt;post&lt;/a&gt;. In short, storage providers decouple the SDS appliance from the full storage solution in order to lower the cost and increase flexibility. To the storage consumer (e.g. a process running on Linux OS), SDS can present a block disk or file system. There are also SDS solution that can host your own object storage and we will discuss that later. In fact, SDS has gained significant popularity in recent years. For example, the report &amp;#8220;&lt;a href="https://www.architecting.it/product/brksw0140-ebook/"&gt;Validating Software-Defined Storage Operating Models for the Enterprise&lt;/a&gt;&amp;#8221; by &lt;em&gt;&lt;a href="https://www.architecting.it/"&gt;archiectingit&lt;/a&gt;&lt;/em&gt; divided the evolution into four phases and cited that Gartner predicts the SDS revolution to reach 50% of the storage market by 2024, from 15% in 2020.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post discusses SDS in the context of container storage. Then we will install Portworx on Azure Kubernetes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-sds-for-kubernetes"&gt;SDS for Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many SDS appliances also developed the capability to present storage volumes to containerized workload. I put together a list of SDS products that works on Azure with their supported access modes: &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Solution&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Licence&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Development and Support&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Access Mode&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://portworx.com/products/portworx-enterprise/features/"&gt;Portworx&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Closed source. Free Essential tier. Enterprise features on License. &lt;/td&gt;&lt;td&gt;Commercially supported by PureStorage&lt;/td&gt;&lt;td&gt;RWO, RWX&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.ceph.com/en/quincy/"&gt;Ceph&lt;/a&gt; by &lt;a href="https://rook.io/"&gt;Rook&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/rook/rook"&gt;Open source&lt;/a&gt;. Rook is a graduated &lt;a href="https://www.cncf.io/projects/rook/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Developed and commercially supported by &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_openshift_container_storage/4.8/html-single/red_hat_openshift_container_storage_architecture/index"&gt;Red Hat&lt;/a&gt;, &lt;a href="https://ubuntu.com/ceph"&gt;Canonical&lt;/a&gt; and &lt;a href="https://softiron.com/"&gt;SoftIron&lt;/a&gt;&lt;/td&gt;&lt;td&gt;RWO, ROX, RWX&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://openebs.io/"&gt;OpenEBS&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/openebs/openebs"&gt;Open source&lt;/a&gt;. Sandbox &lt;a href="https://www.cncf.io/projects/openebs/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Developed and &lt;a href="https://openebs.io/commercial-support"&gt;commercially supported&lt;/a&gt; by &lt;a href="https://openebs.io/"&gt;MayaData&lt;/a&gt; et al.&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://longhorn.io/"&gt;Longhorn&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://github.com/longhorn/longhorn"&gt;Open source&lt;/a&gt;. Incubating &lt;a href="https://www.cncf.io/projects/longhorn/"&gt;CNCF project&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Originally developed by &lt;a href="https://rancher.com/products/longhorn"&gt;Rancher&lt;/a&gt;, and commercially supported by &lt;a href="https://www.suse.com/products/longhorn/"&gt;SUSE&lt;/a&gt;&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://docs.ondat.io/docs/install/microsoft-azure-aks/"&gt;StorageOS&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Closed source. License required.&lt;/td&gt;&lt;td&gt;Commercial support by &lt;a href="https://www.ondat.io/"&gt;Ondat&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;RWO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="https://linbit.com/kubernetes/"&gt;LINBIT&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Open-source with enterprise plans&lt;/td&gt;&lt;td&gt;Enterprise support&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RWO is the most commonly supported mode. The report &amp;#8220;&lt;a href="https://www.architecting.it/product/performance-benchmarking-cloud-native-storage-solutions-for-kubernetes-ebook/"&gt;Performance Benchmarking Cloud Native Storage Solutions for Kubernetes&lt;/a&gt;&amp;#8221; makes a comparison of performance among some of the options in early 2021. Another potentially opinionated &lt;a href="https://linbit.com/cloud-native-sds-platform-comparsion/"&gt;comparison list&lt;/a&gt; is by LINBIT.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Portworx is a leading player with commercial SDS solution and I will test its free &lt;a href="https://portworx.com/blog/portworx-essentials-vs-portworx-enterprise/"&gt;Essential&lt;/a&gt; tier in the rest of this post. Ceph is one of the most mature leading open-source offering and I will test it in the next post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For Portworx, we can use the terraform template &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;cloudkube for Azure&lt;/a&gt;. The template assigns the kubelet&amp;#8217;s managed identity as contributor of the node resource group. The template also creates a bastion host with direct SSH access to the nodes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-portworx-operator-on-azure"&gt;Portworx Operator on Azure&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Portworx Operator to configure storage cluster. Portworx has an &lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install"&gt;instruction&lt;/a&gt; for AKS but it is not tailored to specific identity model. For simplicity, use my cloudkube &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;Terraform template&lt;/a&gt; to create the AKS cluster, and skip the &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/install-using-operator"&gt;Prepare Your AKS Platform&lt;/a&gt;&amp;#8221; page. Instead, follow the &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/azure-managed-identity-on-aks"&gt;Deploy Portworx using Azure managed identity on new AKS cluster&lt;/a&gt;&amp;#8221; page starting at step 7. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At that step, we need to create a secret with the client ID of the managed identity for node agent. The terraform template outputs the BYO identity&amp;#8217;s client ID. After cluster creation, we simply SSH to the bastion host and create the secret using the output.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1953" height="755" src="https://static.digihunch.com/wp-content/uploads/2022/06/image-15.png" alt="" class="wp-image-5810"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install Porworx using operator, we can follow a wizard in &lt;a href="https://central.portworx.com/"&gt;PX-central&lt;/a&gt;. If this is the first time, we need to create an account and log in to the portal. If this is not the first time and you have previously created a cluster, you need to detach that cluster by going to Profile from bottom left corner on the portal page. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Follow the guide in &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/install-using-operator"&gt;Install Portworx on AKS using the Operator&lt;/a&gt;&amp;#8220;. In the wizard, click on &amp;#8220;Portworx Essentials&amp;#8221; for free tier, or &amp;#8220;Portworx Enterprise&amp;#8221; for the 30-day trial. Then select operator with latest version. In the rest of the wizard steps, select options applicable to Azure environment. The last step will present two kubectl commands to install operator and install the CR. Run the command to install operator and verify result:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f &lt;span style="color:#e6db74"&gt;&amp;#39;https://install.portworx.com/2.9?comp=pxoperator&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system get deployment portworx-operator&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading"&gt;Portworx Custom Resource&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To install the CR, we need to customize the given manifest in order to use our managed identity. We can download the YAML manifest (portworx_essentials.yml) and modify it in text editor. As the page &amp;#8220;&lt;a href="https://docs.portworx.com/portworx-enterprise/platform/kubernetes/azure-aks/install/azure-managed-identity-on-aks"&gt;Deploy Portworx using Azure managed identity on new AKS cluster&lt;/a&gt;&amp;#8221; suggest at step 9: in the&amp;nbsp;env&amp;nbsp;section, remove the AZURE_CLIENT_SECRET and AZURE_TENANT_ID sections but keep the AZURE_CLIENT_ID section. My CRD declaration looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SOURCE&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;https&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//install.portworx.com/?operator=true&amp;amp;mc=false&amp;amp;kbver=&amp;amp;oem=esse&amp;amp;user=myuserid&amp;amp;b=true&amp;amp;kd=type%3DPremium_LRS%2Csize%3D150&amp;amp;s=%22type%3DPremium_LRS%2Csize%3D150%22&amp;amp;c=my-very-long-px-cluster-id&amp;amp;aks=true&amp;amp;stork=true&amp;amp;csi=true&amp;amp;mon=true&amp;amp;tel=false&amp;amp;st=k8s&amp;amp;promop=true&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageCluster&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;core&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;libopenstorage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;org&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;my&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;very&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;long&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;cluster&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kube&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;annotations&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;install&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;source&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://install.portworx.com/?operator=true&amp;amp;mc=false&amp;amp;kbver=&amp;amp;oem=esse&amp;amp;user=myuserid&amp;amp;b=true&amp;amp;kd=type%3DPremium_LRS%2Csize%3D150&amp;amp;s=%22type%3DPremium_LRS%2Csize%3D150%22&amp;amp;c=my-very-long-px-cluster-id&amp;amp;aks=true&amp;amp;stork=true&amp;amp;csi=true&amp;amp;mon=true&amp;amp;tel=false&amp;amp;st=k8s&amp;amp;promop=true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;is&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;aks&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;misc&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;--oem esse&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;oci&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;monitor&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2.10&lt;/span&gt;.&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;imagePullPolicy&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Always&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kvdb&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;internal&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cloudStorage&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deviceSpecs&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Premium_LRS&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kvdbDeviceSpec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Premium_LRS&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secretsProvider&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stork&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;webhook&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;autopilot&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;monitoring&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;prometheus&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;enabled&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exportMetrics&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;featureGates&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;CSI&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AZURE_CLIENT_ID&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;valueFrom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secretKeyRef&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;azure&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AZURE_CLIENT_ID&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Secret&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;essential&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kube&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;essen&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aaaabbbbccccddddmyverylongpxessenuserid&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;osb&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;endpoint&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ssssssyyyyyyyzzzzzzmyverylongpxosbendpoint&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this custom resource manifest, I specify to get the cluster to create disk from Azure, even though it has the capability of using an existing disk. Then apply the CRD manifest:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kubectl apply -f portworx_essentials.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This will take up to 10 minutes to create several related resources. There are several check points to ensure the cluster is created successfully. Here are some useful commands:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system get po &lt;span style="color:#75715e"&gt;# all Pods related to portworx should be ready and running, especially the portworx-api ones&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system get storagecluster &lt;span style="color:#75715e"&gt;# the status should report online&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PX_POD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl get pods -l name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;portworx -n kube-system -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# get the name of one of the portworx Pod for storage cluster&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n kube-system exec -c portworx -it $PX_POD --tty --stdin -- /opt/pwx/bin/pxctl status &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# should report &amp;#34;Status: PX is operational&amp;#34; with valid license loaded&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get sc &lt;span style="color:#75715e"&gt;# portworx related storage classes are available&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The pre-built storage classes are not CSI based. However, CSI should be automatically enabled in recent operator versions. We should create our own &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/volumelifecycle/"&gt;CSI storage classes&lt;/a&gt; and &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/create-pvcs/dynamic-provisioning/"&gt;PVCs&lt;/a&gt; using our own storage classes. Here are two examples:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageClass&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;csi&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;provisioner&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pxd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;parameters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;priority_io&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;high&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;io_profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;db&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StorageClass&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;storage&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;k8s&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;px&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;csi&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;artifact&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;provisioner&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pxd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;portworx&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;parameters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;priority_io&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;medium&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;io_profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sequential&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;That is a bare minimum Portworx installation. Check out their &lt;a href="https://docs.portworx.com/portworx-install-with-kubernetes/"&gt;documentation&lt;/a&gt; for the full features. With this minimum install we can go to the section &amp;#8220;Validate Persistent Storage&amp;#8221; from the previous post to validate the persistent volume.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For troubleshooting purpose, &lt;a href="https://docs.portworx.com/reference/cli/basics/"&gt;pxctl&lt;/a&gt; is the utility and it is available on Portworx Pods.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Performance&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We care not only the functionality, but also the performance. So I ran a quick performance test using kube-str, using all default configuration. The result is as follows:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;px-csi-database&lt;/td&gt;&lt;td&gt;IOPS=969.614136 BW(KiB/s)=3894&lt;/td&gt;&lt;td&gt;IOPS=729.698059 BW(KiB/s)=2935&lt;/td&gt;&lt;td&gt;IOPS=1172.772827 BW(KiB/s)=150639&lt;/td&gt;&lt;td&gt;IOPS=691.626526 BW(KiB/s)=89053&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;px-csi-artifact&lt;/td&gt;&lt;td&gt;IOPS=780.681946 BW(KiB/s)=3139&lt;/td&gt;&lt;td&gt;IOPS=682.522766 BW(KiB/s)=2746&lt;/td&gt;&lt;td&gt;IOPS=773.548584 BW(KiB/s)=99549&lt;/td&gt;&lt;td&gt;IOPS=659.015320 BW(KiB/s)=84890&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that those numbers reflect performance under default configuration, and they should not be considered as the best performance that Portworx can deliver on Azure Kubernetes. Before moving to production, it is important to establish your own test parameters that best represents the container workload, and then iterate through different parameters for the storage class based on the requirement and performance output.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/08/storage-solution-on-aks-2-of-3-ceph-by-rook/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Storage on Azure 3 of 3 – Ceph by Rook&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Zookeeper Summary</title><link>https://static.digihunch.com/2020/08/zookeeper/</link><pubDate>Wed, 26 Aug 2020 23:10:00 -0400</pubDate><guid>https://static.digihunch.com/2020/08/zookeeper/</guid><description>&lt;h3 class="wp-block-heading" id="h-distributed-systems"&gt;Distributed systems&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Distributed system involves &lt;span style="text-decoration: underline;"&gt;independent computing entities&lt;/span&gt; linked together by network. The components &lt;span style="text-decoration: underline;"&gt;communicate and coordinate&lt;/span&gt; with each other to achieve a &lt;span style="text-decoration: underline;"&gt;common goal&lt;/span&gt;. In early days, designers and developers often had made some assumptions (aka. fallacies) of distributed computing:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The network is reliable&lt;/li&gt;&#10;&lt;li&gt;Latency is zero&lt;/li&gt;&#10;&lt;li&gt;Bandwidth is infinite&lt;/li&gt;&#10;&lt;li&gt;Network is secure&lt;/li&gt;&#10;&lt;li&gt;Topology doesn&amp;#8217;t change: in reality, components to a network get removed/added over time. the system should tolerate such changes.&lt;/li&gt;&#10;&lt;li&gt;There is one administrator: for distributed systems to function, they interact with external system beyond administrative control.&lt;/li&gt;&#10;&lt;li&gt;Transport cost is zero:&amp;nbsp; cost is involved everywhere, in the form of CPU cycles spent, to actual dollars paid to service provider.&lt;/li&gt;&#10;&lt;li&gt;Network is homogenous&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These fallacies make coordinating distributed computing entities a huge challenge and Zookeeper is introduced to address these challenges. Zookeeper implements common tasks for distributed coordination, such as:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Configuration Management (propagate configuration changes to all worker nodes dynamically)&lt;/li&gt;&#10;&lt;li&gt;Naming service&amp;nbsp;&lt;/li&gt;&#10;&lt;li&gt;Distributed synchronization (locks and barriers)&lt;/li&gt;&#10;&lt;li&gt;Cluster membership operations (e.g. detection of node leave/join)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper is a centralized coordination service for the distributed application. ZooKeeper itself is distributed as well. It runs on its own cluster of servers called a ZooKeeper ensemble, separate from application&amp;#8217;s cluster. Distributed consensus, group management, presence protocols, and leader election are implemented by the service so that the application developers do not need to reinvent the wheel by implementing them on their own.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkservice.jpg" alt="ZooKeeper Service"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Developers will have to use APIs through ZooKeeper&amp;#8217;s client library, which has language bindings for almost all popular programming languages. The client library is responsible for the interactions of an application with the ZooKeeper service. For testing with API access one can alternatively use its Java-based command-line shell (zkCli.sh)&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ zkCli.sh -server zknode:2181&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-how-zookeeper-works"&gt;How Zookeeper works&lt;/h3&gt;&#10;&lt;h4 class="wp-block-heading" id="h-data-model"&gt;Data Model&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper allows distributed process to coordinate with each other through a shared hierarchical namespace of data registers (znodes). The hierarchy start with root node which has child znode(s). Each znode can have their children, as well as store its own data (hence the name data register). The data in a znode is stored in byte format for a maximum of 1MB (ZooKeeper by design is just a coordinator service of host application, so its own data set size is fairly small).&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="360" height="368" src="https://static.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg" alt="" class="wp-image-7753" srcset="https://static.digihunch.com/wp-content/uploads/2023/01/zkdm.jpeg 360w, https://static.digihunch.com/wp-content/uploads/2023/01/zkdm-293x300.jpeg 293w" sizes="auto, (max-width: 360px) 100vw, 360px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Zookeeper data model&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Znodes have two types (set at time of creation) &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;persistent znode: for storing persistent data, such as configuration. The znodes and their data will exist even if the creator client dies.&lt;/li&gt;&#10;&lt;li&gt;ephemeral znode: deleted by ZooKeeper service when the creating client&amp;#8217;s session ends (due to disconnection or explicit termination). It can also be explicitly deleted by creator client through delete API call. They cannot have children. Their visibility is controlled by ACL policy&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper can assign an incremental sequence number as part of znode name during its creation. This makes a sequential node. Both persistent znode and ephemeral znode can be either sequential or not.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In typical client-server architecture, server is passively open and do not initiate communication to client. Client pulls information from server. This is however an anti-pattern for large scale distributed system. ZooKeeper implements a Watch mechanism where clients can get notifications from ZooKeeper service, instead of having to poll for events. Clients can register with the ZooKeeper service (by setting a watch on znode) for any changes associated with a znode. A watch will only trigger notification once, and needs to be re-registered (by client) for trigger the next notification. A watch is triggered upon:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Any changes to the data of a znode;&lt;/li&gt;&#10;&lt;li&gt;any changes to the children of a znode;&lt;/li&gt;&#10;&lt;li&gt;Creation of deletion of a znode&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper guarantees that notifications are delivered in the order of event occurrence. When a client disconnects from ZooKeeper server, it doesn&amp;#8217;t receive any watches until the connection is re-established. &lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-api-operations"&gt;API Operations&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper operations are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;create&lt;/td&gt;&lt;td&gt;Creates a znode in the specified path&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;delete&lt;/td&gt;&lt;td&gt;Deletes a znodes from the specified path. Not allowed if the znode has children. version number required&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;exists&lt;/td&gt;&lt;td&gt;Check if a znode at the specified path exists, and get version number; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getChildren&lt;/td&gt;&lt;td&gt;Get a list of children of a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getData&lt;/td&gt;&lt;td&gt;get the data associated with a znode; support watch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setData&lt;/td&gt;&lt;td&gt;writes data into the data field of a znode. Version number required.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;getACL&lt;/td&gt;&lt;td&gt;get the ACL of a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;setACL&lt;/td&gt;&lt;td&gt;set the ACL in a znode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sync&lt;/td&gt;&lt;td&gt;synchronizes a client&amp;#8217;s view of a znode &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The write operations (setData, create, delete) are atomic, durable and eventually consistent. Every znode has a stat structure including cZxid, mZxid an dpZxid that keeps track of the ID of the transactions that created, last modified this znode, or pertains to adding or removing its children.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Production znode ensemble with more than one node is running in quorum mode. Updates to ZooKeeper tree by clients must be persistently stored in this quorum of nodes for a transaction to be completed successfully. Odd number of node is recommended to avoid split-brain where network partition causes two subsets of servers in the ensemble function independently, and different clients get different results for the same requests, depending upon the server they are connected to.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All ZooKeeper nodes are listed in the configuration for client application to randomly pick from and try to connect and establish a session. The session is associated with every operation the client executes in a ZooKeeper service. The session also has a timeout period specified by the application client during session establishment. If the connection remains idle for more than the timeout period, the server expires the session. Appropriate session timeout should be set based on network condition. Sessions are kept alive by client sending heartbeat to ZooKeeper service. Application developer needs to handle connection-loss scenarios properly.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-leader-election-and-atomic-broadcast"&gt;Leader Election and Atomic Broadcast&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper ensemble contains a leader nodes, follower nodes and observer nodes.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The leader node is elected by the cluster. It handles all write requests. &lt;/li&gt;&#10;&lt;li&gt;The follower nodes are leader candidates that are not elected. They are backup to the leader nodes. They handle read request, and receive the updates proposed by the leader, and through a majority consensus mechanism, a consistent state is maintained across the ensemble. &lt;/li&gt;&#10;&lt;li&gt;The observer nodes are ineligible as leader candidates. They have otherwise the same function as followers.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The service relies on the replication mechanism to ensure that all updates are persistent in all servers that constitute the ensemble. This is the core mechanism in ZooKeeper, implemented as a special atomic messaging protocol called ZooKeeper Atomic Broadcast (ZAB). ZAB (a variant of Paxos algorithm) ensures the election of new leader in the event of old leader crash, and ensures integrity of data. It defines three states (looking, following and leading) of a node, and goes through four phases (election, discovery, sync, broadcast) in its operation.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All read requests (exists, getData, getChildren) are process locally by the ZooKeeper node where the client is connected to. This makes read operation fast. All write requests (create, delete, and setData) are forwarded to the leader in the ensemble, which carries out the client request as a transaction. A transaction is identified by zxid and is idempotent. Transaction also satisfies the property of isolation (no transaction is interfered with by any other transaction). Only after a majority of the followers acknowledge that they have persisted the change does the leader commit the update.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.6.1/images/zkcomponents.jpg" alt="ZooKeeper Components"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Transaction processing involves two steps in ZooKeeper: leader election and atomic broadcast. This resembles a two-phase commit protocol (which also includes a leader election and an atomic broadcast)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;ZooKeeper use local storage to persist transactions. The transactions are logged to transaction logs, in sync&amp;#8217;ed write, requiring a dedicated block device separated from boot device of server. The local storage also keep point-in-time copies (snapshots) of the ZooKeeper tree.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-recipes"&gt;ZooKeeper Recipes&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The ZooKeeper recipes defines high-level implementation (construct) of some common distributed coordination mechanism:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Barrier_(computer_science)"&gt;Barrier&lt;/a&gt;: any thread/process must stop at this point and cannot proceed until all other threads/processes reach this barrier.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://computersciencewiki.org/index.php/Queue"&gt;Queue&lt;/a&gt;: allow FIFO in distributed system&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Lock_(computer_science)"&gt;Lock&lt;/a&gt;: Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Leader_election"&gt;Leader Election&lt;/a&gt;: designate a single process as the organizer of some task distributed among several nodes.&lt;/li&gt;&#10;&lt;li&gt;Group membership: node may join or leave a group, which needs to be made available to clients. An alternative to ZooKeeper to manage group membership is &lt;a href="https://en.wikipedia.org/wiki/Gossip_protocol"&gt;gossip protocol&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/"&gt;Service discovery&lt;/a&gt;: help client to determine IP and port for a service that are hosted by multiple servers.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol"&gt;Two-phase commit&lt;/a&gt;: a mechanism for atomic commitment in two steps: first a commit request phase involving a voting by participants; and second, either a commit action, or an abort action, based on the voting result.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-zookeeper-administration"&gt;Zookeeper Administration&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The official &lt;a href="https://zookeeper.apache.org/doc/r3.6.1/zookeeperAdmin.html"&gt;documentation&lt;/a&gt; includes all we need to know about administration. In addition, we need to configure &lt;a href="https://logging.apache.org/log4j/1.2/manual.html"&gt;log4j&lt;/a&gt; for proper logging. As best practices, we also should turn off &lt;a href="https://static.digihunch.com/2018/04/centos-remove-swap-safely/"&gt;swapping&lt;/a&gt; on ZooKeeper. We should clean up the data directory periodically if auto purge is not enabled. For optimal performance, ZooKeeper transaction log should be configured in a dedicated device.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For monitoring, ZooKeeper responds to a small sets of four-letter commands issued through telnet or nc to server&amp;#8217;s client port. This allows the admin to check health of server or diagnose any problems. This requires the following property in zoo keeper config:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4lw.commands.whitelist=stat, ruok, conf, isro, wchc&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The value can be set to asterick to allow all four-letter keyword. Once enabled, we can check server status&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo ruok | nc localhost &lt;span style="color:#ae81ff"&gt;2181&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;imok&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;More four-letter commands are listed &lt;a href="https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#sc_zkCommands"&gt;here&lt;/a&gt;. Apart from the four-letter commands, ZooKeeper can also be managed through Java Management Extensions (&lt;a href="https://www.oracle.com/java/technologies/javase/javamanagement.html"&gt;JMX&lt;/a&gt;).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apache ZooKeeper is a coordination service for distributed application. It has become the solution for high availability for many other projects. Some of Apache&amp;#8217;s well known open-source distributed services include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Apache Hadoop (an umbrella of projects including many components for BigData processing such as Hadoop Common, Hadoop Distributed File System (HDFS), Hadoop YARN (yet another resource negotiator) and Hadoop MapReduce)&lt;/li&gt;&#10;&lt;li&gt;Apache HBase: non-relational database on top of HDFS&lt;/li&gt;&#10;&lt;li&gt;Apache Hive: data warehouse with SQL-like interface&lt;/li&gt;&#10;&lt;li&gt;Apache Kafka: stream processing&lt;/li&gt;&#10;&lt;li&gt;Apache Nifi: automated data flow processing. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some of them, such as Nifi, has an embedded implementation of ZooKeeper ensemble if there isn&amp;#8217;t a separate ensemble. There is some limitation with embedded Zookeeper ensemble. First, we cannot start ZooKeeper without starting Nifi service on the same server. Second, we need to orchestrate the configuration so that the ZooKeeper ensemble does not grow too large. We need to keep in mind that the ZooKeeper ensemble is a separate cluster of its own, and the it is not recommended to have more than 7 nodes on ZooKeeper.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/08/virtualization-4-of-4-networking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 4 of 4 – Networking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/09/host-legacy-application-with-docker-compose/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Host legacy application in Docker 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Kafka high-level Overview</title><link>https://static.digihunch.com/2020/07/zookeeper-and-kafka-overview/</link><pubDate>Tue, 21 Jul 2020 23:19:00 -0400</pubDate><guid>https://static.digihunch.com/2020/07/zookeeper-and-kafka-overview/</guid><description>&lt;h3 class="wp-block-heading" id="h-zookeeper"&gt;Zookeeper&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;General definition of distributed system: a software system that is composed of &lt;strong&gt;independent &lt;/strong&gt;computing entities linked &lt;strong&gt;together &lt;/strong&gt;by a computer network whose components communicate and coordinate with each other to achieve a common computational goal. Implementing coordination among components of a distributed system is hard. For example, designated master node becomes single point of failure; cluster needs to detect availability of new nodes as it joins cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Zookeeper is designed to &lt;strong&gt;simplify cluster coordination&lt;/strong&gt;. Zookeeper implements key aspects in cluster coordination, such as distributed consensus, group management, presence protocols and leader election. In order to coordinate a cluster, zookeeper itself also runs in its own cluster, called &lt;strong&gt;ensemble&lt;/strong&gt;. Zookeeper exposes a simple but powerful interface of primitives. Applications can be designed on these primitives implemented through ZooKeeper APIs to solve the problems of distributed synchronization, cluster configuration management, group membership, etc.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://zookeeper.apache.org/doc/r3.4.6/images/zkservice.jpg" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;Zookeeper Ensemble&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Clients can connect to a Zookeeper service by connecting to any member of the ensemble. The members of the ensemble are aware of each other&amp;#8217;s state. As long as a majority of the nodes are available, the service will be available. &lt;strong&gt;Zookeeper cli (zkCli.sh)&lt;/strong&gt; can be used to connect to Zookeeper server. they can be downloaded from &lt;a href="https://zookeeper.apache.org/releases.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Zookeeper is integrated with many other services apart from Kafka, such as Nifi and Hadoop.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-kafka"&gt;&lt;strong&gt;Kafka&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://kafka.apache.org/"&gt;Kafka &lt;/a&gt;is a messaging system that is horizontally scalable, fault tolerant. It can also serve as queue storage system and stream processing system. It is distributed and use Zookeeper for cluster coordination. Each node is called a broker.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://kafka.apache.org/25/images/log_anatomy.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Topics &lt;/strong&gt;in Kafka (think of table in database) is a category or feed name to which messages (records) are published. Topic is broken up into ordered commit logs called partitions. Each partition has an ID. Each message in a partition is assigned an offset. Topics that are created in Kafka are distributed across brokers based on the partition, replication, and other factors. Each partition is replicated across several brokers depending on replication factor. For each partition, Kafka elect one replica as the leader of partition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Writes to a partition is generally sequential. Reading messages can either be from the beginning, or rewind or skip to any port in partition given an offset value. Data in a topic is retained for a configurable period of time. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;message &lt;/strong&gt;is a unit of data in Kafka, in the format of key-value pair. A key is used to control the message that is to be written to partitions. Messages with the same keys are always written to the same partition (hash map)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;producer &lt;/strong&gt;publishes new message to a topic. Producers do not care which partition the message is written to and will balance messages over every partition of a topic evenly. Directing messages to a partition is done using the message key and a partitioner, this will generate a hash of the key and map it to a partition.&lt;/p&gt;&#10;&lt;figure class="wp-block-image is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://kafka.apache.org/25/images/log_consumer.png" alt="" width="370" height="225"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A &lt;strong&gt;consumer &lt;/strong&gt;is subscribed to one or more topics and read messages sequentially. The consumer keeps track of messages it has consumed by keeping track on the offset of the message. The offset is a bit of metadata (an integer value that continually increases) that kafka adds to each message. Each partition has a unique offset which is stored with the offset of the last consumed message. A consumer can stop and start without losing its current state.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Kafka &lt;strong&gt;broker &lt;/strong&gt;is designed to operate as part of a cluster. One broker in the cluster also function as the cluster&amp;#8217;s controller, which is responsible for administrative operations such as: assigning partitions to brokers; monitoring for broker failures in cluster. A particular partition is owned by a broker and that broker is called the leader of the partition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All consumers and producers operating on that partition must connect to the leader.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kafka cluster may replicate across cluster using MirrorMaker.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Reference: &lt;strong&gt;Kafka: The Definitive Guide: Real-Time Data and Stream Processing at Scale&lt;/strong&gt;&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2023/01/kafka-780x1024.jpeg" alt="" class="wp-image-7913" width="207" height="272" srcset="https://static.digihunch.com/wp-content/uploads/2023/01/kafka-780x1024.jpeg 780w, https://static.digihunch.com/wp-content/uploads/2023/01/kafka-229x300.jpeg 229w, https://static.digihunch.com/wp-content/uploads/2023/01/kafka-768x1008.jpeg 768w, https://static.digihunch.com/wp-content/uploads/2023/01/kafka.jpeg 1036w" sizes="auto, (max-width: 207px) 100vw, 207px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt; &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/07/nfs-network-file-system-and-rpc-remote-procedure-call/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How RPC and NFS work&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/07/overview-of-virtualization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Virtualization 1 of 4 – Hypervisor&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>