Azure FinOps kurz III: Automatizace nákladů

Azure FinOps kurz III: Automatizace nákladů

středa , leden 31, 2024

V dnešním už třetím díle FinOps online kurzu se budeme zabývat velmi atraktivním tématem a to je automatizace. Je zde vůbec možnost si ulevit v rámci Azure FinOps tím, že některé věci automatizujeme?

Na začátek je zapotřebí si odpovědět na tuto základní otázku. Asi už tušíte, že správná odpověď je ANO. Můžeme si tím velmi zjednodušit život. Prvním obrovským pomocníkem jsou Azure Policy. 

Azure Policy

Díky politikám, které uplatníme v Azure, můžeme velmi ovlivnit cenu, kterou platíme za jednotlivé projekty či zdroje. 

Omezování Typů VM

Implementujte politiku, která omezuje vytváření VM pouze na určité typy. To pomůže kontrolovat náklady tím, že zabrání používání dražších typů VM bez schválení.

{
  "if": {
    "allOf": [
      {
        "field": "type",
        "equals": "Microsoft.Compute/virtualMachines"
      },
      {
        "field": "Microsoft.Compute/virtualMachines/vmSize",
        "notIn": ["Standard_DS1_v2", "Standard_DS2_v2"]
      }
    ]
  },
  "then": {
    "effect": "deny"
  }
}

Zde je například kus kódu Azure JSON politiky zakazující virtuální servery DS1_v2 a DS2_v2 ve Vašem prostředí. Vhodné je toto použití na testovací či developerské prostředí, kde se nemusí vytáčet drahé virtuálky, aby nečině běžely bez většího využití. 

Auto-shutdown VM

Dalším skvělým pomocníkem je automatické vypnutí virtuálky. Je možnost rovnou nastavit při vytváření serveru. 

Nastavení auto-shutdown na 19 hodin večer každý den

Nebo je možné nastavit globální politiku, která vypne všechny servery v testovacím a developerském prostředí každý den po práci v určitou hodinu. 

