Azure Resource Manager a šablony prakticky

Azure Resource Manager (ARM) je již nějakou dobu primární metodou, jak v Azure pracovat s výpočetními, datovými i platformními službami, a postupně vytlačuje tu klasickou. Pokud používáte webový portál Azure, s volbou „Resource Manager“ vs. „Classic“ jste se určitě setkali, ale kromě toho, že vám každá nabídla mírně odlišný formulář, se na první pohled nijak zvlášť neliší. Síla ARM se naplno projeví, jakmile začnete používat šablony nasazení, neboli anglicky Deployment Templates.

Pokud je vám koncept Resource Manageru cizí, projděte si nejprve oficiální dokumentaci, abyste získali představu o tom, jak ARM funguje.

Šablona nasazení vám umožňuje definovat kompletní Azure infrastrukturu ve formátu JSON. K čemu je to dobré? Díky tomuto předpisu můžete automatizovat deployment na různá prostředí – vývoj, testování, produkci... Nasazování je libovolně opakovatelné a parametrizovatelné, takže si každý vývojář může vytvořit vlastní verzi celého backendu pro testování. Zdrojový kód šablony je možné udržovat v úložišti zdrojových kódů a verzovat ho jako zbytek aplikace. A třešnička na závěr – šablonu můžete zapojit i do cyklu Continuous Integration (CI) jako jeden z kroků po sestavení a otestování aplikace.

V tomto článku se na šablony podíváme prakticky a ukážeme si, jak se vytvářejí, na co si dát pozor a co naopak využít, na základě zkušeností ze skutečných projektů.

Limit velikosti šablony je 1 MB po rozpadu všech proměnných a funkcí, nicméně nejjednodušší šablona vypadá takto:

 
{ 
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 
  "contentVersion": "", 
  "parameters": { }, 
  "variables": { }, 
  "resources": [ ], 
  "outputs": { } 
}

Je to opravdu soubor JSON rozdělený na klíčové oblasti:

  • Do parameters se umisťují hodnoty (parametry), které přijdou do šablony zvnějšku - požadovaný počet instancí webu, název Storage Accountu nebo třeba heslo administrátora linuxového serveru. Používání parametrů není povinné, ale je vhodné. Když je vynecháte, nasadí se prostředky pokaždé se stejnými názvy ve stejných lokalitách a pravděpodobně dojde ke konfliktům.
  • Ve variables se dynamicky sestavují proměnné, aby se mohly používat dále v šabloně. Tvoří je parametry, funkce a konstanty. Používají se například pro vytvoření jedinečného názvu z parametru nebo přidání předpon/přípon ke jménům.
  • Část resources je typicky nejobsálejší, protože je v ní definice všech zdrojů, které budeme nasazovat – weby, virtuální stroje, síťové adaptéry, databáze... vše najdeme zde.
  • A nakonec v outputs říkáme, jaké hodnoty chceme ze šablony dostat zpět. Mohou to být třeba vygenerované jedinečné názvy, connection stringy nebo adresy serverů.

Přestože je šablona textový soubor ve formátu JSON, nemusí být statická – pomocí výrazů uzavřených v hranatých závorkách [ ] můžeme hodnoty vyplňovat až za běhu podle kontextu. Výrazy se mohou objevit kdekoliv, podmínkou ale je, aby vracely validní JSON hodnotu.

Do výrazů se dají vkládat předpřipravené funkce. Často používané jsou např.:

