L'automatisation dans Azure Kubernetes Services avec Azure DevOps
Tags: kubernetes, devops, azure
Published: 2021-02-10 14:42:40


Dans cet article nous allons parler d’industrialisation avec un cluster [Azure Kubernetes Services](https://thomasrannou.azurewebsites.net/2019/07/10/introduction-a-azure-kubernetes-service/). Nous verrons comment :
- Automatiser le déploiement d'un cluster avec Terraform
- Automatiser le déploiement d’une application dans ce même cluster.

Let's go.

# Automatiser le déploiement d’un AKS avec Terraform et Azure DevOps !

Dans cette première partie, je vous propose de configurer un pipeline Azure DevOps pour exécuter un script Terraform afin de provisionner un cluster Azure Kubernetes Services (AKS) !

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vf07y491akl0zgpuu42e.jpg)

## Terraform
 
Terraform est un outil d’Infrastructure As Code qui permet de décrire les ressources (VM, VNet, Web App, cluster Kubernetes, base de données) à provisionner dans un environnement auquel on se connecte au moyen d’un provider.

Il peut s’agir d’un environnement Cloud comme Azure, AWS, GCP ou un environnement on premise virtualisé avec VMWare par exemple. L’infrastructure est décrite grâce au langage de configuration HCL (Hashicorp Configuration Language).

Pour une approche globale de [Terraform](https://thomasrannou.azurewebsites.net/2020/04/16/terraform-pour-azure/) avec Azure je vous renvoie à cette très bonne [vidéo](https://www.youtube.com/watch?v=2Bh4NfLCslw) (et sa [suite](https://www.youtube.com/watch?v=YJsfVS6BZB4)) réalisée par [Stanislas Quastana](https://stanislas.io).

## Azure DevOps
Les élements présentés ici sont tous disponible dans ce répo [GitHub](https://github.com/thomasrannou/TerraformAKS).

### Importer les fichiers

Dans mon projet Azure DevOps fraichement [créé](https://www.youtube.com/watch?v=IJrs_eIGNX8), onglet Repos j’importe les 6 fichiers disponibles :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/awrh8pha5nhts1zahwwo.PNG)

Nous avons à disposition les fichiers suivants :
- outputs.tf : permet de spécifier les informations renvoyées par Terraform à l’issu du déploiement
- provider.tf : spécifie le provider cible : ici azurerm, permettra à Terraform de comprendre qu’il cible un déploiement dans le cloud Microsoft
- variables.tf : la définition des variables utilisées dans le script
- variables.tfvars : l’affectation de ces même variables
- main.tf : le script Terraform en lui-même

Voyons maintenant les parties importantes de ces scripts pour notre automatisation.

### Les variables

Dans le fichier .tfvars nous avons ceci :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2766rnyk1eb7ic9680bn.PNG)
 
On set les variables avec des valeurs assez abstraites pour le moment, ces variables seront renseignées à l’exécution du pipeline.

### Le main.tf

Ce script contient un certain nombre de choses.
La description du Backend pour le stockage du tfstate :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tlwfd1nrnr4nz0f0m2gd.PNG)
 
La description du ressource group qui contiendra nos ressources Azure :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qrptdooxd1rp6lxex783.PNG)
 
La définition d’un Azure Container Registry :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dykmg2b8fl3926fvrfp5.PNG)
 
La définition du cluster Azure Kubernetes Services :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1fg3hbc5d3c3nqiu8ku0.PNG)
 
On remarque dans ce script l’utilisation des variable définies dans le tfvars au moyen de : ${var.nomDeLaVariable} et la relation entre les composants. Par exemple le resource_group_name et la location du cluster référence le resource group précédemment créé.

On notera également la configuration du [node pool](https://thomasrannou.azurewebsites.net/2020/01/28/les-pools-de-noeuds-dans-aks/) ou on précise que l’on utilisera des VM de type D2_V2 déployés dans 3 Availability Zone pour un cluster [hautement disponible](https://thomasrannou.azurewebsites.net/2020/10/23/gerer-la-haute-disponibilite-dun-cluster-aks/). Ce node pool est déployé en se basant sur le mécanisme Azure des VMSS.

J’ai également configuré un backend qui cible un Storage Account dans Azure. Cet espace de stockage héberge un conteneur : terraform-azure-kubernetes-tfstate.
Cet espace, je l’ai créé depuis le portail Azure. Il est de type Storage V2 et est en accessibilité publique. Mon conteneur est lui en accessibilité “Objet Blob”. On peut donc accéder à une ressource précise mais il n’est pas possible de lister toutes les ressources présentes dans le conteneur.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/p0ofaipf8ashvaa17uoa.PNG)
 
