DevOps sur Microsoft Azure – Partie 1 : Introduction à DevOps
DevOps avec Azure – partie 2 : Infrastructure as Code (IaC) avec Azure ARM
DevOps avec Azure – Partie 3 : Création et déploiement des ressources avec Visual Studio et ARM Template
Dans la troisième partie de cette serie, j’ai montré comment créer une infrastructure applicative (les ressources qui seront utilisées par l’application) avec Azure ARM Template et Visual Studio. Nous avons utilisé l’EDI pour définir les caractéristiques de notre infrastructure, et ensuite déployer cette dernière sur Azure. Pour en savoir plus, veuillez consulter mon billet de blog.
Dans ce billet, nous verrons comment mettre en place un pipeline d’Intégration et livraison continues en utilisant Visual Studio Team Service (VSTS). Pour en savoir plus sur VSTS, veuillez consulter l’article suivant.
Création du projet
Pour commencer, vous allez créer un nouveau projet « Azure Resource Group » en utilisant le modèle « Web App » :
Une fois le projet créé, vous allez éditer le fichier WebSite.parameters.json pour définir le nom du HostingPlan :
Code json : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "hostingPlanName": { "value": "HostPlan" } } } |
Ajout du gestionnaire de versions
Pour la suite, vous allez créer un nouveau projet d’équipe sur VSTS, en utilisant Git comme gestionnaire de versions. Une fois le projet d’équipe créé, vous aurez une page avec les instructions pour ajouter un repository à votre projet.
Vous allez dérouler la section « Push an existing repository from command line » , puis copier les commandes qui seront affichées et les exécuter en invite de commande dans le dossier racine de votre projet :
Création de la Build Definition
Nous allons maintenant créer une Build Definition dans VSTS pour notre projet. La Build definition est un ensemble de tâches qui va permettre notamment de charger les sources du projet, restaurer les dépendances, générer le projet, exécuter les tests unitaires et enfin produire une sortie (un package de publication) qui pourra être déployée dans l’environnement d’exécution.
Pour notre cas, la sortie sera les fichiers json et PowerShell qui seront utilisés par l’API ARM Template pour déployer notre infrastructure dans Azure. Nous aurons donc besoin d’une tâche pour copier ces fichiers dans le dossier Staging sur le serveur VSTS et d’une seconde tâche pour publier l’artefact dans VSTS, afin que ces fichiers soient visibles pour la Release Definition, qui pourra les utiliser pour le déploiement.
Pour créer la définition de Build, vous devez cliquer dans le menu sur Build and Release, puis sur le bouton New definition :
Vérifiez ensuite que le bon repositorie a été sélectionné pour votre projet, puis cliquez sur Continue.
Puis, dans la fenêtre de sélection du Template qui va s’afficher, sélectionnez Empty.
Vous allez donner un nom à la Build Definition (AzResource-CI). L’onglet Tasks sera affiché avec un Agent Phase ayant pour nom Phase 1. Vous allez laisser les informations par défaut, et ajouter juste les tâches dont nous avons besoin.
Pour le faire, cliquez sur le « + » dans la zone Phase 1 à gauche. Puis, dans la fenêtre qui va s’afficher à droite, saisissez dans la zone de filtre « copy files », puis sélectionnez la tâche « Copy Files », puis cliquez sur Add.
Toujours dans la même fenêtre, vous devez effacer ce que vous avez saisi dans la zone de texte de filtre, puis saisir « Publish » et sélectionner la tâche « Publish Build Artifacts ».
Nous allons maintenant configurer chaque tâche en commençant par la tâche Copy Files.
Vous allez donc sélectionner cette dernière.
Dans la fenêtre qui va s’afficher à droite, vous allez renseigner le répertoire source (Source Folder). Vous devez juste cliquer sur le bouton « … », puis sélectionner le répertoire dans lequel se trouvent les fichiers du projet (AzResourceWeb).
Par la suite, vous devez saisir « $(build.artifactstagingdirectory) » dans le champ « Target Folder » (répertoire de destination). $(build.artifactstagingdirectory) est une variable utilisée dans VSTS qui permet de spécifier le répertoire dans lequel seront déposés les fichiers qui seront publiés.
Cela fait, cliquez sur la tâche « Publish Artifact ».
Dans le champ « Path to publish », saisissez $(build.artifactstagingdirectory). Il s’agit du répertoire dans lequel les fichiers à publier ont été copiés.
Dans le champ « Artifact name », vous allez saisir « drop ».
Dans le champ « Artifact publish location », vous allez dérouler la liste déroulante et choisir « Visual Studio Team Services/TFS ».
Une fois cela fait, vous allez cliquer sur l’onglet « Triggers », puis cocher la case « Enable Continuous integration ». Cette opération va permettre de lancer la buid à chaque commit d’une modification de code :
Cliquez enfin sur « Save & queue » pour enregistrer et démarrer une build, qui vous permettra de valider que tout est correct.
Création de la Release Definition
Passons maintenant à la création de la Release Definition qui permettra de déployer notre projet dans Azure pour créer ou mettre à jour l’infrastructure correspondante.
Pour le faire, cliquez sur Releases dans le menu Build and Release, puis sur le bouton New definition :
Dans la liste des templates qui va s’afficher, sélectionnez Empty process. Dans la fenêtre Environment, vous allez donner comme nom Dev à votre environnement et puis fermer cette fenêtre.
Vous aurez affiché à l’écran l’onglet Pipeline.
Nous avons précédemment défini notre environnement de déploiement. Nous devons maintenant définir l’Artefact qui sera utilisé. Il s’agit du résultat qui sera produit par l’opération de Build.
Vous allez donc cliquer sur Add artifact. Dans la fenêtre qui va s’afficher, vous allez sélectionnez Build comme Source, ensuite sélectionner votre Build definition (AzResource-CI) dans la zone Source, puis cliquer sur Add.
Un bouton en forme de cercle avec l’image d’une éclaire va s’afficher sur l’Artefact nouvellement créé.
Ce bouton permet d’activer le déploiement continu. Lorsque le déploiement continu est activé, chaque fois qu’une opération de Build s’effectuera avec succès, le processus de déploiement de cette version du code se lancera automatiquement.
Pour mettre en place le déploiement continu, vous devez donc cliquer sur ce bouton, ensuite activer la fonctionnalité.
Une fois cela fait, vous devez aller dans l’onglet Tasks, puis cliquer sur le bouton « + » dans la zone « Agent Phase ». Dans la fenêtre d’ajout des tâches qui va s’afficher, saisissez « Azure Resource » dans le champ de recherche, puis sélectionnez la tâche « Azure Resource Group Deployment » et cliquez sur Add :
Une fois la tâche ajoutée, cliquez sur cette dernière et renseignez les champs suivants :
Azure subscription : votre suscription Azure devrait s’afficher par défaut dans la liste, si vous avez déjà configuré un Endpoint. Sinon, vous devez cliquer sur le bouton « Manage » pour vous rendre dans l’interface de gestion des services et ajouter un nouveau endpoint « Azure Resource Manager Service ». Pour plus de détails, vous pouvez consulter l’article suivant :
Action : cliquez sur le champ et sélectionnez dans la liste déroulante « Create or update resource group ».
Resource group : donnez le nom qui sera utilisé dans Azure pour le groupe de ressource.
Location : sélectionnez l’emplacement ou votre infrastructure sera déployée.
Template location : sélectionnez « Linked artifact ». Ce choix va vous permettre de définir l’emplacement du fichier ARM template qui sera utilisé pour le déploiement.
Template : vous devez renseigner le chemin vers le fichier WebSite.json. Pour cela, il suffit de cliquer sur « … », ensuite sélectionner le répertoire de publication de la Build (drop) et le fichier WebSite.json :
Template parameters : vous devez renseigner le chemin vers le fichier WebSite.parameters.json. Pour le faire, vous devez procéder de même que ci-dessus.
Vous pouvez laisser les valeurs par défaut pour les autres options :
Enregistrez les modifications.
Pour tester que le déploiement se fait correctement, vous pouvez créer une nouvelle release. Il suffit de cliquer sur le bouton Release, puis Create Release. Dans la fenêtre qui va s’afficher, sélectionnez l’environnement Dev, puis l’artefact qui a été généré par notre dernière build, puis cliquez sur Create.
Lorsque la Release sera créée, vous pourrez cliquer sur le nom de celle-ci dans le message de confirmation pour afficher la fenêtre de gestion de cette release. Vous pouvez ainsi dans les logs voir si le déploiement c’est effectué correctement.
Si c’est le cas, vous devez voir votre Resources Group, ainsi que les services qui ont été définis dans le template ARM dans le portail Azure :
Nous disposons désormais d’un pipeline d’Intégration et de Livraison continues pour notre projet ARM template. Chaque fois que notre infrastructure aura besoin d’une mise à jour, il nous suffira d’apporter les modifications dans le projet Azure ARM Template et ensuite procéder à un commit des changements pour que notre infrastructure soit mise à jour.
Pour vérifier cela, nous allons mettre à jour notre template ARM dans Visual Studio, en y ajoutant un compte de stockage Azure :
Code json : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "hostingPlanName": { "type": "string", "minLength": 1 }, "skuName": { "type": "string", "defaultValue": "F1", "allowedValues": [ "F1", "D1", "B1", "B2", "B3", "S1", "S2", "S3", "P1", "P2", "P3", "P4" ], "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" } }, "StaticFilesStorageType": { "type": "string", "defaultValue": "Standard_LRS", "allowedValues": [ "Standard_LRS", "Standard_ZRS", "Standard_GRS", "Standard_RAGRS", "Premium_LRS" ] } }, "variables": { "webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]", "StaticFilesStorageName": "staticfilesstorage44" }, "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'))]" } }, { "apiVersion": "2014-04-01", "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]", "type": "Microsoft.Insights/autoscalesettings", "location": "[resourceGroup().location]", "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", "displayName": "AutoScaleSettings" }, "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" ], "properties": { "profiles": [ { "name": "Default", "capacity": { "minimum": 1, "maximum": 2, "default": 1 }, "rules": [ { "metricTrigger": { "metricName": "CpuPercentage", "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", "timeGrain": "PT1M", "statistic": "Average", "timeWindow": "PT10M", "timeAggregation": "Average", "operator": "GreaterThan", "threshold": 80.0 }, "scaleAction": { "direction": "Increase", "type": "ChangeCount", "value": 1, "cooldown": "PT10M" } }, { "metricTrigger": { "metricName": "CpuPercentage", "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", "timeGrain": "PT1M", "statistic": "Average", "timeWindow": "PT1H", "timeAggregation": "Average", "operator": "LessThan", "threshold": 60.0 }, "scaleAction": { "direction": "Decrease", "type": "ChangeCount", "value": 1, "cooldown": "PT1H" } } ] } ], "enabled": false, "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]", "targetResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" } }, { "apiVersion": "2014-04-01", "name": "[concat('ServerErrors ', variables('webSiteName'))]", "type": "Microsoft.Insights/alertrules", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Web/sites/', variables('webSiteName'))]" ], "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]": "Resource", "displayName": "ServerErrorsAlertRule" }, "properties": { "name": "[concat('ServerErrors ', variables('webSiteName'))]", "description": "[concat(variables('webSiteName'), ' has some server errors, status code 5xx.')]", "isEnabled": false, "condition": { "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", "dataSource": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]", "metricName": "Http5xx" }, "operator": "GreaterThan", "threshold": 0.0, "windowSize": "PT5M" }, "action": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", "sendToServiceOwners": true, "customEmails": [] } } }, { "apiVersion": "2014-04-01", "name": "[concat('ForbiddenRequests ', variables('webSiteName'))]", "type": "Microsoft.Insights/alertrules", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Web/sites/', variables('webSiteName'))]" ], "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]": "Resource", "displayName": "ForbiddenRequestsAlertRule" }, "properties": { "name": "[concat('ForbiddenRequests ', variables('webSiteName'))]", "description": "[concat(variables('webSiteName'), ' has some requests that are forbidden, status code 403.')]", "isEnabled": false, "condition": { "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", "dataSource": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]", "metricName": "Http403" }, "operator": "GreaterThan", "threshold": 0, "windowSize": "PT5M" }, "action": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", "sendToServiceOwners": true, "customEmails": [] } } }, { "apiVersion": "2014-04-01", "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]", "type": "Microsoft.Insights/alertrules", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" ], "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", "displayName": "CPUHighAlertRule" }, "properties": { "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]", "description": "[concat('The average CPU is high across all the instances of ', parameters('hostingPlanName'))]", "isEnabled": false, "condition": { "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", "dataSource": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", "metricName": "CpuPercentage" }, "operator": "GreaterThan", "threshold": 90, "windowSize": "PT15M" }, "action": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", "sendToServiceOwners": true, "customEmails": [] } } }, { "apiVersion": "2014-04-01", "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]", "type": "Microsoft.Insights/alertrules", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" ], "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", "displayName": "LongHttpQueueAlertRule" }, "properties": { "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]", "description": "[concat('The HTTP queue for the instances of ', parameters('hostingPlanName'), ' has a large number of pending requests.')]", "isEnabled": false, "condition": { "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", "dataSource": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", "metricName": "HttpQueueLength" }, "operator": "GreaterThan", "threshold": 100.0, "windowSize": "PT5M" }, "action": { "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", "sendToServiceOwners": true, "customEmails": [] } } }, { "apiVersion": "2014-04-01", "name": "[variables('webSiteName')]", "type": "Microsoft.Insights/components", "location": "East US", "dependsOn": [ "[resourceId('Microsoft.Web/sites/', variables('webSiteName'))]" ], "tags": { "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]": "Resource", "displayName": "AppInsightsComponent" }, "properties": { "applicationId": "[variables('webSiteName')]" } }, { "name": "[variables('StaticFilesStorageName')]", "type": "Microsoft.Storage/storageAccounts", "location": "[resourceGroup().location]", "apiVersion": "2016-01-01", "sku": { "name": "[parameters('StaticFilesStorageType')]" }, "dependsOn": [ ], "tags": { "displayName": "StaticFilesStorage" }, "kind": "Storage" } ] } |
Puis pousser nos changements sur VSTS. De retour sur VSTS, nous verrons que la Build va démarrer automatiquement. Une fois la Build et la Release effectuées, nous pourrons observer les changements dans Azure :
Vous êtes désormais en mesure de mettre en place un pipeline de CI & CD pour déployer un projet Azure ARM sur la plateforme cloud de Microsoft.