Funkce Příklad Co dělá
parameters() [parameters('webTier')] Vrátí hodnotu parametru „webTier“.
variables() [variables('saltedPass')] Vrátí hodnotu proměnné „saltedPass“.
concat() [concat('ABCD', base64(parameters('password')))] Spojí řetězec „ABCD“ s hodnotou parametru „password“ kódovanou do Base 64.
resourceGroup() [resourceGroup().location] Vratí hodnotu „location“ z JSON objektu Resource Group, do níž se právě nasazuje (např. „North Europe“).
uniqueString() [uniqueString(subscription().subscriptionId)] Vrátí hash dlouhý 13 znaků, který není globálně jedinečný. Parametr určuje rozsah unikátnosti hodnoty – v tomto případě unikátní v rámci subscription.
[uniqueString(resourceGroup().id)] Hash dlouhý 13 znaků, jedinečný v rámci Resource Group.
reference() [reference(concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName')), '2016-01-01').primaryEndpoints.blob] Vrátí primární URI pro Blob Storage. Funkce reference() získává hodnotu z runtime, takže se nedá použít v proměnných, ale dá se použít v outputs. Nevykoná se, dokud prostředek neexistuje.

Na velikosti písmen u názvů funkcí a parametrů nezáleží.

Pro představu, jak vypadá celá jednoduchá šablona, můžete zabrousit na GitHub a podívat se na jeden z příkladů. Často se pro definici používají soubory dva: azuredeploy.json a azuredeploy.parameters.json (tyto názvy jsou obvyklé, ale mohou být libovolné).

  • azuredeploy.json obsahuje samotnou šablonu – deklarace parametrů, definice proměnných a zdrojů atd. Sám o sobě stačí k nasazení, pokud poskytnete hodnoty parametrů.
  • azuredeploy.parameters.json obsahuje hodnoty parametrů, které vstupují do šablony, takže je nemusíte vyplňovat při každém nasazení ručně. Při nasazení přes příkazovou řádku se přidává jako parametr.

Práce se šablonami ve Visual Studiu zjednodušuje napovídání kódu a jeho formátování, k dispozici je také šablona projektu Azure Resource Group a panel JSON Outline.

clip_image002 clip_image003 clip_image005

Začínáme

Začít s tvorbou šablon můžete několika způsoby:

  1. Ve Visual Studiu založit nový projekt typu Azure Resource Group a postupně pomocí UI poskládat vše potřebné.
  2. Na GitHubu najít hotovou šablonu, která odpovídá vašim potřebám, a přizpůsobit si ji. Následně do ní přidávat další prostředky.
  3. Připravit celou infrastrukturu v Azure a následně na Resource Group použít příkaz Automation Script, který vygeneruje hotovou šablonu reflektující přesně ty prostředky, které aktuálně provozujete.

V našem případě jsme zvolili třetí možnost, protože prostředí vznikalo postupně během vývoje. Výsledný JSON vypadá jako kompletní šablona, která je i do jisté míry parametrizovaná. Nenechte se však zmást – úpravy budou potřeba.

Upravování šablony

Názvy a parametry

První věc, na kterou se zaměříme, jsou názvy. Na začátku je dobré si říct, které budete generovat automaticky a které se budou volit jako vstup šablony. Azure typicky vygeneruje individuální parametry pro každou součást, kterou nasazujete.

My jsme šli opačným směrem a zredukovali zadávání názvů na jediný parametr: nameRoot. Zbytek se vygeneruje automaticky. Protože jsou jména prostředků dynamická, nacházejí se v sekci variables:

 
"variables": {
  "backendName": "[concat(parameters('nameRoot'), '-backend', uniqueString(subscription().subscriptionId))]",
  "hostingPlanName": "[concat(parameters('nameRoot'), '-plan')]",
  "documentDbName": "[concat(toLower(parameters('nameRoot')), uniqueString(subscription().subscriptionId))]",
  "storageAccountName": "[concat(toLower(parameters('nameRoot')), uniqueString(subscription().subscriptionId))]",
  "iotHubName": "[concat(parameters('nameRoot'), '-hub', uniqueString(subscription().subscriptionId))]",
  "serviceBusNamespaceName": "[concat(parameters('nameRoot'), '-ns', uniqueString(subscription().subscriptionId))]",
  "faceApiName": "[concat(parameters('nameRoot'), '-face')]",
  "emotionApiName": "[concat(parameters('nameRoot'), '-emotion')]"
}

Princip jejich skládání je většinou stejný – funkcí concat() spojíme text zadaný do parametru nameRoot s příponou podle typu a nakonec v některých případech přidáme pomocí funkce uniqueString() hash, který je jedinečný pro daný účet (díky subscription().subscriptionId).

Unikátní řetězec přidáváme u těch služeb, jejichž název bude tvořit URI. Například u Service Bus vypadá výsledek pro nameRoot = „jmeno“ takto:

sb://jmeno-nsrztevh1eba7ma.servicebus.windows.net/

Šablona slouží vývojářům, takže jsme nebyli úplně striktní vzhledem k ošetření omezení vstupů. Každopádně je dobré mít tato pravidla na paměti hlavně při ladění:

Prostředek Unikátní název Rozsah Znaky
Storage Account Ano 3-24 znaků pouze číslice nebo malá písmena
Web App Ano 2-60 znaků číslice, písmena, pomlčka
DocumentDB Ano 3-50 znaků číslice, malá písmena, pomlčka
IoT Hub Ano 3-50 znaků číslice, malá písmena, pomlčka
Service Bus Ano 6-50 znaků číslice, písmena, pomlčkamusí začínat písmenem, musí končit písmenem nebo číslicí

Některé zdroje mají názvy, které musí zůstat konstantní. Naše aplikace například využívá službu Service Bus, v níž je fronta s názvem „Events“. Protože se na ni odkazuje automatický job, chtěli jsme tento název zachovat, proto jsme parametr odebrali a zadali jej jako prostý text.

Výchozí hodnoty

Jakmile jsou názvy srovnané a parametry zredukované, je na čase pročistit definice jednotlivých prostředků. Azure má ve zvyku ve vygenerované šabloně velmi explicitně stanovit hodnotu různých parametrů, která se ale často kryje s hodnotou výchozí nebo pochází z běhového prostředí a pro definici není podstatná. Například u Service Bus:

 
"properties": {
  "provisioningState": "Succeeded",
  "status": "Active",
  "createdAt": "2016-09-22T09:59:23.153Z",
  "serviceBusEndpoint": "[concat('https://', parameters('namespaces_sbus_name'),'.servicebus.windows.net:443/')]",
  "enabled": true,
  "updatedAt": "2016-09-22T09:59:48.983Z"
}

Všechny tyto hodnoty je možné ze šablony odebrat.

Závislosti

Souběžně s čištěním šablony se můžete zaměřit i na kontrolu závislostí. Některé prostředky závisejí na tom, aby existovaly jiné zdroje. Například:

  • Service Bus Queue nemůže vzniknout, dokud není vytvořeno Service Bus Namespace.

  • Virtual Machine nemůže vzniknout, dokud není vytvořen síťový adaptér a Storage Account.

  • Web App nemůže vzniknout, dokud není vytvořen App Service Plan.dependsOn

    
    "type": "Microsoft.ServiceBus/namespaces",
    "sku": {
      "name": "Basic",
      "tier": "Basic"
    },
    "kind": "Messaging",
    "name": "[variables('serviceBusNamespaceName')]",
    "apiVersion": "2015-08-01",
    "location": "[resourceGroup().location]",
    "tags": {},
    "resources": [
    {
      "type": "queues",
      "name": "Events",
      "apiVersion": "2015-08-01",
      "properties": {},
      "resources": [],
       "dependsOn"  : [ 
         "[resourceId('Microsoft.ServiceBus/namespaces', variables('serviceBusNamespaceName'))]" 
      ]
    }
    

    Výstupy

    clip_image006listKeys()

    • Storage Connection String se používá v C# kódu pro připojení k Azure Storage. Tato hodnota je složena ze dvou údajů: proměnné storageAccountName a prvního klíče, který vrací nově vytvořený Storage Account. Používáme funkci concat() a listKeys() :
    
    "StorageConnectionString": {
      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-01-01').keys[0].value)]",
      "type": "string"
    },
    
    • Storage Keys obsahuje výpis obou klíčů k Azure Storage jako JSON objekt:
    
    "StorageKeys": {
      "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-01-01')]",
      "type": "object"
    },
    
    • Service Bus Keys je opět JSON objekt, v němž najdeme connection string. Přistupujeme na něj přímo přes název RootManageSharedAccessKey, protože je konstantní a určili jsme ho v dřívější části šablony:
    
    "ServiceBusKeys": {
      "value": "[listKeys(resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', variables('serviceBusNamespaceName'), 'RootManageSharedAccessKey'), '2015-08-01')]",
      "type": "object"
    },
    
    • IoT Hub Keys funguje stejně jako Service Bus, opět používáme název klíče:
    
    "IotHubKeys": {
      "value": "[listKeys(resourceId('Microsoft.Devices/IotHubs/Iothubkeys', variables('iotHubName'), 'iothubowner'), '2016-02-03')]",
      "type": "object"
    },
    
    • DocumentDB Endpoint vznikl prostým dosazením názvu účtu DocumentDB do známé adresy URL:
    
    "DocumentDbEndpoint": {
      "value": "[concat('https://', variables('documentDbName'), '.documents.azure.com:443')]",
      "type": "string"
    },
    
    • Pro získání zbylých klíčů se opět používá funkce listKeys() :
    
    "DocumentDbKeys": {
      "value": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', variables('documentDbName')), '2015-04-08')]",
      "type": "object"
    },
    
    "FaceApiKeys": {
      "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('faceApiName')), '2016-02-01-preview')]",
      "type": "object"
    },
    
    "EmotionApiKeys": {
      "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('emotionApiName')), '2016-02-01-preview')]",
      "type": "object"
    }
    

    Nasazení

    clip_image008clip_image010clip_image012clip_image014clip_image016clip_image018clip_image020

    Na závěr

  • My jsme zařadili frontu jako resource pod Service Bus a ještě v sekci „

  • “ řekli, že nemá vzniknut dříve než Service Bus:

  • Vynecháte-li správné stanovení závislostí, Azure vás upozorní chybovou hláškou.

  • Cílem šablony bylo bezobslužně připravit kompletní prostředí pro nasazení a běh aplikace. Důležité jsou i připojovací řetězce a klíče k jednotlivým službám, které je potřeba doplnit do konfigurace před nasazením kódu. Proto jsme připravili sadu výstupů, díky níž není třeba procházet konfiguraci a kopírovat klíče odtud.

  • Jejich získání je ve většině případů triviální a spočívá v použití funkce

  • , nicméně některé hodnoty je potřeba skládat a odvozovat složitěji.

  • K nasazení šablony můžete použít PowerShell, nástroje příkazové řádky (CLI), Visual Studio Team Services nebo webový portál Microsoft Azure. Pro vývoj a postupné zkoušení a opravování chyb se ukázalo jako vhodné použít právě portál.

  • 1. Na hlavní obrazovce (Dashboard) klikneme na „New“.

  • 2. Vyhledáme „Template deployment“.

  • 3. Klikneme na „Edit“.

  • 4. Zkopírujeme svou šablonu z Visual Studia a nahradíme jí kompletní obsah editoru.

  • 5. Potvrdíme tlačítkem „Save“.

  • 6. Vytvoříme novou Resource Group nebo vybereme stávající a zkontrolujeme parametry, které se doplnily ze šablony. Nakonec potvrdíme souhlas s podmínkami a klikneme na „Purchase“.

  • 7. Azure zkontroluje, zda je šablona validní a upozorní vás na případné nedostatky. Pro drobné úpravy a rychlé iterování se osvědčilo použít webové rozhraní a teprve potom je promítnout zpět do Visual Studia. Následně začne samotné nasazení.

  • 8. Průběh deploymentu můžete sledovat po kliknutí na notifikaci, která se objeví vpravo nahoře.

  • 9. Výstupy najdete v Resource Group, sekci „Overview“, v části „Essentials“ pod odkazem „Last Deployment“.

  • Další možnosti nasazování (příkazovou řádku a VSTS) probereme v jiném článku.

  • Hotová šablona je bezpečně uložená ve správě zdrojového kódu, verzovaná a připravená k opakovanému nasazení. Díky dynamickým názvům nehrozí konflikty a každý vývojář z týmu může šablonu vzít a vytvořit si vlastní verzi celého prostředí pro vývoj a testování. Připojovací údaje k jednotlivým službám najde jako výstup, takže je nemusí hledat na portále.