<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Retain Policy on Digi Hunch</title><link>https://static.digihunch.com/tag/retain-policy/</link><description>Recent content in Retain Policy on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 02 Apr 2025 13:58:29 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/retain-policy/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS</title><link>https://static.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/</link><pubDate>Sun, 31 Jul 2022 15:22:00 -0400</pubDate><guid>https://static.digihunch.com/2022/07/kubernetes-storage-on-azure-1-of-3-built-in-storage-and-nfs/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-aks-storage-1.webp" alt="Featured image of post Kubernetes Storage on Azure 1 of 3 – built-in storage and NFS" /&gt;&lt;p class="wp-block-paragraph"&gt;In the previous &lt;a href="https://static.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;post&lt;/a&gt;, we understand that to host stateful workload, we need to manage persistent storage to the Kubernetes platform. In this post, I will explore the different storage options. These options are specific to Azure Kubernetes service. However, the principals apply to any Kubernetes platform regardless of cloud vendor.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In another old &lt;a href="https://static.digihunch.com/2021/06/kubernetes-storage-explained/"&gt;post&lt;/a&gt;, I discussed in-tree and CSI storage classes, and from a developer&amp;#8217;s perspective, how to mount volumes statically and dynamically once the storage class is available. Here in this post we are concerned with how to make storage classes available, from a platform specialist perspective.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Storage in Azure Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As soon as the Azure Kubernetes cluster is launched, a number of built-in &lt;a href="https://docs.microsoft.com/en-us/azure/aks/concepts-storage#storage-classes"&gt;storage classes&lt;/a&gt; are available. Unlike third-party storage classes, they do not require kubelet identity to be contributor for node resource group. As &lt;a href="https://static.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;discussed&lt;/a&gt; we shall use CSI based storage classes. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;managed-csi&lt;/li&gt;&#10;&lt;li&gt;managed-csi-premium&lt;/li&gt;&#10;&lt;li&gt;azurefile-csi&lt;/li&gt;&#10;&lt;li&gt;azurefile-csi-premium&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The main difference between them is the backing technology. However, form Kubernetes workload&amp;#8217;s perspective, the Pods as storage consumers are concerned with the &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes"&gt;access mode&lt;/a&gt; instead of backing technology. Here are supported access modes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ReadWriteOnce: read-write by a single node&lt;/li&gt;&#10;&lt;li&gt;ReadOnlyMany: read only by many nodes&lt;/li&gt;&#10;&lt;li&gt;ReadWriteMany: read-write by many nodes&lt;/li&gt;&#10;&lt;li&gt;ReadWriteOncePod: new in Kubernetes 1.22 to restrict volume access to a single Pod&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The storage classes managed-csi and managed-csi-premium support ReadWriteOnce. The storage classes azurefile-csi and azurefile-csi-premium support ReadWriteMany.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from these built-in options, Azure also suggests a few more options based on other types of Azure resources. For example, AKS can integrate with &lt;a href="https://azure.microsoft.com/en-us/services/hpc-cache/#overview"&gt;HPC cache&lt;/a&gt; and it for HPC. We can also self-manage a virtual machine &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-nfs-volume"&gt;configured as NFS server&lt;/a&gt;, and use the &lt;a href="https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner"&gt;NFS subdir external provisioner&lt;/a&gt; to configure storage class. Despite of the overhead with managing a VM, you have more configurability. My previous client reports that they gain better performance than the built-in options. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another alternative is &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-netapp-files"&gt;Azure NetApp Files&lt;/a&gt;. However, being a full enterprise grade solution (similar to &lt;a href="https://static.digihunch.com/2022/05/fsx-ontap-enterprise-storage-on-aws/"&gt;FSx ONTAP&lt;/a&gt;), &lt;a href="https://azure.microsoft.com/en-ca/services/netapp/"&gt;Azure NetApp Files&lt;/a&gt; costs an arm and a leg. In &lt;a href="https://www.kasten.io/kubernetes/resources/blog/aks-and-storage-performance-differences-among-kubernetes-storage-services"&gt;this comparison&lt;/a&gt;, it cost 60 to 100 times as the cost by built-in options.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another option is &lt;a href="https://docs.microsoft.com/en-us/azure/aks/use-ultra-disks"&gt;Azure Ultra Disk&lt;/a&gt;, which needs to be enabled at cluster level. You can provision performance target (DiskIOPSReadWrite and DiskMBpsReadWrite) in the storage class. Ultra Disk is a good middle ground between the pricey NetApp files and the less performant built-in options.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-terraform-template-for-aks"&gt;Terraform Template for AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To explore the storage options, I use my own terraform template to create an AKS cluster. The template is in the &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;azure&lt;/a&gt; directory of the &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; repo. The template consists a few configurations with Azure Kubernetes to facilitate storage configuration. First, it configures an SSH key pair to use between the bastion host and the node. Users can SSH to Kubernetes nodes from bastion host as soon as terraform apply is completed. Second, the third party storage options installed after the cluster creation need their Pod to instruct Azure to create Azure disks. This requires that a Kubernetes node agent have the permission to provision resources in the node resource group. This is important to understand because there are a couple of managed identities at play (refer to &lt;a href="https://static.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;this&lt;/a&gt; post) when building an AKS cluster. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Azure Kubernetes, it is the managed identity of kubelet, that needs to have contributor permission over the resource group for the nodes (not the one for the AKS cluster itself). A managed identity is expressed by a client ID, an object ID (aka principal ID), and the identity ID. We can find them out with an AZ CLI command as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1899" height="207" src="https://static.digihunch.com/wp-content/uploads/2022/06/image-14.png" alt="" class="wp-image-5653"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can also tell that the kubelet managed identity represents node agent, by connecting to a node and looking at the argument (kubernetes.azure.com/kubelet-identity-client-id) of kubelet process:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1794" height="784" src="https://static.digihunch.com/wp-content/uploads/2022/06/image-13.png" alt="" class="wp-image-5650"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the template, I also chose to designate the same BYO identity for both the cluster and for kubelet (node agent), in order to minimize my requirement on permission. If I had left it with a system assigned identity for node agent, I would have to assign that identity as a contributor for the node resource group, either as a user, or via Terraform&amp;#8217;s identity. Either way, it is beyond what a &lt;a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-steps"&gt;Contributor&lt;/a&gt; is allowed to do.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Benchmarking with kubestr&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I used &lt;a href="https://fio.readthedocs.io/en/latest/index.html"&gt;fio&lt;/a&gt; utility for storage benchmarking from virtual machines. However, fio utility is not for container. For &lt;a href="https://docs.portworx.com/install-with-other/operate-and-maintain/performance-and-tuning/fio/"&gt;fio testing&lt;/a&gt; on Kubernetes, I&amp;#8217;d have to use a Docker image, and test with target volume attached. Fortunately, the &lt;a href="https://www.kasten.io/"&gt;Kasten&lt;/a&gt; team shared their initiative in the open source project &lt;a href="https://kubestr.io/"&gt;Kubestr&lt;/a&gt;. The kubestr &lt;a href="https://github.com/kastenhq/kubestr/releases/tag/v0.4.31"&gt;release&lt;/a&gt; is available as an executable on common platforms. It connects to the cluster the same way as kubectl and &lt;a href="https://www.youtube.com/watch?v=j9UkQM-oa1k"&gt;here&lt;/a&gt; is a demo. To begin with, download the utility to bastion host, and run it without any argument, which prints the storage classes and &lt;a href="https://kubernetes.io/docs/concepts/storage/volume-snapshot-classes/"&gt;volume snapshot classes&lt;/a&gt;:&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;curl -L -o kubestr.tar.gz https://github.com/kastenhq/kubestr/releases/download/v0.4.31/kubestr_0.4.31_Linux_amd64.tar.gz&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tar -xvf kubestr.tar.gz &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm kubestr.tar.gz &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; chmod +x kubestr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./kubestr &lt;span style="color:#75715e"&gt;# if kubectl is configured, this command will print out the details of storage classes and volume snapshot classes&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 addition to outputting details, it is also very simple to perform storage benchmarking with kubestr. All we need to do is giving it the storage class name and it will run four tests by default with common global options (ioengine=libaio verify=0 direct=1 gtod_reduce=1). The four tests are:&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;strong&gt;JobName&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;block_s&lt;/strong&gt;ize&lt;/td&gt;&lt;td&gt;&lt;strong&gt;filesize&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;iodepth&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;rw&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;read_iops&lt;/td&gt;&lt;td&gt;4k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randread&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;write_iops&lt;/td&gt;&lt;td&gt;4k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randwrite&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;read_bw&lt;/td&gt;&lt;td&gt;128k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randread&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;write_bw&lt;/td&gt;&lt;td&gt;128k&lt;/td&gt;&lt;td&gt;2G&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;randwrite&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During each test, it measures and reports IOPS and bandwidth (throughput). If your I/O profile falls out of the four jobs, you can even customize your test by supplying a fio config file. For example, you need a longer test duration, or you need a larger total size for the test. Before the test, kubestr automatically mount their test volumes using the storage class being tested.&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;./kubestr fio -s my-storage-class &lt;span style="color:#75715e"&gt;# benchmarking a storage class&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;For read_iops and write_iops, we mainly look at the IOPS. For read_bw and write_bw, we mainly look at the bandwidth. The iops and bw &lt;a href="https://fio.readthedocs.io/en/latest/fio_doc.html#interpreting-the-output"&gt;based on samples&lt;/a&gt; are reported as first line of result, followed by min, max and average.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Metrics&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With kubestr I ran a performance test amongst the native storage classes with Azure Kubernetes Service, with results as below:&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;managed-csi&lt;/td&gt;&lt;td&gt;IOPS=314.729797 BW(KiB/s)=1275&lt;/td&gt;&lt;td&gt;IOPS=297.071136 BW(KiB/s)=1204&lt;/td&gt;&lt;td&gt;IOPS=315.311188 BW(KiB/s)=40887&lt;/td&gt;&lt;td&gt;IOPS=261.048645 BW(KiB/s)=33941&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;managed-csi-premium&lt;/td&gt;&lt;td&gt;IOPS=493.395844 BW(KiB/s)=1990&lt;/td&gt;&lt;td&gt;IOPS=426.731812 BW(KiB/s)=1723&lt;/td&gt;&lt;td&gt;IOPS=455.950348 BW(KiB/s)=58894&lt;/td&gt;&lt;td&gt;IOPS=422.888855 BW(KiB/s)=54662&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;azurefile-csi&lt;/td&gt;&lt;td&gt;IOPS=259.333282 BW(KiB/s)=1053&lt;/td&gt;&lt;td&gt;IOPS=283.985779 BW(KiB/s)=1152&lt;/td&gt;&lt;td&gt;IOPS=240.447403 BW(KiB/s)=31298&lt;/td&gt;&lt;td&gt;IOPS=230.689804 BW(KiB/s)=30048&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;azurefile-csi-premium&lt;/td&gt;&lt;td&gt;IOPS=394.044739 BW(KiB/s)=1586&lt;/td&gt;&lt;td&gt;IOPS=371.181793 BW(KiB/s)=1494&lt;/td&gt;&lt;td&gt;IOPS=380.360535 BW(KiB/s)=49018&lt;/td&gt;&lt;td&gt;IOPS=491.313446 BW(KiB/s)=63310&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These numbers are based on default test parameters (e.g. 100Gi PVC size). The numbers indicate that block storage generally performs better in default setting. So we should use managed disk instead of azure file unless multiple pods needs to access the same volume.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Validate Persistent Storage&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use mysql as a quick and dirty test of storage classes. We can deploy the following 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-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&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:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PersistentVolumeClaim&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;metadata&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-pvc&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;spec&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;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;managed-csi-premium&lt;/span&gt; &lt;span style="color:#75715e"&gt;# the storage class being tested&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;accessModes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&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;resources&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;requests&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;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5Gi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&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;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&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:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&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:#f92672"&gt;metadata&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;data&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;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;eHl6 &lt;/span&gt; &lt;span style="color:#75715e"&gt;# base64 code of xyz&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&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;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;apps/v1&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;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deployment&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;metadata&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;labels&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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;spec&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;replicas&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&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;selector&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;matchLabels&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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;template&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;metadata&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;labels&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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;spec&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;containers&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;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql:5.6&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;env&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;MYSQL_ROOT_PASSWORD&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;valueFrom&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;secretKeyRef&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;password&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;ports&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;containerPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3306&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;volumeMounts&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-persistent-storage&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;mountPath&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/var/lib/mysql&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;volumes&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-persistent-storage&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;persistentVolumeClaim&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;claimName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-pvc&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&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;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&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:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Service&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;metadata&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql-service&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;labels&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;service&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;spec&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;selector&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;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mysql&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;ports&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;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tcp-mysql&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;protocol&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TCP&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;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3306&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;targetPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3306&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;Once the Pod has been created, then we use a throw-away Pod to connect to mysql service and build some data:&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 run mysql-cli --rm -i --tty --image imega/mysql-client -- /bin/sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;If you don&lt;span style="color:#e6db74"&gt;&amp;#39;t see a command prompt, try pressing enter.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;/ # mysql --host=mysql-service.default.svc.cluster.local --user=root --password=xyz&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 9 Server version: 5.6.51 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type &amp;#39;&lt;/span&gt;help;&lt;span style="color:#e6db74"&gt;&amp;#39; or &amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\h&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; for help. Type &amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\c&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt; to clear the current input statement. MySQL &lt;span style="color:#f92672"&gt;[(&lt;/span&gt;none&lt;span style="color:#f92672"&gt;)]&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From the mysql shell, we create a test database with dummy data populated:&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-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DATABASE&lt;/span&gt; &lt;span style="color:#f92672"&gt;`&lt;/span&gt;testdb&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;USE testdb; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; tasks ( &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task_id INT AUTO_INCREMENT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title VARCHAR(&lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description TEXT, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; created_at &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CURRENT_TIMESTAMP&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) ENGINE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;INNODB; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job A&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Morning Standup&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job B&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Latte with two shots of espresso&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job C&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;Coding coding and coding&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INTO&lt;/span&gt; tasks (title, description) &lt;span style="color:#66d9ef"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;Job D&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;git commit&amp;#39;&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;Each SQL command should return with number of rows affected and then we can exit the MySQL shell and Pod shell. Once we exit out of the Pod shell, the Pod is deleted. We can re-connect to validate the data are still present:&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 run mysql-tester --rm -i --tty --image imega/mysql-client -- mysql --host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mysql-service.default.svc.cluster.local --user&lt;span style="color:#f92672"&gt;=&lt;/span&gt;root --password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;xyz --database testdb --execute&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT * FROM tasks;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;If you don&lt;span style="color:#e6db74"&gt;&amp;#39;t see a command prompt, try pressing enter.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Got error: Access denied for user &amp;#39;&lt;/span&gt;root&lt;span style="color:#e6db74"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;147.206.3.15&lt;span style="color:#e6db74"&gt;&amp;#39; (using password: NO)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| task_id | title | description | created_at |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 1 | Job A | Morning Standup | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 2 | Job B | Latte with two shots of espresso | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 3 | Job C | Coding coding and coding | 2022-06-22 20:29:12 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;| 4 | Job D | git commit | 2022-06-22 20:29:13 |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;+---------+-------+----------------------------------+---------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Session ended, resume using &amp;#39;&lt;/span&gt;kubectl attach mysql-tester -c mysql-tester -i -t&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt; command when the pod is running&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pod &lt;span style="color:#e6db74"&gt;&amp;#34;mysql-tester&amp;#34;&lt;/span&gt; deleted&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output validates the persistent storage of the data. When testing a different storage class, simply start over with a different storage class specified in the PVC part of the manifest.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This post focuses on storage options for persistent volumes on Azure Kubernetes service. I use my own Terraform template with some custom configuration. I also covered kubestr as benchmarking tool and ran it against the built-in storage classes. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some situations, the built-in options do not suit your needs. For example, you might want your application to use persistent volume in a consistent way across multiple cloud vendors. We will have to resort to third-party software defined storage layer. In the next post, I will explore a couple of SDS-based options, namely Portworx and Ceph. The custom configurations in the Terraform template will be helpful when we configure Portworx.&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/intro-to-ceph-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Intro to Ceph storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/08/kubernetes-storage-on-azure-2-of-3-portworx/"&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 2 of 3 – Portworx&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>