Logos de Microsoft Azure et de Python

Déployer une API REST Python sur Microsoft Azure

C’est bien de créer une application. C’est encore mieux de la déployer et de voir qu’elle fonctionne. Voyons comment faire cela avec Microsoft Azure.

Introduction

Au cours des derniers mois, j’ai commencé mon parcours de programmation avec Python, en construisant une API REST qui utilise Flask, SQLAlchemy, Alembic et Docker.

J’ai eu l’opportunité au travail de construire une autre API et à un moment donné, il était temps de déployer le MVP sur Microsoft Azure.

Ci-dessous, vous trouverez les étapes détaillées sans image, car Microsoft déplace souvent les choses et une capture d’écran devient rapidement obsolète…

Je laisse les noms des lames et des onglets en anglais, car j’ai écrit l’article anglais en premier. Ils peuvent également changer, alors soyez patient et regardez bien autour de vous ;)

Pour le nommage des ressources, vous pouvez utiliser le guide officiel à ce sujet.

Configuration du coffre-fort

Créer le Key Vault (coffre-fort de clés)

Via la recherche sur le portail Azure, créez une ressource Key Vault avec :

  • Onglet Basic : son nom doit être kv-[project name]-[env].
  • Onglet Access config : on ne change rien.
  • Onglet Networking : on ne change rien.

Contrôler de l’accès à la Key Vault

Sous le Contrôle d’accès (IAM), vous devrez donner le Role permission suivant :

  • ajouter une attribution de rôle Key Vault Administrator pour pouvoir définir les secrets.

Astuce : ajoutez un membre en tapant l'email complet.

Configurer le Key Vault

Une fois créé, dans la lame Objets, créez chaque secret manuellement.

Le nom de la clé doit être kebab-case.

Configuration du registre des conteneurs

Créer le Container Registry (registre des conteneurs ou IC)

Dans la barre de recherche, tapez « Container Registry » et sélectionnez le type de ressource.

Lors de la configuration, utilisez le plan Basic pour des prix plus bas.

Note

Le pipeline Azure DevOps créera le référentiel lorsque l’IC sera en place.

Configurer le Container Registry

Sous le Access Control (IAM), vous devrez donner les Role permissions suivants :

  • ACR Registry Catalog Lister
  • AcrPull
  • AcrPush

Ces autorisations permettent :

  • de lister les images dans le registre lorsque vous les parcourez sur le portail Azure.
  • de configurer le pipeline Azure pour pouvoir dire au DevOps de pousser les images dans le registre.
  • au Container App, que nous créerons plus tard, de lire les images.

Pipeline Azure

Prérequis pour le Pipeline

Dans Azure, vous devez ajouter à votre compte utilisateur le Application Administrator Role dans Azure AD ou Microsoft Entra ID pour permettre la création du pipeline dans Azure DevOps (ci-après DevOps pour plus succinct).

Si vous n’êtes pas administrateur, vous devrez demander à un administrateur de vous accorder cette permission. Ce n’est pas quelque chose que vous pouvez recevoir comme les autorisations basées sur les rôles.

Vous aurez également besoin d’un DevOps où votre projet réside. L’article ne détaille pas la création du DevOps et suppose que vous en avez un et que vous avez créé un dépôt Git pour stocker le code de votre application.

Configurer le pipeline

Allez sur le DevOps puis la lame Pipelines > Pipelines.

Ensuite,

  • dans l’onglet Connect, sélectionnez Azure Repositories.
  • sur l’onglet Select, sélectionner le référentiel cible.
  • sur l’onglet Configure, sélectionnez le conteneur de registre créé ci-dessus.

Une fois que vous avez confirmé la création du pipeline, ajoutez le tag latest dans le fichier de configuration généré afin que vous puissiez sélectionner ce tag sur le Container App plus tard.

Sinon, vous devrez mettre à jour l’image à déployer dans le Container App après chaque nouvelle version.

