Introduction
Pour isoler de l’internet une ressource Azure une des méthodes recommandées est de l’héberger dans un réseau privé virtuel. Dans notre cas, ce sera une App Service. Ce qui nous permettra de la protéger des accès non désirés et de réduire la surface d’attaque. Elle reste cependant accessible aux ressources déployées au sein de ce réseau privé, cela peut être utile si l’on souhaite exposer notre API via API management ou alors une gateway « maison » afin d’obliger les consommateurs à passer par ces portes d’entrée pour accéder à notre API. Mais un problème subsiste : comment déployer via une pipeline Azure Dev Ops les sources de notre API si celle-ci n’est plus accessible par internet ?
Dans ce cas, il faut déployer un agent Dev Ops dans le réseau privé pour déployer nos sources. Nous pouvons faire cela en utilisant une machine virtuelle, mais en plus du temps de configuration de la VM cela va engendrer des coûts non négligeables. Les VM dans Azure sont assez couteuses et leur intégration dans un réseau privé engendre un surcoût assez élevé. Ça ne vaut pas le coup pour un besoin ponctuel. Nous allons voir dans cet article comment déployer un agent Dev Ops conteneurisé dans notre réseau privé avec Terraform. Le fait de passer par un conteneur Docker nous permet de réduire les frais de gestion comparé à une VM. De plus, cela nous permet d’améliorer l’élasticité de notre solution car cet agent ne sera actif que le temps du déploiement, afin de minimiser les coûts d’infrastructure au maximum.
Configuration du réseau virtuel
Dans notre réseau cible nous allons créer un sous réseau dédié à l’agent DevOps. Les APIs seront quant à elles dans leur propre sous-réseau. A noter que pour héberger une App Service dans un réseau privé virtuel il faut mettre en place des Private Endpoints (uniquement disponibles sur des App Service Plan premium et supérieur). Pour déployer notre agent, nous allons utiliser un Azure Container Instance. Pour se faire, il faut déléguer notre sous réseau « devops » au service « container groups ». Cette délégation permet au sous-réseau d’être géré par le service Azure qui déploie les containeur Docker.
Il nous faut aussi créer une zone privée DNS. Cela permet à notre agent Dev Ops de pouvoir accéder à l’App Service via une url. Cela est nécessaire pour le déploiement des sources. Dans notre cas la zone DNS se nomme « privatelink ». Nous allons créer deux enregistrement DNS de type Alias. Un pour l’API déployée et un pour le service de déploiement des sources. Les enregistrements doivent être nommé comme suit :
- ‘nom app service’.scm : Pour le gestionnaire de code source intégré.
- ‘nom app service’ : Pour l’API en elle-même.
Et voilà notre réseau privé est prêt pour les déploiements.
Création de l’agent Dev Ops conteneurisé
Pour créer l’agent nous allons utiliser une image docker disponible dans le registre publique docker. Vous pouvez cependant en créer une vous-même via le fichier docker accessible ici. L’image utilisée se base sur ce fichier. Ce que l’image docker va faire au démarrage, c’est se connecter à votre projet Azure Dev Ops, puis télécharger la définition d’un agent via l’API Dev Ops et ensuite lancer sa configuration. Afin de réaliser ces différentes tâches il nous faut un « Personal Access Token » pour que l’image docker puisse interagir avec notre Azure Dev Ops. Pour se faire on va dans nos paramètres utilisateur sur le portail Azure Dev Ops et on crée un PAT qui peut gérer les pools d’agent. Microsoft vient de mettre à disposition une API permettant de gérer ces tokens. Ce qui pourrait nous permettre de créer un PAT temporaire afin de maximiser la sécurité de notre pipeline, cela serait un bon axe d’amélioration de notre solution.
Il faut ensuite créer un pool d’agents dédiés dans le portail Azure Dev Ops, ce qui nous permettra, lors de la création de la pipeline de release, de lancer un job sur notre agent déployé dans le réseau virtuel. On va dans les paramètres de l’organisation Dev Ops et dans la partie Pipeline on ajoute notre pool qui doit être de type Self-hosted. Au démarrage l’agent Dev Ops ira s’inscrire automatiquement dans ce pool.
Nous allons maintenant mettre en place notre script Terraform pour déployer notre agent. Passer par un script Terraform va nous permettre de créer l’agent à la volée et de le détruire une fois l’API déployée. Pour nous faciliter la tâche, Microsoft à créé un module Terraform permettant de déployer un agent Dev Ops via un Azure Container Instance (ACI).
provider "azurerm" { skip_provider_registration = "true" features { } } module "aci-devops-agent" { source = "Azure/aci-devops-agent/azurerm" version = "0.9.2" resource_group_name = var.resource_group_name location = var.location enable_vnet_integration = true create_resource_group = false vnet_resource_group_name = var.vnet_resource_group_name vnet_name = var.vnet_name subnet_name = var.aci_subnet_name linux_agents_configuration = { agent_name_prefix = "linux-agent" agent_pool_name = var.devops_pool_name count = 1 docker_image = "jcorioland/aci-devops-agent" docker_tag = "0.2-linux" cpu = 1 memory = 4 user_assigned_identity_ids = [] use_system_assigned_identity = false } azure_devops_org_name = var.devops_org_name azure_devops_personal_access_token = var.devops_pat }
Nous déployons un ACI linux car pour le moment, seul les containers linux peuvent être intégrés à un réseau virtuel. On passe à notre script le PAT généré. À noter une petite subtilité ici, le PAT doit être fournis en base64 au format suivant « :personal_access_token ». Il nous faut aussi le nom de l’organisation Dev Ops. Il est disponible sur l’onglet « Overview » des paramètres de votre organisation Dev Ops.
On passe aussi au module Terraform les spécificités techniques de notre ACI, pas besoin d’avoir un container avec énormément de ressources, un CPU et 4 GB de mémoire sont largement suffisants. Nous lui définissons aussi un nom, dans notre cas il est statique, c’est-à-dire le suffixe puis le numéro de l’agent (linux-agent-0). Pour gérer le parallélisme des builds, il peut être pertinent de dynamiser le préfix pour par exemple, intégrer le numéro de release, afin de rendre le nom d’agent unique. Une autre solution serait de ne pas créer, puis détruire l’agent à la volée pour chaque build, mais dans ce cas il faut bien prendre en compte les coûts d’infrastructure.
Tous ces paramètres sont définis dans le fichier de variable Terraform utilisé par défaut « variables.auto.tfvars ». Ce fichier sera ensuite transformé dans la pipeline de release. Les valeurs seront injectées à partir d’un groupe de variables définis pour cette pipeline qui pourront être différentes en fonction de l’environnement de déploiement.
Nous pouvons maintenant passer à la création de notre pipeline de release.
Industrialisation
Nous allons maintenant créer notre Pipeline de release qui va déployer notre agent puis l’utiliser pour déployer une API .net dans notre App Service hébergé dans un réseau virtuel prive. Pour finir elle détruira l’agent Dev Ops.
Notre pipeline de release est découpée en trois jobs. Les jobs « Deploy VNet / Destroy VNet » sont lancés sur la pool d’agent par défaut, tandis que le job qui déploie l’API est lancé sur la pool que nous avons créé précédemment. Nous avons utilisé des groupes de tâches pour les déploiements Terraform afin de clarifier notre release. Voici le détail de la tâche « Terraform Deploy » :
Dans la première étape, nous remplaçons les tokens dans notre fichier « variables.auto.tfvars » par ceux définis dans un groupe de variables. Puis nous utilisons les tâches Terraform disponible dans Azure Dev Ops pour exécuter le flux classique du déploiement d’un plan Terraform : Initialisation > Validation du plan > Application du plan. La tâche Terraform Destroy est similaire sauf que dans la dernière étape, nous exécutons la commande apply avec le paramètre « -destroy », ce qui, au lieu de déployer les ressources définies dans le plan, les supprime à la place. Si vous souhaitez en savoir plus sur Terraform et son industrialisation, vous pouvez vous référez à cette série d’articles.
Pour le déploiement de notre API, nous utilisons la tâche par défaut disponible dans Azure Dev Ops pour déployer du code sur un App Service. Ici rien de particulier, on la configure comme si notre App Service était accessible, ce qui est le cas mais juste pour l’agent Dev Ops qui vient d’être déployé.
Le dernier job détruit l’agent Dev Ops déployé afin de libérer les ressources et de réduire les coûts d’infrastructure, à noter que ce job est exécuté même si les jobs précédents sont en échec, afin de toujours libérer les ressources, même si une erreur de déploiement a eu lieu par exemple.
Et voilà, nous avons une pipeline de release Dev Ops complète pour déployer du code sur un App Service isolé de l’internet par un réseau privé virtuel, en exploitant l’efficacité du l’infra as code Terraform et sa composition (grâce au registre de modules Terraform), afin de minimiser l’impact sur notre facture Azure.
0 commentaires