{
      "parameters": {
      "time": {
        "type": "String",
        "metadata": {
          "displayName": "Scheduled Shutdown Time",
          "description": "Daily Scheduled shutdown time. i.e. 2300 = 11:00 PM"
        },
        "defaultValue": "1900"
      },
      "timeZoneId": {
        "type": "String",
        "metadata": {
          "displayName": "Time zone",
          "description": "The time zone ID (e.g. Pacific Standard time)."
        },
        "defaultValue": "Central Europe Standard Time"
      },
      "EnableNotification": {
        "type": "String",
        "metadata": {
          "displayName": "Send Notification before auto-shutdown",
          "description": "If notifications are enabled for this schedule (i.e. Enabled, Disabled)."
        },
        "allowedValues": [
          "Disabled",
          "Enabled"
        ],
        "defaultValue": "Disabled"
      },
      "NotificationEmailRecipient": {
        "type": "String",
        "metadata": {
          "displayName": "Email Address",
          "description": "Email address to be used for notification"
        },
        "defaultValue": ""
      },
      "NotificationWebhookUrl": {
        "type": "String",
        "metadata": {
          "displayName": "Webhook URL",
          "description": "A notification will be posted to the specified webhook endpoint when the auto-shutdown is about to happen."
        },
        "defaultValue": ""
      }
    },
    "policyRule": {
      "if": {
        "field": "type",
        "equals": "Microsoft.Compute/virtualMachines"
      },
      "then": {
        "effect": "deployIfNotExists",
        "details": {
          "type": "Microsoft.DevTestLab/schedules",
          "existenceCondition": {
            "allOf": [
              {
                "field": "Microsoft.DevTestLab/schedules/taskType",
                "equals": "ComputeVmShutdownTask"
              },
              {
                "field": "Microsoft.DevTestLab/schedules/targetResourceId",
                "equals": "[concat(resourceGroup().id,'/providers/Microsoft.Compute/virtualMachines/',field('name'))]"
              }
            ]
          },
          "roleDefinitionIds": [
            "/providers/microsoft.authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"
          ],
          "deployment": {
            "properties": {
              "mode": "incremental",
              "template": {
                "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                "contentVersion": "1.0.0.0",
                "parameters": {
                  "vmName": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string"
                  },
                  "time": {
                    "type": "string",
                    "defaultValue": "",
                    "metadata": {
                      "description": "Daily Scheduled shutdown time. i.e. 2300 = 11:00 PM"
                    }
                  },
                  "timeZoneId": {
                    "type": "string",
                    "defaultValue": "",
                    "metadata": {
                      "description": "The time zone ID (e.g. Pacific Standard time)."
                    }
                  },
                  "EnableNotification": {
                    "type": "string",
                    "defaultValue": "",
                    "metadata": {
                      "description": "If notifications are enabled for this schedule (i.e. Enabled, Disabled)."
                    }
                  },
                  "NotificationEmailRecipient": {
                    "type": "string",
                    "defaultValue": "",
                    "metadata": {
                      "description": "Email address to be used for notification"
                    }
                  },
                  "NotificationWebhookUrl": {
                    "type": "string",
                    "defaultValue": "",
                    "metadata": {
                      "description": "A notification will be posted to the specified webhook endpoint when the auto-shutdown is about to happen."
                    }
                  }
                },
                "variables": {},
                "resources": [
                  {
                    "name": "[concat('shutdown-computevm-',parameters('vmName'))]",
                    "type": "Microsoft.DevTestLab/schedules",
                    "location": "[parameters('location')]",
                    "apiVersion": "2018-09-15",
                    "properties": {
                      "status": "Enabled",
                      "taskType": "ComputeVmShutdownTask",
                      "dailyRecurrence": {
                        "time": "[parameters('time')]"
                      },
                      "timeZoneId": "[parameters('timeZoneId')]",
                      "notificationSettings": {
                        "status": "[parameters('EnableNotification')]",
                        "timeInMinutes": 30,
                        "webhookUrl": "[parameters('NotificationWebhookUrl')]",
                        "emailRecipient": "[parameters('NotificationEmailRecipient')]",
                        "notificationLocale": "en"
                      },
                      "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]"
                    }
                  }
                ],
                "outputs": {}
              },
              "parameters": {
                "vmName": {
                  "value": "[field('name')]"
                },
                "location": {
                  "value": "[field('location')]"
                },
                "time": {
                  "value": "[parameters('time')]"
                },
                "timeZoneId": {
                  "value": "[parameters('timeZoneId')]"
                },
                "EnableNotification": {
                  "value": "[parameters('EnableNotification')]"
                },
                "NotificationEmailRecipient": {
                  "value": "[parameters('NotificationEmailRecipient')]"
                },
                "NotificationWebhookUrl": {
                  "value": "[parameters('NotificationWebhookUrl')]"
                }
              }
            }
          }
        }
      }
    }
  }
  }
}

Zde je příklad custom politiky vytvořené pro automatické vypínání zdrojů každý den v 19 hodin večer. Potom, co aplikujete tuto politiku, je zapotřebí dát uživatelům právo k zapnutí virtuálky, jinak jim zůstane vypnutá na pořád. 

Proto v některých případech je lepší si s uživateli odsouhlasit pracovní dobu jako je 5x8 nebo 5x12 a v těchto časech ji zapínat a mimo ji vypínat. K tomu slouží skvělý zdroj, který se jmenuje "Azure Start/Stop VMs during off hours - V2"

Azure Start/Stop VMs during off hours - V2

Sami si volíte, kdy budou servery běžet a kdy ne. Sami se dle instrukcí spustí a vypnou. Efektivní je především běh pouze v pracovní době a úspora peněz v době, kdy nejsou využívané jako je vékend či v noci. Podrobný článek jsem na to dělal zde. Nemusíte dávat uživateli speciální práva na start nebo stop serveru. 

Povolené regiony

Politika "Allowed Locations" (povolené regiony) je jedna ze základních politik, které v našem Azure nastavujeme. Nikdo asi nechceme mít roztahané zdroje přes několik regionů. Jedním z hlavních důvodů je platba za data přenášená mezi regiony. Dalším je latence, governance atd.

Za každých 100 GB $4.75 není moc, ale ani málo...

