Voir le flux RSS

Blog de Hinault Romaric (.NET Core, ASP.NET Core, Azure, DevOps)

[Actualité] DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS

Noter ce billet
par , 11/02/2018 à 20h57 (829 Affichages)
Ce billet est la quatrième partie de ma série consacrée à DevOps avec la plateforme Cloud Microsoft Azure.


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 » :

Nom : img00.png
Affichages : 1254
Taille : 114,5 Ko

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 - Visualiser dans une fenêtre à part
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 - Visualiser dans une fenêtre à part
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.

Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Viadeo Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Twitter Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Google Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Facebook Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Digg Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Delicious Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog MySpace Envoyer le billet « DevOPS avec Azure – Partie 4 : ARM Template CI & CD avec VSTS » dans le blog Yahoo

Mis à jour 13/02/2018 à 09h53 par ClaudeLELOUP

Tags: azure, devops, vsts
Catégories
Intégration continue , Déploiement continu , Microsoft Azure , Azure , DevOps

Commentaires