La documentation complète pour créer un Azure Storage Account est [ici](https://docs.microsoft.com/fr-fr/azure/storage/common/storage-account-create?tabs=azure-portal) !

### Service Connection

Avant de créer notre pipeline Azure DevOps nous aller configurer un service connection à notre tenant Azure. Pour cela dans les Project settings sélectionner l’onglet Service Connections.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/956l8apwewkrhsl8cbsx.PNG)
 
Puis ajouter votre service connection. Vous devrez renseigner les informations suivantes :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dmzdcqrf7rii20jn70x7.PNG)
 
Pour créer un [service principal](https://docs.microsoft.com/fr-fr/cli/azure/create-an-azure-service-principal-azure-cli) vous pouvez utiliser cette commande dans une fenêtre Powershell ou le shell du portail Azure :
```powershell
az ad sp create-for-rbac --name spTerraformAzDo
```

Pour obtenir le tenantId et le subscriptionId vous pouvez utiliser cette commande :
```powershell
az account show
```

### Le pipeline

Il faut maintenant créer notre Pipeline pour exécuter ce script Terraform. Dans l’onglet Pipeline, choisir New Pipeline :
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a1el7bh0rnvr38dyrw4j.PNG)

Mon code est ici stocké dans mon repo Azure DevOps mais il pourrait tout à fait être utilisé directement depuis mon repo GitHub.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bejddthdyji1tvj3pqiu.PNG)
 
Je choisis mon repository local :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4adr8diei2xiclrg08y7.PNG)
 
Ici le pipeline est déjà écrit, il faut donc le réutiliser et sélectionner le fichier yml correspondant, ici nommé sobrement azure-pipelines.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n1cwmtzi1j86y684bic5.PNG)
 
### Les variables du pipeline

Maintenant que notre pipeline est créé et avant de le détailler et de l’exécuter nous allons lui fournir les variables dont il aura besoin à l’exécution. Pour cela, sélectionner Variables :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h7q5w9qj284vhjyanvlo.PNG)
 
Vous allez devoir créer et valoriser l’ensemble de ces variables :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2rkc3m63bl69v2d7hoos.PNG)
 
Ici vous avez dû remarquer que toutes mes variables déclarées ont le même nom que la valeur affectée à la variable Terraform dans mon fichier tfvars modulo les deux underscores positionnés avant et après le nom :

aks-cluster-name = __ aks-cluster-name __

## Le contenu du pipeline

Si on s’intéresse maintenant au pipeline en lui-même on y trouve tout d’abord la copie des fichiers dans un espace de travail temporaire :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wennujgxrtmtke2kc84b.PNG)
 
### Gestion des variables

Etapes très importante ! Ici nous allons parcourir les fichiers tfvars et provider.tf pour y trouver __ positionnées autour d’un nom de variable. Exemple : __ aks-cluster-name __
Quand on en trouve, on remplace le contenu par la valeur renseignée précédemment dans la variable du pipeline :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xf9w0rew4kp2oa36xl9v.PNG)
 
Ainsi __ aks-cluster-name __ devient “lenomdemoncluster”.

On passe ensuite à la récupération de la dernière version de Terraform précisée dans une variable également :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3n6iars884jpy68jmp9i.PNG)
 
Il est maintenant temps de voir les 3 étapes nécessaire à Terraform pour déployer mon cluster.

### Init

Le Init permet de tester la connexion à l’environnement cible, ici ma souscription Azure et mon backend. Ce backend, un storage account Azure, doit, je le rappelle, avoir été créé au préalable.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/czjl3osmktpxxf0rimud.PNG)
 
### Plan
Le plan permet de comparer le contenu décrit dans le script à son infrastructure actuelle (via le tfstate). Ainsi Terraform obtient une liste d’élément à supprimer / modifier / créer.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qiobpljzjuiohix1bdpt.PNG)
 
### Apply
L’étape Apply permet de réaliser le déploiement de notre infrastructure sur Azure. Ici aussi j’ai besoin de passer dans commandOptions le nom de mon fichier de variables.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/05xnjo3ii2coteli84jp.PNG)

