Для приложения требуется создать Resource Group, в которую будут входить:
- один WebApp;
- один SQL сервер;
- две SQL базы;
- один Storage Account.
За основу — можно взять готовый шаблон от Azure («Фягку» 😀 ) отсюда>>>.
Шаблон, который получился в результате написания поста, можно посмотреть тут>>>.
Общий обзор по работе с Azure Resource Manager и созданию шаблонов — в посте Azure: Resource manager, Azure CLI и деплой resource group.
Переключаем подписку:
$ azure account list info: Executing command account list data: Name Id Current State data: -------------------------------------------------- ------------------------------------ ------- -------- data: Pay-As-You-Go fe37db50-***-***-***-66145cdbd735 false Disabled ... data: Meister PII e7aac243-***-***-***-c2493a6a3a7b false Enabled
$ azure account set 0a4f2b9c-***-***-***-40b17ef8c3ab info: Executing command account set info: Setting subscription to "Pay-As-You-Go" with id "0a4f2b9c-***-***-***-40b17ef8c3ab". info: Changes saved
Качаем шаблон:
$ git clone https://github.com/Azure/azure-quickstart-templates.git $ cd 201-web-app-sql-database
Переименовываем файлы:
$ mv azuredeploy.json jm-platform.json $ mv azuredeploy.parameters.json jm-platform.parameters.json
Содержание
Создание группы ресурсов
Используя Azure CLI — запускаем создание группы из этого шаблона с опциями:
-l
: регион Azure;-n
: имя группы;-d
: имя деплоя;-f
: файл шаблона;-e
: файл с параметрами для шаблона.
$ azure group create -l westeurope -n jm-platfrom-dev-eu-2 -d InitDeploy -f jm-platform.json -e jm-platform.parameters.json info: Executing command group create + Getting resource group jm-platfrom-dev-eu-2 + Creating resource group jm-platfrom-dev-eu-2 info: Created resource group jm-platfrom-dev-eu-2 + Initializing template configurations and parameters + Creating a deployment error: Long running operation failed with error: 'At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.'. info: Error information has been recorded to /home/setevoy/.azure/azure.err error: group create command failed
Ошибка сейчас — ОК, смотрим лог /home/setevoy/.azure/azure.err log
:
... { body: '{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.","details":[{"code":"BadRequest","message":"{\\r\\n \\"code\\": \\"40632\\",\\r\\n \\"message\\": \\"Password validation failed. The password does not meet policy requirements because it is not complex enough. ...
Генерируем пароль:
$ pwgen 12 ohba4ebu3Wee oez2iew8Se3S ooru7Eeniet3 Quohci4que6x eiGie9ohwooY eeno2iC6shee ahR1iceephe9 ahte7heisieX Diex7eiGe7in wie9Awei0Wae tohDie6aghie ooM4eeChahng dai5ieNgu0bi Shie3Choh0vo amie1yo7oThi ie1edeeCieku teeDoh6Bohph Kee9Ahch9Qui ...
Обновляем jm-platform.parameters.json
, меняем:
... "administratorLoginPassword": { "value": "GEN-PASSWORD" } ...
На:
"administratorLoginPassword": { "value": "eeno2iC6shee" }
Удаляем группу:
$ azure group list info: Executing command group list + Listing resource groups data: Name Location Provisioning State Tags: data: ------------------------------------------------- -------------- ------------------ ----- data: DNS westeurope Succeeded null data: europe-jm westeurope Succeeded null data: jm-platfrom-dev-eu westeurope Succeeded null data: jm-platfrom-dev-eu-2 westeurope Succeeded null
$ azure group delete -n jm-platfrom-dev-eu-2 info: Executing command group delete Delete resource group jm-platfrom-dev-eu-2? [y/n] y + Deleting resource group jm-platfrom-dev-eu-2
Пробуем:
$ azure group create -l westeurope -n jm-platfrom-dev-eu-2 -d InitDeploy -f jm-platform.json -e jm-platform.parameters.json info: Executing command group create + Getting resource group jm-platfrom-dev-eu-2 + Creating resource group jm-platfrom-dev-eu-2 info: Created resource group jm-platfrom-dev-eu-2 + Initializing template configurations and parameters + Creating a deployment info: Created template deployment "InitDeploy" data: Id: /subscriptions/0a4f2b9c-***-***-***-40b17ef8c3ab/resourceGroups/jm-platfrom-dev-eu-2 data: Name: jm-platfrom-dev-eu-2 data: Location: westeurope data: Provisioning State: Succeeded data: Tags: null data: info: group create command OK
Обновление шаблона
... "type": "Microsoft.Insights/autoscalesettings", ... "type": "Microsoft.Insights/alertrules", ... "type": "Microsoft.Insights/components", ...
AutoscaleSettings, Application Insights и AlertRules — для приложения не нужны, убираем их из ресурсов.
Выносим имя баз (т.к. их будет две) в переменные.
В файле шаблона — убираем:
... "databaseName": { "type": "string", "defaultValue": "sampledb", "metadata": { "description": "The name of the new database to create." } }, ...
Там же находим переменные:
... "variables": { "hostingPlanName": "[concat('hostingplan', uniqueString(resourceGroup().id))]", "webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]", "sqlserverName": "[concat('sqlserver', uniqueString(resourceGroup().id))]" }, ...
И добавляем две переменные для двух баз данных:
... "variables": { "hostingPlanName": "[concat('hostingplan', uniqueString(resourceGroup().id))]", "webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]", "sqlserverName": "[concat('sqlserver', uniqueString(resourceGroup().id))]", "sqlDBbasename": "[concat('sql-DB-', resourceGroup().name)]", "sqlCMSbasename": "[concat('sql-CMS-', resourceGroup().name)]" }, ...
Описание объекта resourceGroup()
есть тут>>>, а функции uniqueString()
— тут>>>.
Находим ресурс создания SQL сервера:
... "resources": [ { "name": "[variables('sqlserverName')]", "type": "Microsoft.Sql/servers", ...
У которого есть вложенный блок resources с базой данных:
... "resources": [ { "name": "[parameters('databaseName')]", "type": "databases", ...
Дублируем его и обновляем имя — вместо [parameters('databaseName')]
используем переменные sqlCMSbasename и sqlDBbasename:
... "resources": [ { "name": "", "type": "databases", "location": "[resourceGroup().location]", "tags": { "displayName": "DB Database" }, "apiVersion": "2014-04-01-preview", "dependsOn": [ "[variables('sqlserverName')]" ], "properties": { "edition": "[parameters('edition')]", "collation": "[parameters('collation')]", "maxSizeBytes": "[parameters('maxSizeBytes')]", "requestedServiceObjectiveName": "[parameters('requestedServiceObjectiveName')]" } }, { "name": "[variables('sqlCMSbasename')]", "type": "databases", "location": "[resourceGroup().location]", "tags": { "displayName": "CMS Database" }, "apiVersion": "2014-04-01-preview", "dependsOn": [ "[variables('sqlserverName')]" ], "properties": { "edition": "[parameters('edition')]", "collation": "[parameters('collation')]", "maxSizeBytes": "[parameters('maxSizeBytes')]", "requestedServiceObjectiveName": "[parameters('requestedServiceObjectiveName')]" } }, ...
Далее, находим connectionstrings:
... "resources": [ { "apiVersion": "2015-08-01", "type": "config", "name": "connectionstrings", "dependsOn": [ "[variables('webSiteName')]" ], "properties": { "DefaultConnection": { "value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('databaseName'), ';User Id=', parameters('administratorLogin'), '@', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]", "type": "SQLServer" ...
Обновляем — добавляем вторую базу и обновляем имя в parameters('databaseName')
— тоже используем переменные:
... "properties": { "sqlDBConnection": { "value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', variables('sqlDBbasename'), ';User Id=', parameters('administratorLogin'), '@', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]", "type": "SQLServer" }, "sqlCMSConnection": { "value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', variables('sqlCMSbasename'), ';User Id=', parameters('administratorLogin'), '@', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]", "type": "SQLServer" } } ...
Запускаем:
$ azure group create -l westeurope -n jm-platfrom-dev-eu-3 -d DBsNamesChanged -f azuredeploy.json -e azuredeploy.parameters.json info: Executing command group create + Getting resource group jm-platfrom-dev-eu-3 + Creating resource group jm-platfrom-dev-eu-3 info: Created resource group jm-platfrom-dev-eu-3 + Initializing template configurations and parameters + Creating a deployment info: Created template deployment "DBsNamesChanged" data: Id: /subscriptions/0a4f2b9c-***-***-***-40b17ef8c3ab/resourceGroups/jm-platfrom-dev-eu-3 data: Name: jm-platfrom-dev-eu-3 data: Location: westeurope data: Provisioning State: Succeeded data: Tags: null data: info: group create command OK
Проверяем:
$ azure group log show jm-platfrom-dev-eu-3 --last-deployment | grep "sql-" /sql-DB-jm-platfrom-dev-eu-3 sqlserverjnknw5oaslkpe/databases/sql-DB-jm- /sql-CMS-jm-platfrom-dev-eu-3 sqlserverjnknw5oaslkpe/databases/sql-CMS-jm- /sql-DB-jm-platfrom-dev-eu-3
Базы с новыми именами готовы.
Приводим к нормальному виду остальные имена:
... "variables": { "hostingPlanName": "[concat(resourceGroup().name, '-app-plan')]", "webSiteName": "[concat(resourceGroup().name, '-app')]", "sqlserverName": "[concat(resourceGroup().name, '-sql-server')]", "sqlDBbasename": "[concat(resourceGroup().name, '-DB-database')]", "sqlCMSbasename": "[concat(resourceGroup().name, '-CMS-database')]" }, ...
Storage Account
Для статики приложения (Umbraco CMS) — используется Blob Storage.
Как пример — можно взять шаблон отсюда>>> (одно из немногих достоинств Azure — наличие большой коллекции таких шаблонов на все случаи жизни, удобный поиск вот тут>>>).
В файл параметров вносим storageAccountType
:
... "administratorLoginPassword": { "value": "eeno2iC6shee" }, "storageAccountType": { "value": "Standard_GRS" } } ...
В файле шаблона — добавляем переменную с именем аккаунта.
Увы — тут не выйдет использовать атрибут resourceGroup().name
, т.к. имя Storage не должно содержать тире. Снова используем uniquestring()
для генерации случайного имени из атрибута id
объекта resourceGroup
:
... "sqlCMSbasename": "[concat(resourceGroup().name, '-CMS-database')]", "storageAccountName": "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'storage')]" }, ...
В конец блока resources
добавляем создание хранилища:
... { "type": "Microsoft.Storage/storageAccounts", "name": "[variables('storageAccountName')]", "apiVersion": "2016-01-01", "dependsOn": [ "[variables('storageAccountName')]" ], "location": "[resourceGroup().location]", "sku": { "name": "[parameters('storageAccountType')]" }, "kind": "Storage", "properties": { } } ...
Примечание: наткнулся на сообщение об ошибке, которая меня в первый момент очень «удивила»:
error: Deployment template validation failed: ‘The resource ‘/subscriptions/0a4f2b9c-***-***-***-40b17ef8c3ab/resourceGroups/jm-platfrom-dev-eu-1/providers/Microsoft.Storage/storageAccounts/jm-platfrom-dev-eu-1-storage’ at line ‘1’ and column ‘3561’ doesn’t depend on parent resource ‘/subscriptions/0a4f2b9c-***-***-***-40b17ef8c3ab/resourceGroups/jm-platfrom-dev-eu-1/providers/Microsoft.Sql/servers/jm-platfrom-dev-eu-1-sql-server’. Please add dependency explicitly using the ‘dependsOn’ syntax. Please see https://aka.ms/arm-template/#resources for usage details.’.
Казалось бы — при чём тут ресурс SQL сервера — к Storage Account-у? Оказалось — я "type": "Microsoft.Storage/storageAccounts"
добавил внутрь блока ресурса "type": "Microsoft.Sql/servers"
, вместо добавления его отдельным блоком.
Запускаем, проверяем:
$ azure group delete -n jm-platfrom-dev-eu-3 $ azure group create -l westeurope -n jm-platfrom-dev-eu-1 -d StorageAccAdd -f jm-platform.json -e jm-platform.parameters.json info: Executing command group create + Getting resource group jm-platfrom-dev-eu-1 + Updating resource group jm-platfrom-dev-eu-1 info: Updated resource group jm-platfrom-dev-eu-1 + Initializing template configurations and parameters + Creating a deployment error: Deployment template validation failed: 'The template parameters 'storageAccountType' in the parameters file are not valid; they are not present in the original template and can therefore not be provided at deployment time. The only supported parameters for this template are 'skuName, skuCapacity, administratorLogin, administratorLoginPassword, collation, edition, maxSizeBytes, requestedServiceObjectiveName'. Please see https://aka.ms/arm-deploy/#parameter-file for usage details.'. info: Error information has been recorded to /home/setevoy/.azure/azure.err error: group create command failed
Упс.
Возвращаемся к файлу шаблона, и в начале файла, в конеце блока parameters
добавляем параметр storageAccountType:
... "storageAccountType": { "type": "string", "defaultValue": "Standard_LRS", "allowedValues": [ "Standard_LRS", "Standard_GRS", "Standard_ZRS", "Premium_LRS" ], "metadata": { "description": "Storage Account type" } } ...
Повторяем:
$ azure group delete -n jm-platfrom-dev-eu-3 $ azure group create -l westeurope -n jm-platfrom-dev-eu-1 -d StorageAccAdd -f jm-platform.json -e jm-platform.parameters.json + Getting resource group jm-platfrom-dev-eu-1 + Creating resource group jm-platfrom-dev-eu-1 info: Created resource group jm-platfrom-dev-eu-1 + Initializing template configurations and parameters + Creating a deployment info: Created template deployment "StorageAccAdd" data: Id: /subscriptions/0a4f2b9c-***-***-***-40b17ef8c3ab/resourceGroups/jm-platfrom-dev-eu-1 data: Name: jm-platfrom-dev-eu-1 data: Location: westeurope data: Provisioning State: Succeeded data: Tags: null data: info: group create command OK
Проверяем:
$ azure storage account list | grep dev-eu data: 5depgbnaltmzmstorage Standard_GRS westeurope jm-platfrom-dev-eu-1
Firewall
Для доступа к SQL сереру — требуется открыть доступ из нашей сети.
Находим:
... { "type": "firewallrules", "apiVersion": "2014-04-01-preview", "dependsOn": [ "[variables('sqlserverName')]" ], "location": "[resourceGroup().location]", "name": "AllowAllWindowsAzureIps", "properties": { "endIpAddress": "0.0.0.0", "startIpAddress": "0.0.0.0" } } ...
Меняем на свои IP:
... "properties": { "endIpAddress": "194.***.***.45", "startIpAddress": "194.***.***.45" ...
Deployment Slot
Для деплоя на WebApp — добавим Deployment slot.
Находим ресурс "type": "Microsoft.Web/sites"
и внутрь его блока resources
— добавляем создание слота:
... { "apiVersion": "2015-08-01", "name": "[variables('slotName')]", "type": "slots", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Web/Sites', variables('webSiteName'))]" ], "properties": {}, "resources": [ ] } ...
В переменные добавляем имя слота:
... "slotName": "staging" ...
Далее, что бы избежать ошибки:
«Cannot complete the operation because the site will exceed the number of slots allowed for the \’Free\’ SKU.\\»
обновляем параметр skuName
, и устанавливаем defaultValue == S1
(Pricing Tier Standard: 2 Small):
Кто-то из читающих — видели описание этих планов? Кроме вот этого>>> — не нагулил ничего. А по этой ссылке — описания ограничений на кол-во инстансов не увидел, есть только в Portal-е.
F1 — B3 можно удалить:
{ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "skuName": { "type": "string", "defaultValue": "S1", "allowedValues": [ "S1", "S2", "S3", "P1", "P2", "P3", "P4" ], ...
Теги
Для удобства — добавим теги для создаваемых ресурсов.
Для SQL сервера можно указать так:
... "resources": [ { "name": "[variables('sqlserverName')]", "type": "Microsoft.Sql/servers", "location": "[resourceGroup().location]", "tags": { "Application": "JM Platform" }, ...
Повторяем для остальных. например — для WebApp:
... "tags": { "Application": "JM Platform WebApp" }, ...
В целом — готово, можно деплоить группы.
По ходу дела — нашёл хороший шаблон для полного деплоя (не только инфрастуктуры) Umbraco CMS вот тут>>>.
Ссылки по теме
Provision a web app with a SQL Database
Azure: Resource manager, Azure CLI и деплой resource group
Authoring Azure Resource Manager templates
Azure Resource Manager template functions (хоть что-то по функциям ARM)
Simple bash scripting for “azure” cli (не совсем по теме поста — но несколько хороших примеров)