Einstieg in Azure Resource Manager

Moderne Applikationen, die in Azure gehostet werden, bestehen aus vielen verschiedenen Komponenten / Services, die untereinander kommunizieren, Daten austauschen und zwischen denen eine mehr oder minder große Abhängigkeit besteht. Schon einfache Anwendungen bestehen aus einer Datenbank, einem Cache, einer API, einer Webseite usw.  – also vielen einzelnen Komponenten, die gemanaget sein wollen.

Azure Portal

Um die oben genannten Dienste in Azure zu erstellen und zu verwalten, stellt Microsoft als ersten Anlaufpunkt das Azure Portal bereit. Die Oberfläche bietet eine komfortable UI, mit der man alle von Azure angebotenen Services erstellen und managen kann. Es eignet sich hervorragend für Entwickler, die schnell einen Service ausprobieren möchten, für das Erstellen einfacher Prototypen oder einen Proof-Of-Concept – auch kleinere Projekte lassen sich ausreichend gut über das Portal verwalten.

portal
Azure Portal

Es hat jedoch auch seine Nachteile – gerade dann, wenn ein Projekt größer wird und mehrere Personen gleichzeitig an der Infrastruktur arbeiten. Wer kann nachvollziehen, wieso diese eine Einstellung, die aktuell Probleme verursacht, vor zwei Wochen über das Portal an der Web-App gemacht wurde?! Wer hat die Anzahl der Instanzen innerhalb eines VM Scale Sets von „4“ auf „6“ gestellt – und vor allem wieso? Wieso wurde diese eine Resource gelöscht und wer weiß, wie sie nun wiederhergestellt werden kann? Ja, für solche Dinge gibt es auch „Role-Based Access Control„, um zu verhindern, dass bestimmte Personen / Rollen Einstellungen vornehmen können. Spätestens jedoch, wenn die ganze Infrastruktur neu erstellt werden muss (mit allen Abhängigkeiten untereinander), kommt man zu der Einsicht, dass man seine Umgebung auf eine andere Art und Weise managen muss.

Azure Resource Manager / Templates

Bevor man in den Azure Resource Manager und die dazugehörigen Templates einsteigen kann, muss man zunächst einige Begrifflichkeiten klären:

  • Resource – ein über Azure verwaltbares Element. Beispiel: eine virtuelle Maschine, eine Datenbank, eine Web App, Einstellungen einer Web App usw.
  • Resource Group – ein Container für Resources. Eine Ressource kann ohne eine Resource Group (RG) nicht in Azure existieren. Deployments von Ressourcen werden immer auf einer RG ausgeführt. Typischerweise werden Ressourcen mit dem gleichen Lifecycle in eine Gruppe gepackt.
  • Resource Provider – ein Dienst zum Erstellen einer Ressource über den Azure Resource Manager. Beispiel „Microsoft.Web“, um eine Web App zu erstellen. „Microsoft.Storage“, zur Erstellung eines Storage Accounts usw.
  • Azure Resource Manager (ARM) Templates – eine JSON Datei, die eine oder mehrere Ressourcen beschreibt, die über ein Deployment in eine Resource Group bereitgestellt werden. Die Vorlage kann zum konsistenten und wiederholten Bereitstellen der Ressourcen verwendet werden.

Wie nun aus den oben bereitgestellten Informationen abgeleitet werden kann, ist der Azure Resource Manager (ARM) die Komponente in der Azure Cloud, die sich um das Bereitstellen und Verwalten von Ressourcen kümmert. Über ARM werden einzelne Dienste oder Gruppen von Ressourcen gemanaget. Auch können ARM Templates verwendet werden, um eine Infrastruktur wiederholt zu deployen.

Ein, wie ich finde, großer Vorteil bei der Verwendung von ARM Templates ist die Nachvollziehbarkeit von Änderungen an der Infrastruktur. Denn Templates können gemeinsam mit dem Quellcode der Applikation in die jeweilige Source Code Verwaltung abgelegt werden. Hat man im Entwicklungsprozess Continuous Integration/Deployment etabliert, kann man z.B. aus Jenkins, TeamCity oder Visual Studio Team Services heraus das Deployment der Infrastruktur erledigen und niemand muss sich mehr um ein Update der Umgebung kümmern – Web Apps, Datenbanken, Caches usw. werden bereitgestellt, danach erfolgt das Deployment der eigentlichen Applikation (entweder in einem Schritt oder separat) – es sind keinerlei manuelle Schritte mehr notwendig.

