Monday, April 17, 2023

Azure VM Selection Made Easy: A Script Identifying Best Constrained CPU VMs for High Memory/ Low CPU Workload

                                         Azure Constrained VCPU and how to list it

Are you struggling to find the most Cost-Effective Azure VMs for Database Workloads or any high memory low CPU workload? Look no further! In this blog post, we’ll introduce the concept of Azure constrained CPU along with cases where az cli displays misleading info, and finally a script that makes it easy to identify the best constrained CPU VM for your needs. This will help you to confidently select the cheapest/suitable Azure VM for your workload.

Here’s a link to my vmsize selector script az-cli-examples/ but first let’s dive into some notions. 

I. Constrained vCPU Vms

Not everyone needs the latest and greatest in CPU power! Some users are actually looking for VM sizes that offer ample memory, storage, and I/O bandwidth without breaking the bank. And when it comes to moving a database to Azure that's licensed for 8 cores (BOYL) but needs a whopping 200GB+ of RAM, you're in for a real treat. You'll have to fork over cash for a 16Core-level VM just to match the memory specs, and say goodbye to your budget while you're at it. That’s the kinda news that makes your boss happy ain’t it?

Azure’s response
To fix this, Azure created Constrained vCPU VMs which allow for constraining the vCPU count to 1/2 or 1/4 of the original VM size (i.e 16=>4), while keeping the same memory, storage, and I/O bandwidth. This makes it an excellent choice for workloads such as databases (SQL Server, Oracle) that are not CPU-intensive but require high memory, and IO bandwidth.
The VM series that support this feature are DS, ES, GS, and MS.

Licensing fees charged for SQL Server for example are based on the available vCPU count.
Constrained vCPUs will result in a 50% to 75% decrease in licensing fees & keep a high VM specs to VCPU ratio.


These new VM sizes have a suffix that specifies the number of available vCPUs to make them easier to identify.
                     Naming convention: Standard_M8-2ms  => 8Core VM level specs with only 2 vCPUs 
Hence, each vm size with  -{digit} in its name supports vCPU constrained feature, the digit being the actual vCPU

II. list Problem with az cli

The option is nice, but finding the ones available to us using a simple az cli query is even better. However, while using the Azure CLI to list the available VM sizes using az vm list-sizes, you will not find the number of constrained cores in the output, making it challenging to filter out the VM sizes based on their requirements.

  • Example with a vm size with 4 constrained vCPus , here you can see that az cli is showing 8 cores which is wrong

$ az vm list-sizes -l eastus --query "sort_by(@,&memoryInMb)[?numberOfCores == \`8\` \
&& (contains(name,'Standard_E8-4ds_v5'))].{name:name,NumberOfCores:numberOfCores}”
Name      - NumberOfCores   
-------------------  --------------- 
Standard_E8-4ds_v5    8  <<— not the actual vCPu      

Several users have reported this issue on the Az CLI GitHub repo, but there has been no official solution to date. Even my shell script to list vm size based on Cpu cores never showed me the actual constrained vCPUs. Few customers started to show me E series sizes with 256GB of Ram + 8 vCPUs that I didn’t know about from my tool.

III. Trick to show actual vCPUs

After upvoting the GitHub issue, I ought to find a workaround to show the real vCPUs for these constrained Series

Since vm size name had a suffix specifying the number of available vCPUs I leveraged it in the below query 

az vm list-sizes -l eastus --query "sort_by(@,&memoryInMb)[?numberOfCores == \`32\` && (contains(name,'E') && contains(name,'-8'))].{name: name, numberOfCores: numberOfCores, memoryInMb: memoryInMb, ConstrainedNumberOfCores: '8'}"

Name                  NumberOfCores    MemoryInMb    ConstrainedNumberOfCores
--------------------  ---------------  ------------  --------------------------
Standard_E32-8s_v4    32 <- default    262144        8 <- actual
Standard_E32-8ds_v5   32               262144        8
Standard_E32-8s_v5    32               262144        8


I just added a virtual column that matches the filter I chose for the vm-size [name=> E series VM, with 32 original cores and 8 constrained vCPUs] “-8”. 

Version2: here I leveraged jquery to make the Constrained value dynamic no matter what is filtered

$ az vm list-sizes -l eastus --query "sort_by(@,&memoryInMb)[?contains(name,'E32') && contains(name,'-8')]" -o json| jq '.[] | . + {ConstrainedNumberOfCores: .name | capture("-(?\\d+)") | .digit}'

{ "maxDataDiskCount": 32, "memoryInMb": 262144, "name": "Standard_E32-8s_v4", "numberOfCores": 32, <<----- default number "osDiskSizeInMb": 1047552, "resourceDiskSizeInMb": 0, "ConstrainedNumberOfCores": "8" <<--- actual number }

IV. Cleaner solution (list-skus)

These tweaks weren't giving the clean output I wanted in my check_az_vmsize script. But then I spotted a second az cli query based on "az vm list-skus". It had all the metadata on VM size capabilities that I was looking for! And to top it off, I finally found that missing piece of information I had been searching for - the number of constrained vCPUs. It was so seamless, it was almost like finding a diamond in a pile of trash.

Here is the final query based on the same query filter (E32, 8 Constrained vCPU)

$ az vm list-skus -z --resource-type  virtualMachines --size "E32" -l eastus --query "[?capabilities[?name==\`vCPUsAvailable\`].value|[0] == '8'].{name:name,VCPU:capabilities[?name==\`vCPUs\`].value|[0],ActualVCPU:capabilities[?name==\`vCPUsAvailable\`].value|[0],MemoryGB:capabilities[?name==\`MemoryGB\`].value|[0]} | reverse(sort_by(@,&name))" -o table
Name VCPU ActualVCPU MemoryGB -------------------- ------ ------------ ---------- Standard_E32-8s_v5 32 8 256 Standard_E32-8s_v4 32 8 256 Standard_E32-8s_v3 32 8 256 Standard_E32-8ds_v5 32 8 256 Standard_E32-8ds_v4 32 8 256 Standard_E32-8as_v5 32 8 256 Standard_E32-8as_v4 32 8 256 Standard_E32-8ads_v5 32 8 256

 After checking the JSON construct of the source, I picked the info through

V. The Ultimate Bundle Script: Azure Constrained CPU VM Selector

We've finally reached the end of our quest to uncover the secrets of Constrained CPU VMs. But why stop there when we can take it one step further?

Here’s a shell-based tool that's so intuitive, you'll think it's reading your mind!  
Try it: Download the script here >> az-cli-examples/ 

  • See demo below


With this tool, you'll have the ability to filter through:

  • Number of vCPU

  • VM compute series

The output has 2 sections:
a) VM sizes with exact vCPU count entered in the prompt

b) All VMs constrained or not containing the vCPU count matching the value entered in the prompt



    • This post helps better understand the concept of Azure constrained CPU and how it can be leveraged to save costs on high memory and low CPU workloads.

    • We have explored cases where az cli can display misleading information about it and track the metadata containing the actual vCPU value

    • By using my simple shell tool, you’ll be able to identify the best VM that meets your specific requirements, allowing you to save money without sacrificing performance.

    • It also saves you hours of searching through Azure website

    • With just 2 prompts, you'll have all the information you need to make an informed decision about your VM selection. 
      So, give it a try and see how much time and money you can save!

Thank you for reading, and happy cost-saving!