Aussi, dans le fichier azure-pipelines.yml généré, vous avez besoin de réaliser quelques modifications pour préparer le déploiement de l’image Docker vers l’App_Container :

  • ajouter une variable projectPath pour définir le chemin du projet :
1
2
3
4
5
6
7
8
variables:
  # Connexion au service de registre des conteneurs établie lors de la création du pipeline
  dockerRegistryServiceConnection: "e5979aa7-383a-4ddb-9aff-6e531f3d023a"
  imageRepository: "my-app"
  containerRegistry: "mycontainerregistry.azurecr.io"
  dockerfilePath: "$(Build.SourcesDirectory)/docker/Dockerfile"
  projectPath: "$(Build.SourcesDirectory)"
  tag: "$(Build.BuildId)"
  • et définir spécifiquement le buildContext pour utiliser projectPath :
1
2
3
4
5
6
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
buildContext: $(projectPath)
containerRegistry: $(dockerRegistryServiceConnection)

Pourquoi ? Si vous avez structuré votre projet avec un Dockerfile dans un sous-dossier docker, vous devez effectuer les étapes ci-dessus pour éviter une erreur sur docker build.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
project
│   config.py
│   requirements.txt
│   run.py
├── app
│   └── some files ...
└── docker
│   └── Dockerfile
│   └── docker-compose.yml
└──

Configuration du Storage Account et des File Share

Créer le Storage Account

Créez bien le Storage Account dans la même zone que toutes les autres ressources.

Il n’y a pas d’instructions spécifiques pour créer la ressource à l’exception du nom qui commence par st et qui n’autorise pas les traits d’union.

Retournez au guide cité dans l’introduction.

Configurer le Storage Account

Sous la lame Data Storage > File shares,

  • Créer un File share. Dans l’application que nous construisions, nous avions besoin de stocker le fichier de la base de données SQLLite. Activez une sauvegarde si nécessaire.
  • Créez un autre File share pour stocker les traces applicatives. Pas besoin de sauvegardes.
  • Créer un autre File share pour les fichiers de configuration et autres fichiers de données que vous devez pouvoir modifier sans mettre à jour le code.

Je nomme mes File shares de la façon suivante : fileshare-[designation]-[project]-[env][designation] correspond soit db pour la base de données, soit logs ou json pour les fichiers que j’ai besoin d’éditer à la volée.

Vous devrez ensuite lier le Container app environment et le Container App à chaque File share.

Configuration du Container App

Prérequis pour le Container App

Vous avez besoin de :

  • Un pipeline existe dans le DevOps avec une image prête.
  • Vous avez besoin d’un rôle Contributor role sur l’abonnement Azure pour votre compte d’utilisateur. Par conséquent, vous devrez peut-être fournir les instructions de création à quelqu’un ayant cette permission si vous ne pouvez pas recevoir ce droit selon la politique de votre organisation.

Créer le Container App

Dans la barre de recherche principale, tapez Container App et sélectionnez le type de ressource.

Dans l’onglet Basics,

  • Configurer le groupe de ressources, le nom et la région
  • Personnaliser le Container Apps Environment (qui peut contenir de nombreux Container Apps) en en créant un nouveau.
    • Cliquez sur New
    • Ensuite, dans l’onglet Basics, donnez un nom selon la convention dans l’introduction et laissez le reste des options avec les valeurs par défaut.
    • Ensuite, dans l’onglet Monitoring, désactivez les logs. L’application Container fournit un flux de logs de console qui peut être utile. Votre application devrait stocker les logs de fichiers dans le File share dédié.
    • Laissez les onglets Workload profiles et Networking tels quels.

Dans l’onglet Container,

  • Sélectionnez la source d’image pour être Azure Container Registry (ACR).
  • Sélectionnez l’ACR que vous avez créé précédemment.
  • Sélectionnez l’image.
  • Sélectionnez le tag latest de l’image.
  • Ajustez le CPU et la mémoire à vos besoins.
  • Vous pouvez configurer les variables d’environnement maintenant, mais nous verrons cela à l’étape Configurer plus loin. De toute façon, il est fort probable que vous ayez besoin de les ajuster au cours de la durée de vie de votre application.