Aber – first things first – wie sieht ein einfaches ARM Template aus? Hier ein Beispiel einer einfachen Web App:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "hostingPlanName": {
      "type": "string",
      "defaultValue": "mytestappplan",
      "minLength": 1
    },
    "skuName": {
      "type": "string",
      "defaultValue": "S1",
      "allowedValues": [
        "S1",
        "S2",
        "S3"
      ],
      "metadata": {
        "description": "Describes plan's pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
      }
    },
    "skuCapacity": {
      "type": "int",
      "defaultValue": 1,
      "minValue": 1,
      "metadata": {
        "description": "Describes plan's instance count"
      }
    }
  },
  "variables": {
    "webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]"
  },
  "resources": [
    {
      "apiVersion": "2015-08-01",
      "name": "[parameters('hostingPlanName')]",
      "type": "Microsoft.Web/serverfarms",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "HostingPlan"
      },
      "sku": {
        "name": "[parameters('skuName')]",
        "capacity": "[parameters('skuCapacity')]"
      },
      "properties": {
        "name": "[parameters('hostingPlanName')]"
      }
    },
    {
      "apiVersion": "2015-08-01",
      "name": "[variables('webSiteName')]",
      "type": "Microsoft.Web/sites",
      "location": "[resourceGroup().location]",
      "tags": {
        "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
        "displayName": "Website"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
      ],
      "properties": {
        "name": "[variables('webSiteName')]",
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
      }
    }
  ]
}

 

Zugegeben, auf den ersten Blick sieht ein solches Template wild aus und für eine einzelne Webseite wohl auch aufwandstechnisch übertrieben, bei genauerer Betrachtung (und umfangreichen Infrastrukturen) allerdings, löst sich dieser Eindruck schnell wieder auf.

Ein ARM Template besteht in der Regel aus mehreren Teilen:

  • Parameters – Parameter, die von außen an das Template weitergereicht werden. Beispiel im oben genannten Template: „hostingPlanName“. Parameter können typisiert und mit Default-Werten oder erlaubten Werten („allowedValues“) ausgestattet sein.
  • Variables – Variablen zur internen Verwendung. Typischerweise werden Parameter „aufbereitet“, z.B. Namen konkateniert, und in Variablen zur späteren Verwendung gespeichert. Beispiel im oben genannten Template: „webSiteName“.
  • Resources – die eigentlichen Ressourcen, die erstellt werden sollen
  • Outputs – Ausgabe-Parameter, die nach der Erstellung der Ressourcen an den Aufrufer zurückgegeben werden.

Im Template können einfache Funktionen, wie z.B. concat, uniqueString oder toLower verwendet werden, um mit Variablen zu arbeiten. Eine genaue Übersicht der möglichen Funktionen findet sich hier: https://docs.microsoft.com/de-de/azure/azure-resource-manager/resource-group-template-functions

Für die Erstellung der Ressourcen ist wie oben bereits erwähnt, der Abschnitt „Resources“ im Template zuständig. Hier werden die für den Resource Provider geltenden Einstellungen definiert. Jeder Provider Typ bietet verständlicherweise andere Parameter und Abschnitte zur Konfiguration der zu erstellenden Ressource an – die entsprechenden Dokumentationen pro Provider findet man hier unter dem Punkt Reference: https://docs.microsoft.com/en-us/azure/templates/

Im Beispiel-Template wird u.a. eine Web-App erstellt – der zuständige Resource Provider ist hier „Microsoft.Web/sites„. Ein wichtiges Element im hier erwähnte Abschnitt ist der Punkt „dependsOn“. Über dieses Array können unter Angaben der Resource-Id Abhängigkeiten untereinander definiert werden. In diesem speziellen Fall bedeutet…

"dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
]

…dass die Web-App abhängig vom App-Service Plan („Microsoft.Web/serverfarms/„) ist, in den die App deployt wird – was auch logisch erscheint. Über die dependsOn-Angaben kann der Azure Resource Manager einen Deployment-Tree aufbauen und so mit Erstellung der Ressourcen zuerst beginnen, auf die andere eine Abhängigkeit haben.

Das Template kann schlussendlich über verschiedene Wege an den Resource Manager übergeben werden:

  • Powerhell
  • Azure CLI
  • REST
  • Visual Studio