(Une étape Terraform Validate peux être présente entre le Init et le Plan pour valider la sémantique du script Terraform).

Une fois mon pipeline enregistré, un build démarre :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lpkxxeiok7ckvgjgn68o.PNG)
 
Le pipeline est en cours d’exécution :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i6kyxzwl28u0dfsk8l46.PNG)
 
Si vous cliquez sur l’étape Deploy vous obtiendrez les détails de l’exécution du traitement :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xjm79r1opjzmf6st6lmg.PNG)
 
Une fois l’exécution achevée, vous pourrez vérifier sur le portail Azure le bon déploiement de votre ressource group contenant un Azure Container Registry et un cluster Azure Kubernetes Services :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ln7w6d01ypk2tk1i7tnm.PNG)

Une fois le script exécuté Terraform m’affichera en vert toutes les informations demandées en output.

Vérifions maintenant notre déploiement !

Je me connecte au cluster via :
```powershell
Az aks get-credentials -–resource-group rg-aks --name clustervacd
```

Et j’exécute la commande ci-dessous pour vérifier que mes nœuds sont bien sur des Availability Zones différentes  :

```powershell
kubectl describe nodes | select-string -pattern '^Name:','zone='
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fed5rm4yhfwjywcdq5z6.PNG)

Dans Azure, si je retourne dans mon espace de stockage je trouve mon fichier tfstate !

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1r45cw1xyme48tnmxwxu.PNG)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4ulzv2mql32egaamkhoc.PNG)
  
Grace à ce state, si je réexécute mon pipeline, il n’y aura pas de redéploiement de mon cluster, puisqu’il existe déjà.
En revanche, si je modifie le node_count dans mon main.tf et que je le positionne à 10 :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fsnlzb370pk847oemgcs.PNG)

Un build se relance automatiquement et je pourrais trouver dans les logs :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7go2vctl0kvtozy1z4h1.PNG)

Ici Terraform a identifié, grace au tfstate, que ma ressource était bien présente (0 to Add) mais qu’il faut la modifier (1 to change).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6k75gzq274lnfw7pe2pm.PNG)

Pendant le build, mon node pool se verra appliquer la modification demandée et passera automatiquement de 3 à 10 nœuds :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/e2cq4artncqhps3v9wit.PNG)

Voilà pour cette automatisation du déploiement d’un cluster AKS avec Terraform et Azure DevOps ! Terraform nous a ici permis de décrire un cluster AKS hautement disponible grâce aux Availability Zone.

On passe maintenant à la configuration d’un pipeline d’intégration / déploiement continu d’une application sur ce cluster fraichement créé !

# Configurer un pipeline CI/CD Azure DevOps pour déployer dans AKS

Nous allons déployer sur notre cluster AKS une [application](https://thomasrannou.azurewebsites.net/2020/05/24/templates-pour-deployer-sur-aks/) hébergée sur GitHub toujours grâce à Azure DevOps !

## Prérequis

Nous avons pour le moment créé :
- Un Azure Container Registry pour stocker l’image Docker générée
- Un cluster Azure Kubernetes Services pour hoster mon application

On peut passer à la partie applicative !

Je crée, tout d’abord, un nouveau projet Azure DevOps :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rqcah0jmprlbon1iw091.PNG)
 
Mon code étant hébergé sur GitHub, je peux directement passer à la création du pipeline dans l’onglet Pipeline :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hcuxckotjn2594fckgh6.PNG)
 
J’indique que mon code est sur GitHub, il faudra que vous vous identifiez sur votre compte quand Azure DevOps vous le demandera :

 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l9dkgyqi95jn5jbf41jd.PNG)

Je choisis le repository  concerné :
https://github.com/thomasrannou/DemoAppAzDo 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/awgk2gv2rziaa5z9phal.PNG)

Cette fois-ci je ne vais pas immédiatement lancer de build (Run). Je choisis donc de sauvegarder mon pipeline en l’état (Save) : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ckt2vb3yqkssxcm5r6cr.PNG)

Avant de l’exécuter, j’ai besoin d’ajouter 2 services connexion à mon projet Azure DevOps, une connexion à mon Container Registry Azure et une seconde à mon cluster AKS. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qag29skudyq9ejavj9v7.PNG)

Cela va se passer dans les “project settings”, onglet “Service Connections”. J’ajoute dans un premier temps une “Docker Registry service connection” que je nomme acr-connexion:
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r2dpahzd71jp7al7y17v.PNG)

Vous devez ici choisir votre souscription Azure et le container registry que vous venez de créer précédemment. De même pour établir une connexion avec le cluster AKS (aks-connexion) : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lqguhykvz1p1rwvp26y6.PNG)

De retour sur mon pipeline, on peut maintenant ajouter les variables globales (bouton Variables) :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kf9p22hcseof9lqpp4rp.PNG)
 
J’ai besoin d’une variable “containerRegistry” avec l’url de ma registry précédemment créée. L’url de votre registry se présente comme ceci : nomdelaregistry suivi de azurecr.io.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sc9z34if9cpoqe0pwpug.PNG)
 
Ainsi que le nom de l’image Docker “imageRepository”, ici valorisée comme ceci :

 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b8zezon04hzestebb0py.PNG)

Je peux maintenant exécuter mon pipeline (Run) :  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rxk8r7cy3p9lvw4kx8qe.PNG)

J’ai un indicateur visuel sur mon déploiement :
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jrxjbclka4lftdszj6n7.PNG)

Une fois le build terminé, un :
```powershell
kubectl get pods
```
m’indique que 5 pods exécutent mon application. Via un :
```powershell
kubectl get services 
```

je vois également que celle-ci est accessible via une external-ip : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ke7yvrg7hqe46tb8um0z.PNG)

### Description du pipeline

On va maintenant passer en revue le pipeline que nous venons d’exécuter pour comprendre comment on en arrive à ce résultat !

Tout d’abord, on définit les variables de traitement :
- Le nom de ma service connexion à mon Container Registry (acr-connexion)
- Le path du dockerfile (présent sur GitHub)
- Un tag automatiquement généré par le build
- Le nom de mon pull secret, nous y reviendrons.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/esr9aalxjz5h01emdc8t.PNG)

Première étape : le push sur ma registry, je dois ici renseigner que je veux effectuer un Docker « build and push » avec en paramètres :
- le nom de mon repository (applicationnetcore)
- le path du Dockerfile de mon projet
- la connexion à utiliser pour atteindre mon registry
- le tag généré précédemment

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/11c8p83zp2mow3unmyew.PNG)

### Déploiement

Le déploiement sur mon cluster Kubernetes sera réalisée grâce aux deux fichiers yml présent dans le dossier manifest de mon repository. On trouve un deployment.yml et un service.yml.
  
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/swi8l7thijjeiif5k1dv.PNG)

En vue de mon installation, je dois modifier (dans mon pipeline) le fichier Deployment.yml pour  valoriser :
image: #{containerRegistry}#/#{imageRepository}#:#{tag}#

Pour ce faire, je vais utiliser l’instruction replacetokens. Je précise ici que je veux analyser les fichier .yml pour y trouver des chaines de caractère telles que #{texte}# et les remplacer par une valeur concrète renseignée dans les variables du pipeline (globales comme locales, les deux fonctionnent) :

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vne8fyoba7sythudg4lx.PNG)

Après cette étape, le contenu de mon fichier de travail deployment.yml sera par exemple :

image: registryacrdemo.azurecr.io/applicationnetcore:25

Kubernetes exige ensuite un secret pour que mon cluster puisse récupérer l’image sur mon container registry : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h23ybhehwdhh5u55u6zr.PNG)

J’utilise enfin mes deux fichiers yml pour déployer mon application et un service pour l’exposer sur le port 80.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b1svhm3n57khtqeifz96.PNG)

## Modification du projet

Mon projet déployé ressemble à ceci : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/p6gb2qby0gy17knsll5j.PNG)

Si  je modifie la page pour ajouter une image, je peux créer une nouvelle branche :  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jdylbk6nd95xgyi81u36.PNG)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r0m3c1h2inpabcqhaqg8.PNG)

Et déclencher une pull request : 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/89kzfbre4xim9nefdz4p.PNG)

Qui, une fois validée, va lancer un nouveau build coté Azure DevOps :  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wedk92zztmgfphu839tg.PNG)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oq1bjgr2fkv4hxk83htk.PNG)

Mon application se met alors automatiquement à jour via un [rolling update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/) Kubernetes !  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ivb8lhpymbcbsp17fojo.PNG)

C'est la fin de cette mise en œuvre de pipeline Azure DevOps pour automatiser la gestion de mon cluster AKS ainsi que le déploiement d’applications !

J’espère que cet article vous aura été utile !
A bientôt.

Thomas