Dalším možným aspektem jsou ceny v jednotlivých regionech. A nejsou to žádné centy. Liší se i o desítky procent. Proto je zapotřebí si pečlivě vybrat region, kam umístím svůj projekt. Pokud mám legislativní omezení na EU, tak si stejně můžu vybrat nejlevnější region v rámci EU. Pokud nemám žádné omezení, tak můžu testovací prostředí mít v nejlevnějším regionu, což je aktuálně v době psaní tohoto článku region "Central India". 

Příklad použití levného regionu pro VM (B8 v2) stojí ve West Europe 250 USD a v regionu Central India 154 USD a nebo v Brazílii 433 USD. Což jsou obrovské rozdíly, pokud si vezmeme, že těch serverů může uživatel po nás chtít třeba dvacet.

Každý region má jinou cenu za stejný server se stejnými parametry.

Dalším velkým pomocníkem jsou budgety a jejich alerting. 

Budget

Budgety se dají nastavit velmi jednoduše a to buď na celých skupinách či předplatných. 

Dále je možné nastavit spending limit, ale to lze jen pro několik možných typů předplatnách jako je free account atd. 

Dále je možné pomocí Automation accountu nastavit akci, kdy zastavíme veškeré zdroje, pokud dosáhneme limit daného budgetu, což je věc, která nás asi nejvíce zajímá. 

Postup je následující: 

  1. Vytvoříme si Automation account a přidáme mu oprávnění service principal do našeho předplatného nebo skupiny. 
  2. Dále si vytvoříme Runbook, který bude obsahovat skript, který budeme spoustět, pokud se nám naplní limit.
  3. Vytvoříme Action group, která bude spuštěná limitem budgetu a bude spoustět runbook, který jsme si vytvořili. 

Zde je skript do runbooku, který zastaví zdroje, aby se dále neplatili:

param (
    [Parameter(Mandatory=$false)] 
    [String]  $AzureCredentialAssetName = 'AzureCredential',
        
    [Parameter(Mandatory=$true)]
    [String] $AzureSubscriptionId,

    [Parameter(Mandatory=$true)]
    [String] $AzureTenantID,

    [Parameter(Mandatory=$false)] 
    [String] $ResourceGroupName
)

# Returns strings with status messages
[OutputType([String])]

# Connect to Azure and select the subscription to work against

$Cred = Get-AutomationPSCredential -Name $AzureCredentialAssetName -ErrorAction Stop
Add-AzureRmAccount -Credential $Cred -TenantId $azureTenantId  -ServicePrincipal  -ErrorAction Stop -ErrorVariable err

if($err) {
    throw $err
}

$SubId = $AzureSubscriptionId

# If there is a specific resource group, then get all VMs in the resource group,
# otherwise get all VMs in the subscription.
if ($ResourceGroupName) 
{ 
    $VMs = Get-AzureRmVM -ResourceGroupName $ResourceGroupName -status
}
else 
{ 
    $VMs = Get-AzureRmVM -status
}

# Stop each of the VMs
foreach ($VM in $VMs)
{
    if($VM.PowerState -eq "VM running"){
    $StopRtn = $VM | Stop-AzureRmVM -Force -ErrorAction Continue

        if ($StopRtn.Status -ne 'Succeeded')
        {
            # The VM failed to stop, so send notice
            Write-Output ($VM.Name + " failed to stop")
            Write-Error ($VM.Name + " failed to stop. Error was:") -ErrorAction Continue
            Write-Error (ConvertTo-Json $StopRtn.Error) -ErrorAction Continue
        }
        else
        {
            # The VM stopped, so send notice
            Write-Output ($VM.Name + " has been stopped")
        }
    }
}
# Add Resource Lock to prevent further changes
if ($ResourceGroupName) 
{ 
    New-AzureRmResourceLock -LockLevel ReadOnly -LockNotes "Lock after breaching budget - contact operations to remove" -LockName "BudgetBreachLock" -ResourceGroupName $ResourceGroupName -force
}
else{
    New-AzureRmResourceLock -LockLevel ReadOnly -LockNotes "Lock after breaching budget - contact operations to remove" -LockName "BudgetBreachLock"   -Scope "/subscriptions/$AzureSubscriptionId"
}

Vhodné použití je pro testovací a developerské prostředí, kdy chceme udržet náklady na uzdě a zároveň naužit uživatele mít rozpočet pod kontrolou. 