Dans l’onglet Bindings, laissez-le vide.

Dans l’onglet Ingress, vous devrez configurer la partie TCP pour que l’API REST soit accessible via HTTP :

  • Cochez la case Ingress.
  • Sélectionnez Ingress Traffic comme étant Accepting traffic from anywhere.
  • Sélectionnez HTTP pour le Ingress type.
  • Laisser le mode de certificat client par défaut (aucune option sélectionnée)
  • Définir le Target port à 5000, le port par défaut de l’application Flask que nous déployons.
  • Confirmer la création dans l’onglet Review + create.

Note : si l’examen de la configuration échoue avec l’erreur suivante, cela signifie probablement que vous n’avez pas la permission de créer la ressource :

Le client youraccount@example.com avec l’objet id « xxx » n’a pas l’autorisation d’effectuer l’action « Microsoft.App/register/action » sur l’étendue « /subscriptions/yyy » ou l’étendue n’est pas valide. Si l’accès a été récemment accordé, veuillez rafraîchir vos informations d’identification. (Code : AuthorizationFailed) (Code : AuthorizationFailed)

Configurer le Container Apps Environment

Une fois qu’Azure a créé le Container Apps Environment, allez directement dedans pour y lier les File shares.

Pour ce faire, récupérez le nom et la clé d’accès sous le Storage account.

  • Allez dans Security + networking et sélectionnez la lame Access keys.

  • Copier

    • l’une des clés.
    • le nom du Storage account.
    • les noms des File shares créés précédemment (sous la lame File shares).

De retour sur Container Apps Environment :

  • Se rendre dans Settings et dans la lame Azure Files.
  • Définisser le nom avec ce modèle : azure-files-[designation] (designation serait db, logs, etc.)
  • AJouter une nouvelle entrée et remplissez le champ en collant les valeurs copiées précédemment du Storage account.
  • Définisser le Access mode sur Read/Write ou Read only (je le fais pour les fichiers que l’application ne doit pas écrire).
  • Répéter l’opération pour tous les File shares que l’application Container App utilise.

À quoi cela va-t-il servir ? Dans l’API REST, vous pouvez utiliser une base de données SQLLite lorsque la base de données est un fichier et que vous devez la conserver.

Il en va de même pour l’enregistrement des fichiers.

Vous pourriez écrire les fichiers dans l’image du conteneur. Mais, lorsqu’il redémarre lors d’un nouveau déploiement, vous perdrez les données…

Configurer les paramètres de déploiement du Container App

Ensuite, rendez-vous dans Container App pour configurer les paramètres de déploiement.

Pour ce faire, sélectionnez la lame Revisions and replicas sous Application et cliquez sur Creation new revision.

Nous allons configurer l’onglet Container en dernier. Vous comprendrez bientôt pourquoi.

Allez dans l’onglet Scale et ajustez les Min replicas et Max replicas à utiliser en fonction d’une règle d’échelle que vous définissez. Je n’ai pas utilisé de règle dans mon scénario, je vais donc sauter cette étape. J’ai simplement fixé les valeurs min. et max. à 1.

Dans l’onglet Volumes,

  • sélectionnez Azure file volume comme Volume type.
  • Donnez un nom au volume. Par exemple, je nommerais le volume des bases de données databases. Vous aurez besoin d’un volume pour chaque partage de fichiers que vous avez créé.

Note importante : le nom du volume doit correspondre au nom du volume que vous créez dans le fichier Docker. Par exemple, le nom du volume correspond à ce qui suit la valeur WORKDIR dans les commandes VOLUME ci-dessous :

1
2
3
4
5
6
# Définir le répertoire de travail de l'image docker
WORKDIR /project-container