Ich arbeite gerne mit der Azure CLI, da diese plattformübergreifend auf MacOS, Linux und Windows zur Verfügung steht, deshalb hier nun das Beispiel mit der CLI:

Zunächst muss eine Resource Group erstellt werden (falls nicht vorhanden):

az group create --name testdeployment --location westeurope

Nach erfolgreicher Ausführung sollte die Ausgabe folgendermaßen aussehen:

{
  "id": "/subscriptions/[subscriptionid]/resourceGroups/testdeployment",
  "location": "westeurope",
  "managedBy": null,
  "name": "testdeployment",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null
}

Im Anschluss kann das eigentliche Deployment durchgeführt werden („WebSite.json“ ist das Template-File mit den oben dargestellten Inhalten im aktuellen Verzeichnis):

az group deployment create --mode complete --resource-group testdeployment --template-file .\WebSite.json --verbose

Im hier verwendeten Beispiel wird der Modus „complete“ benutzt, um das Template anzuwenden. Es gibt zwei Modi, die verwendet werden können:

  • Complete – Ressourcen, die nicht in der Vorlage vorhanden sind, aber in der Resource Group existieren, werden gelöscht.
  • Incremental – Ressourcen, die nicht in der Vorlage vorhanden sind, aber in der Resource Group existieren, bleiben unverändert

Beide Modi haben Vor- bzw. Nachteile und man muss den für sich passenden Modus herausfinden. Complete sagt mir persönlich mehr zu, da man mit diesem Modus die komplette Infrastruktur beschreibt, wie sie nach dem Deployment aussehen soll.

Nach kurzer Zeit ist das Deployment erfolgreich durchgeführt und das Kommando liefert die Ergebnisse vom Azure Resource Manager zurück.

{
  "id": "/subscriptions/[subscriptionid]/resourceGroups/testdeployment/providers/Microsoft.Resources/deployments/WebSite",
  "name": "WebSite",
  "properties": {
    "correlationId": "1fbfa8f1-44dd-483c-ab9b-38296cd834b7",
    "debugSetting": null,
    "dependencies": [
      {
        "dependsOn": [
          {
            "id": "/subscriptions/[subscriptionid]/resourceGroups/testdeployment/providers/Microsoft.Web/serverfarms/mytestappplan",
            "resourceGroup": "testdeployment",
            "resourceName": "mytestappplan",
            "resourceType": "Microsoft.Web/serverfarms"
          }
        ],
        "id": "/subscriptions/[subscriptionid]/resourceGroups/testdeployment/providers/Microsoft.Web/sites/webSitesycktqb3r6rze",
        "resourceGroup": "testdeployment",
        "resourceName": "webSitesycktqb3r6rze",
        "resourceType": "Microsoft.Web/sites"
      }
    ],
    "mode": "Complete",
    "outputs": null,
    "parameters": {
      "hostingPlanName": {
        "type": "String",
        "value": "mytestappplan"
      },
      "skuCapacity": {
        "type": "Int",
        "value": 1
      },
      "skuName": {
        "type": "String",
        "value": "S1"
      }
    },
    "parametersLink": null,
    "providers": [
      {
        "id": null,
        "namespace": "Microsoft.Web",
        "registrationState": null,
        "resourceTypes": [
          {
            "aliases": null,
            "apiVersions": null,
            "locations": [
              "westeurope"
            ],
            "properties": null,
            "resourceType": "serverfarms"
          },
          {
            "aliases": null,
            "apiVersions": null,
            "locations": [
              "westeurope"
            ],
            "properties": null,
            "resourceType": "sites"
          }
        ]
      }
    ],
    "provisioningState": "Succeeded",
    "template": null,
    "templateLink": null,
    "timestamp": "2017-05-23T16:20:27.645497+00:00"
  },
  "resourceGroup": "testdeployment"
}

Ausblick

Mit dem oben gezeigten Beispiel ist ein erster Einstieg in das Thema „Azure Resource Manager“ und den dazugehörigen Templates geschafft. Wer tiefer einsteigen möchte, kann sich über die folgenden Ressourcen informieren:

Im nächsten Artikel werde ich genauer auf Templates eingehen und ein fortgeschrittenes Szenario aufzeigen, in dem die komplette Infrastruktur für eine Cloud App beschrieben wird (API App inkl. Deployment Slots, SQL DB, Redis Cache, Azure Search, AppInsights…), inklusive Abhängigkeiten der Ressourcen untereinander.

Bis dahin…viel Spaß mit Azure! 🙂

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s