Automatické mazání zdrojů

Dalším možnou úsporou může být, hlavně pro vzdělávací prostředí, automatické mazání zdrojů. Smazat je můžeme ve frekvenci denně, týdně či měsíčně. Často se stává, že si něco otestujeme a už to nevypneme či nesmažeme. Tento velmi drastický způsob, jak mazat zdroje, je skvělý nástroj k tomu, abychom na nic nemuseli myslet a neměli vysoké účty za vzdělávací prostředí. 

Technický postup je uvedený v tomto článku

Logic Apps a škálování

Jistě už asi víte, že automatické škálování jde nastavít na VMSS. Ostatně jsem na to dělal článek zde. Ale lze i dělat automatické škálování běžných virtuálních serverů? Ano. Lze to. Díky Logic Apps. Návod je velmi jednoduchý. Nejdříve je třeba si definovat jaké servery chceme škálovat a na jaké velikosti. Příkladem může být produkční prostředí, kde chceme přes pracovní dny mít k dispozici nabušené servery, ale během noci nebo o víkendu nic takového nepotřebujeme, ale nechceme je zastavovat. 

Pro tyto účely můžeme škálovat na základě námi definovaných časů a zmenšovat dle potřeby sizing virtuálek. Například potřebujeme během pracovních dnů od 8 do 18 hodin 4 vCPU, ale během noci stačí jen 2 vCPU. Není nic jednoduššího, než si vytvořit LogicAppku a v ní nastavit trigger a spustit skript na zmenšení VM a ráno opět na zvětšení VM.  

Postupné kroky jsou: 

  1. Vyberu si nejdříve virtuálky a jejich sizing, který chci nastavit v časovém rámci. Jejich skupiny či předplatná.
  2. Vytvořím si Logic App a v ní dvě workflow. Jedno na škálování nahoru a jedno na škálování dolů.
  3. Otevřu si App designer a v něm vyberu jako první souštěč „Recurrence“ (Opakování) pro pravidelné spouštění Logic App. Například každý všední den v 8 ráno. To samé analogicky pro druhý, akorát v 6 večer. 
  4. Jako další krok si vyberu "Create or update a template deployment" (Vytvořit nebo aktualizovat nasazení šablony).
  5. Nastavení akce: Subscription: Vyberte předplatné, ve kterém se nachází VM. Resource Group: Zde vyberte nebo zadajte název skupiny prostředků, např. "test". Location: Vyberte umístění, například "West Europe". Template: Zde vložíte JSON šablonu ARM, kterou jste připravili. Šablona definuje změnu velikosti VM "VMtestsizing" na "Standard_D4s_v4". Template Parameters (pokud jsou potřeba): Zde můžete zadat hodnoty pro parametry definované ve vaší šabloně ARM.

Template může vypadat následovně: 

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "vmName": {
      "type": "string",
      "metadata": {
        "description": "The name of the virtual machine."
      }
    },
    "vmSize": {
      "type": "string",
      "metadata": {
        "description": "The desired size of the virtual machine."
      }
    }
  },
  "resources": [
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2021-04-01",
      "name": "[parameters('vmName')]",
      "location": "West Europe",
      "properties": {
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        }
      }
    }
  ]
}

Parametry mohou vypadat následovně: 

{
  "vmName": {
    "value": "VMtestsizing"
  },
  "vmSize": {
    "value": "Standard_D4s_v4"
  }
}
Dvě aplikace na škálování. Jedna nahoru, jedna dolu.
Každý všední den v 10:30 změna velikosti VM
Změna sizingu pomocí JSON template

V Rámci Logic apps lze samozřejmě automaticky dělat téměř zázraky. Lze automaticky změnit velikost VM, která je moc velká, protože se uživatel ukliknul a server s 16 vCPU nepotřebuje a stačí mu jen 4 vCPU atd. 

Otázky 

  1. Jaké máte automatické FinOps vychytávky u Vás ve firmě?  
  2. Nad čím si lámete hlavu a výrazně by Vám to ulehčilo práci a platili byste méně? 

Svoje názory mi můžete psát dolev komentářích nebo na sockách.

Zatím žádné komentáře
Vyhledávání