# Créer des points de montage avec des chemins d'accès absolus
VOLUME /project-container/databases
VOLUME /project-container/logs
  • Sélectionner le File share cible (qui est en fait la valeur du Azure file que vous avez créée sous Container App Environment).
  • Définir les options de montage à nobrl si le volume contient un fichier de base de données SQLLite. Pourquoi ? Le problème est que Docker monte le volume en tant que système de fichiers CIFS qui ne peut pas gérer le verrou SQLite3. Voir cette réponse et cette réponse sur Stackoverflow. La documentation Microsoft le confirme également.
  • Assurez-vous de cliquer sur le bouton Add avant de continuer.

Retournez à l’onglet Container pour ajouter les volumes basés sur les partages de fichiers que vous avez créés.

Dans notre exemple, nous devons :

  • laisser la section Détails de la révision telle quelle.
  • Dans la section Image du conteneur, cliquez sur l’image existante.

Un volet droit s’ouvre :

  • sous l’onglet Basics, vous trouverez les détails que vous avez spécifiés lors de la création de la ressource Container App. C’est ici que vous pouvez ajouter vos variables d’environnement (descendre tout en bas). Nous y reviendrons lorsque nous aurons lié le Key Vault au Container App pour en extraire les valeurs secrètes.
  • sous les Health probes, laisser tel quel.
  • sous l’onglet Volumes mounts, ajoutez tous les volumes mounts dont vous avez besoin :
    • le nom du volume sera égal au nom que vous avez défini ci-dessus dans l’onglet Volumes.
    • Le chemin de montage doit être la même valeur que celle que vous avez définie dans le Dockerfile comme je l’ai expliqué ci-dessus.
    • laissez le Sub path vide.
  • Cliquez sur Save
  • Assurez-vous de cliquer sur Create pour lancer le déploiement.

Sous la lame Revisions and replicas, vous devriez voir, au bout de quelques minutes, si le déploiement s’est déroulé avec succès lorsque le Running status est Running et qu’une coche verte est affichée.

Si ce n’est pas le cas, cliquez sur le lien de révision dans la première colonne et cliquez sur Console log stream.

Par moment, les journaux n’apparaissent pas toujours, alors essayez plusieurs fois en rechargeant la page. La consistance d’affiche du flux des traces dans la console est très aléatoire… Parfois, j’ai vu des journaux, et à d’autres moments, il n’y avait rien.

Lier le Key Vault au Container App

Il s’agit d’une condition préalable à la configuration des variables d’environnement.

Tout d’abord, sous la lame Identity de la ressource Container App, activez l’identité assignée par le système en basculant le statut sur On.

Vous avez besoin de cela pour pouvoir fournir l’identité du Container App pour lui assigner une permission basée sur un rôle dans l’IAM du Key Vault.

Ensuite, allez dans la ressource Key Vault et naviguez jusqu’à la lame Access control (IAM).

Cliquez sur Add puis sur Add role assignment.

Sous l’onglet Role, recherchez Key Vault Secrets User et sélectionnez-le.

Sous l’onglet Members, sélectionnez Managed identity puis :

  • Sélectionnez Managed identity.
  • Cliquez sur Select members.
  • Dans le volet de droite qui s’ouvre,
    • Sélectionnez votre abonnement,
    • Sélectionnez l’Managed identity : vous devriez avoir une valeur appelée Container App (1) (1 est le nombre de Container App configuré avec une identité système).
    • Sélectionnez la liste des membres cibles sous Select. Elle devrait afficher un membre avec le nom de votre Container App.
    • Assurez-vous de cliquer sur Select.
    • Terminez par Review + assign.

Configurer les variables d’environnement secrètes du Container App

Pour commencer cette section, vous devrez copier la valeur Vault URI qui se trouve sous la lame Overview de la ressource Key Vault.

Notez également les noms des secrets créés.

Préparez une URL pour chacun d’entre eux avec le format suivant : {vault_uri}/secrets/{nom_du_secret}.

De retour à l’application Container App, naviguez jusqu’à la lame Settings et Secrets.

À partir de là, ajoutez votre secret depuis le Key Vault en cliquant sur le bouton Add. Ensuite :

  • Saisissez la valeur Key avec le nom du secret que vous ajoutez en la préfixant kv_.
  • Définissez le Type sur Référence au coffre-fort de clés.
  • définissez la valeur Value sur l’URL correspondante que vous avez préparée.
  • Cliquez sur Add et attendez que le secret apparaisse.

Attention

Si le secret n’apparaît pas, même si l’Azure Portal donne un retour positif dans les notifications, le problème est que vous n’avez pas terminé correctement l’étape « Linking the Key Vault and the Container App ».

Configurer les variables d’environnement

Une fois que vous avez terminé, créez une nouvelle révision à partir de la lame Applications > Revisions and replicas.

Sélectionnez votre image de conteneur et descendez jusqu’à la section Environment Variables dans le panneau qui s’est ouvert à droite

Premièrement, ajoutez les variables qui ne sont pas secrètes en utilisant la Source égale à Manual entry.

Deuxièmement, ajoutez la variable dont les valeurs proviennent du coffre-fort de la clé en utilisant la Source égale à Reference a secret. Sélectionnez ensuite comme Valeur la référence secrète correspondante.

Assurez-vous de cliquer sur Save et de créer la révision.

Vous avez terminé la configuration ! 🏆

Tester l’API

En utilisant Visual Studio Code et l’extension REST Client, vous pouvez créer un petit fichier pour tester vos points d’extrémité :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
### Créer un événement
POST https://capp-myproject-prod.cravesea-7fd7d8d0b6.myregion.azurecontainerapps.io/event
Content-Type : application/json

{
  "id" : 1234
}

### GET tous les événements
GET https://capp-myproject-prod.cravesea-7fd7d8d0b6.myregion.azurecontainerapps.io/event/all
Content-Type : application/json

Résoudre des problèmes

Impossible d’appeler l’application REST API même si le déploiement est réussi

Si vous obtenez le message suivant lorsque vous appelez votre application alors que le déploiement est réussi et qu’Azure vous indique qu’il est en cours d’exécution :

1
azure upstream connect error or disconnect/reset before headers. retried and the latest reset reason : remote connection failure, transport failure reason : delayed connect error : 111

Assurez-vous d’utiliser un serveur de niveau de production, et non le serveur Flask par défaut.

Pour corriger cela, vous devez :

  • installer le paquet gunicorn : c’est un serveur web de niveau production.

  • configurer le Dockerfile avec la commande suivante

    1
    2
    
    # Commande d'exécution pour démarrer le serveur
    CMD ["gunicorn", "--bind", "0.0.0.0:5000", "run:app"]
    

Pour exécuter la commande ci-dessus, vous avez besoin d’un fichier run.py à la racine qui contient quelque chose comme ceci :

1
2
3
4
5
6
7
8
9
import os

from app import MyApp

config_name = os.getenv("FLASK_CONFIG") or "default"
app = MyApp.create_app(config_name)

if __name__ == '__main__' :
    app.run(host="0.0.0.0", port=5000)

Conclusion

Si vous avez lu jusqu’ici, bravo et merci !

Je continuerai à partager mes expériences Python et Azure au fur et à mesure que je travaille avec eux.

Suivez-moi !

Merci d’avoir lu cet article. Assurez-vous de me suivre sur X, de vous abonner à ma publication Substack et d’ajouter mon blog à vos favoris pour ne pas manquer les prochains articles.

Crédit : Les logos des images d’en-tête proviennent de WorldVectorLogo et SVGRepo. Vous pouvez trouver les images originales ici et : J’ai créé l’image avec Sketchpad de Sketch.io.

Licencié sous CC BY-NC-SA 4.0