Dans le développement moderne de logiciels, les micro-services se sont imposés comme une architecture centrale, permettant l’évolutivité, la flexibilité et la gestion efficace de systèmes complexes.

Les micro-services sont de petites applications indépendantes qui exécutent des tâches spécifiques, ce qui permet un déploiement et une mise à l’échelle flexibles. Cette approche modulaire de la conception logicielle relâche le couplage entre les composants, ce qui améliore la flexibilité et la gestion tout au long du développement.

L’article présente une vue d’ensemble des micro-services, de leurs fonctionnalités et de leur création à l’aide de Python. Il montre également comment déployer vos micro-services sur Kinsta à l’aide d’un fichier Docker.

Que sont les micro-services ?

Les micro-services sont des services indépendants et autonomes au sein d’une application, chacun répondant à des besoins professionnels spécifiques. Ils communiquent par le biais d’API légères ou de courtiers de messages, formant ainsi un système complet.

Contrairement aux systèmes monolithiques qui évoluent entièrement en fonction de la demande, les micro-services permettent de faire évoluer les composants individuels à fort trafic. Cette architecture facilite la gestion des pannes et la mise à jour des fonctionnalités, ce qui va à l’encontre des limites des systèmes monolithiques.

L’utilisation de micro-services présente plusieurs avantages, notamment

  • Flexibilité et évolutivité – Le découplage des services individuels vous permet d’augmenter le nombre de nœuds exécutant une instance d’un service particulier à fort trafic.
  • Modularité du code – Chaque service peut utiliser une pile technologique distincte, ce qui signifie que vous pouvez choisir les meilleurs outils de développement pour chacun d’entre eux.

Cependant, les architectures de micro-services présentent certains défis :

  • Surveillance de services multiples – La surveillance de services individuels dans un système devient difficile lorsque des instances d’un service particulier sont déployées et distribuées sur plusieurs nœuds. Cette difficulté est particulièrement évidente en cas de défaillance du réseau ou d’autres problèmes du système.
  • Coût – Le développement d’applications de micro-services peut être sensiblement plus coûteux que la construction de systèmes monolithiques en raison des coûts associés à la gestion de services multiples. Chaque service nécessite sa propre infrastructure et ses propres ressources, ce qui peut s’avérer coûteux, en particulier lors de l’extension du système.

Comment concevoir un micro-service à l’aide de Python

Maintenant que vous connaissez les avantages de l’utilisation d’une architecture de micro-services, il est temps d’en concevoir une avec Python.

Pour cet exemple, supposons que vous souhaitiez créer une application web de commerce électronique. Le site web comporte plusieurs composants, dont le catalogue de produits, une liste de commandes, un système de traitement des paiements et des journaux, chacun d’entre eux devant être implémenté en tant que service indépendant. En outre, vous devez établir une méthode de communication de service à service pour transférer efficacement les données entre ces services, comme HTTP.

Construisons un microservice utilisant Python pour gérer un catalogue de produits. Le micro-service récupèrera les données des produits à partir d’une source spécifiée et renverra les données au format JSON.

Pré-requis

Pour suivre ce tutoriel, assurez-vous d’avoir :

1. Créer votre projet

  1. Pour commencer, créez un dossier pour votre projet appelé flask-microservice et le répertoire courant dans le répertoire du projet.
  2. Ensuite, exécutez python3 --version pour confirmer que Python est correctement installé sur votre ordinateur.
  3. Installez virtualenv pour créer un environnement de développement isolé pour le micro-service Flask en exécutant la commande ci-dessous :
    pip3 install virtualenv
  4. Créez un environnement virtuel en exécutant la commande suivante :
    virtualenv venv
  5. Enfin, activez l’environnement virtuel à l’aide de l’une des commandes suivantes, en fonction du système d’exploitation de votre ordinateur :
    # Windows: 
    .\venv\Scripts\activate
    # Unix or macOS:
    source venv/bin/activate

2. Configurer un serveur Flask

Dans le répertoire racine, créez un fichier requirements.txt et ajoutez ces dépendances.

flask
requests

Exécutez le fichier pip3 command on your terminal to install the dependencies.

pip install -r requirements.txt

Ensuite, créez un nouveau dossier dans le répertoire racine et nommez-le services. Dans ce dossier, créez un nouveau fichier, products.py, et ajoutez le code ci-dessous pour configurer un serveur Flask.

import requests
import os
from flask import Flask, jsonify
app = Flask(__name__)
port = int(os.environ.get('PORT', 5000))

@app.route("/")
def home():
    return "Hello, this is a Flask Microservice"
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=port)

Dans le code ci-dessus, un serveur Flask de base est mis en place. Il initialise une application Flask, définit une route unique pour l’URL racine ("/") et, lorsqu’on y accède, affiche le message "Hello, this is a Flask Microservice". Le serveur fonctionne sur un port spécifié, obtenu à partir d’une variable d’environnement ou par défaut sur le port 5000, et démarre en mode de débogage, ce qui le rend prêt à traiter les demandes entrantes.

3. Définir les points d’extrémité de l’API

Une fois le serveur configuré, créez un point de terminaison API pour un micro-service qui récupère des données sur les produits à partir d’une API publique. Ajoutez ce code au fichier products.py:

BASE_URL = "https://dummyjson.com"
@app.route('/products', methods=['GET'])
def get_products():
    response = requests.get(f"{BASE_URL}/products")
    if response.status_code != 200:
        return jsonify({'error': response.json()['message']}), response.status_code
    products = []
    for product in response.json()['products']:
        product_data = {
            'id': product['id'],
            'title': product['title'],
            'brand': product['brand'],
            'price': product['price'],
            'description': product['description']
        }
        products.append(product_data)
    return jsonify({'data': products}), 200 if products else 204

Le code ci-dessus crée un point d’extrémité /products dans le serveur Flask. Lorsqu’il est accédé par une requête GET, il récupère des données sur les produits à partir d’une API fictive. En cas de succès, il traite les données récupérées, extrait les détails du produit et renvoie les informations au format JSON. En cas d’erreur ou d’absence de données disponibles, il répond par un message d’erreur et un code d’état appropriés.

4. Tester le micro-service

À ce stade, vous avez réussi à mettre en place un micro-service simple. Pour démarrer le service, démarrez le serveur de développement, qui commencera à fonctionner à l’adresse http://localhost:5000.

flask --app services/products run

Ensuite, vous pouvez faire une requête GET au point de terminaison /products en utilisant le client Postman, vous devriez voir une réponse similaire à la capture d’écran ci-dessous.

Test de la requête HTTP API GET dans Postman.
Test de la requête HTTP API GET dans Postman.

Comment implémenter l’authentification et l’autorisation dans un micro-service Python

Lorsque vous construisez des micro-services, il est important de mettre en œuvre des mesures de sécurité robustes telles que l’authentification et l’autorisation. La sécurisation de votre micro-service garantit que seuls les utilisateurs autorisés peuvent accéder et utiliser le service, protégeant ainsi les données sensibles et empêchant les attaques malveillantes.

Les jetons web JSON (JWT) constituent une méthode efficace pour mettre en œuvre une authentification et une autorisation sécurisées dans les micro-services.

Les JWT sont une norme ouverte largement utilisée qui offre un moyen sûr et efficace de transmettre des informations d’authentification entre les clients et les serveurs. Il s’agit de jetons compacts, cryptés et signés numériquement que vous transmettez avec les requêtes HTTP. Lorsque vous joignez un JWT à chaque demande, le serveur peut rapidement vérifier l’identité et les autorisations de l’utilisateur.

Pour mettre en œuvre l’authentification JWT dans le micro-service, procédez comme suit :

  1. Ajoutez le paquetage Python pyjwt à votre fichier requirements.txt et réinstallez les dépendances à l’aide de pip install -r requirements.txt.
  2. Comme le service n’a pas de base de données dédiée, créez un fichier users.json dans le répertoire racine de votre projet pour stocker une liste d’utilisateurs autorisés. Collez le code ci-dessous dans le fichier :
    [
        {   
            "id": 1,
            "username": "admin",
            "password": "admin"
        
        }
    ]

  3. Ensuite, dans votre fichier services/products.py, remplacez les déclarations d’importation par ce qui suit :
    import requests 
    from flask import Flask, jsonify, request, make_response
    import jwt
    from functools import wraps
    import json
    import os
    from jwt.exceptions import DecodeError

    Vous importez ces modules pour gérer les requêtes HTTP, créer une application Flask, gérer les données JSON, mettre en œuvre l’authentification basée sur JWT et gérer les exceptions, ce qui permet un large éventail de fonctionnalités au sein du serveur Flask.

  4. Ajoutez le code suivant sous la création de l’instance de l’application Flask pour générer une clé secrète qui sera utilisée pour signer les jetons JWT.
    app.config['SECRET_KEY'] = os.urandom(24)
  5. Pour vérifier les JWT, créez une fonction décoratrice et ajoutez le code suivant au-dessus des routes API dans le code de votre serveur Flask. Cette fonction décoratrice authentifiera et validera les utilisateurs avant qu’ils n’accèdent aux routes protégées.
    def token_required(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            token = request.cookies.get('token')
            if not token:
                return jsonify({'error': 'Authorization token is missing'}), 401
            try:
                data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
                current_user_id = data['user_id']
            except DecodeError:
                return jsonify({'error': 'Authorization token is invalid'}), 401
            return f(current_user_id, *args, **kwargs)
        return decorated

    Cette fonction décoratrice vérifie les requêtes HTTP entrantes à la recherche d’un jeton d’autorisation JWT, qui devrait se trouver dans les en-têtes de requête ou les cookies. Si le jeton est manquant ou invalide, le décorateur envoie un message unauthorized status code en réponse.

    Inversement, si un jeton valide est présent, le décorateur extrait l’identifiant de l’utilisateur après l’avoir décodé. Ce processus permet de protéger les points d’accès à l’API en n’accordant l’accès qu’aux utilisateurs autorisés.

  6. Définissez un point d’accès à l’API pour l’authentification de l’utilisateur à l’aide du code ci-dessous.
    with open('users.json', 'r') as f:
        users = json.load(f)
    @app.route('/auth', methods=['POST'])
    def authenticate_user():
        if request.headers['Content-Type'] != 'application/json':
            return jsonify({'error': 'Unsupported Media Type'}), 415
        username = request.json.get('username')
        password = request.json.get('password')
        for user in users:
            if user['username'] == username and user['password'] == password:
                token = jwt.encode({'user_id': user['id']}, app.config['SECRET_KEY'],algorithm="HS256")
                response = make_response(jsonify({'message': 'Authentication successful'}))
                response.set_cookie('token', token)
                return response, 200
        return jsonify({'error': 'Invalid username or password'}), 401

    Pour authentifier et autoriser les utilisateurs, le point de terminaison de l’API /auth vérifie les informations d’identification contenues dans la charge utile JSON de la demande POST par rapport à la liste des utilisateurs autorisés. Si les informations d’identification sont valides, il génère un jeton JWT à l’aide de l’identifiant de l’utilisateur et de la clé secrète de l’application et définit le jeton en tant que cookie dans la réponse. Les utilisateurs peuvent maintenant utiliser ce jeton pour effectuer des demandes d’API ultérieures.

    Après avoir créé le point de terminaison /auth, utilisez Postman pour envoyer une requête HTTP POST à http://localhost:5000/auth. Dans le corps de la requête, incluez les informations d’identification de l’utilisateur administrateur fictif que vous avez créé.

    Requête Postman montrant le corps de la requête.
    Requête Postman montrant le corps de la requête.

    Si la demande aboutit, l’API génère un jeton JWT, le place dans les cookies de Postman et envoie une réponse de réussite authentifiée.

  7. Enfin, mettez à jour le point de terminaison de l’API GET pour vérifier le jeton JWT à l’aide du code ci-dessous :
    @app.route('/products', methods=['GET'])
    @token_required
    def get_products(current_user_id):
        headers = {'Authorization': f'Bearer {request.cookies.get("token")}'}    
        response = requests.get(f"{BASE_URL}/products", headers=headers)
        if response.status_code != 200:
            return jsonify({'error': response.json()['message']}), response.status_code
        products = []
        for product in response.json()['products']:
            product_data = {
                'id': product['id'],
                'title': product['title'],
                'brand': product['brand'],
                'price': product['price'],
                'description': product['description']
            }
            products.append(product_data)
        return jsonify({'data': products}), 200 if products else 204

Comment conteneuriser les micro-services Python avec Docker

Docker est une plateforme qui conditionne les applications et leurs dépendances dans un environnement de développement isolé. L’empaquetage des micro-services dans des conteneurs rationalise leurs processus de déploiement et de gestion dans les serveurs, car chaque service fonctionne et s’exécute indépendamment dans son conteneur.

Pour conteneuriser le micro-service, vous devez créer une image Docker à partir d’un fichier Docker qui spécifie les dépendances nécessaires à l’exécution de l’application dans un conteneur. Créez un fichier Docker dans le répertoire racine de votre projet et ajoutez ces instructions :

FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "./services/products.py"]

Avant de construire l’image, passez en revue les commandes suivantes :

  • FROM – Indique à Docker l’image de base à utiliser. Une image de base est une instance pré-construite contenant le logiciel et les dépendances pour exécuter l’application Flask dans un conteneur.
  • WORKDIR – Définit le répertoire spécifié dans le conteneur comme répertoire de travail.
  • COPY requirements.txt ./ – Copie les dépendances du fichier requirements.txt dans le fichier requirements.txt du conteneur.
  • RUN – Exécute la commande spécifiée pour installer les dépendances requises par l’image.
  • COPY . . – Copie tous les fichiers du répertoire racine du projet dans le répertoire de travail du conteneur.
  • EXPOSE – Spécifie le port sur lequel le conteneur écoutera les requêtes. Cependant, Docker ne publie pas le port sur la machine hôte.
  • CMD – Spécifie la commande par défaut à exécuter au démarrage du conteneur.

Ensuite, ajoutez un fichier .dockerignore dans le répertoire racine de votre projet pour spécifier les fichiers que l’image Docker doit exclure. En limitant le contenu de l’image, vous réduirez sa taille finale et le temps de construction associé.

/venv
/services/__pycache__/
.gitignore

Maintenant, exécutez la commande ci-dessous pour construire l’image Docker :

docker build -t flask-microservice .

Enfin, une fois l’image construite, vous pouvez exécuter le micro-service dans un conteneur Docker en utilisant la commande suivante :

docker run -p 5000:5000 flask-microservice

Cette commande démarrera un conteneur Docker exécutant le micro-service et exposera le port 5000 du conteneur au port 5000 de la machine hôte, ce qui vous permettra d’effectuer des requêtes HTTP à partir de votre navigateur web ou de Postman en utilisant l’URL http://localhost:5000.

Déployer des micro-services Python avec Kinsta

Kinsta offre des solutions d’hébergement gérées pour les applications web et les bases de données – vous pouvez déployer et gérer de manière transparente vos micro-services Python et vos API backend dans un environnement de production.

Suivez ces étapes pour configurer votre micro-service Flask pour le déploiement avec MyKinsta :

  1. Tout d’abord, créez un nouveau Procfile dans le répertoire racine et ajoutez le code ci-dessous. Il spécifie la commande pour exécuter le micro-service Flask sur le serveur WSGI HTTP Gunicorn de Kinsta pour les applications Python.
    web: gunicorn services.wsgi
  2. Dans votre fichier requirements.txt, ajoutez la dépendance Gunicorn :
    gunicorn==20.1.*
  3. Ensuite, créez un nouveau fichier services/wsgi.py et ajoutez le code ci-dessous.
    from services.products import app as application
    if __name__ == "__main__":
                    application.run()
  4. Créez un fichier .gitignore dans le dossier racine du projet et ajoutez ce qui suit :
    services/__pycache__
    venv
  5. Enfin, créez un nouveau dépôt sur GitHub et placez-y les fichiers de votre projet.

Une fois que votre dépôt est prêt, suivez les étapes suivantes pour déployer le micro-service Flask sur Kinsta :

  1. Connectez-vous ou créez un compte pour afficher votre tableau de bord MyKinsta.
  2. Autorisez Kinsta avec votre fournisseur Git (Bitbucket, GitHub ou GitLab).
  3. Cliquez sur Applications dans la colonne latérale gauche, puis sur Ajouter une application.
  4. Sur le tableau de bord, cliquez sur Ajouter un service, et sélectionnez Application.
  5. Sélectionnez le dépôt et la branche à partir desquels vous souhaitez effectuer le déploiement.
  6. Attribuez un nom unique à votre application et choisissez l’emplacement du centre de données.
  7. Pour configurer l’environnement de construction, sélectionnez l’option d’utiliser un fichier Dockerfile pour construire l’image du conteneur.
  8. Indiquez le chemin d’accès à votre fichier Docker et le contexte.
  9. Enfin, passez en revue les autres informations et cliquez sur Créer une application.

Tester le micro-service

Une fois le processus de déploiement réussi, cliquez sur l’URL fournie pour tester le micro-service en effectuant des requêtes HTTP dans Postman. Effectuez une requête GET vers le point de terminaison root.

Faites une requête HTTP API GET au point de terminaison product du micro-service.
Faites une requête HTTP API GET au point de terminaison product du micro-service.

Pour vous authentifier et générer un jeton JWT, envoyez une requête POST au point de terminaison de l’API /auth, en transmettant les informations d’identification de l’administrateur dans le corps de la requête.

Requête HTTP API POST au point de terminaison du micro-service auth.
Requête HTTP API POST au point de terminaison du micro-service auth.

Enfin, après avoir réussi à vous authentifier, envoyez une requête GET au point de terminaison /products pour récupérer les données.

Requête HTTP API GET à un point de terminaison du micro-service products.
Requête HTTP API GET à un point de terminaison du micro-service products.

Résumé

À mesure que les applications gagnent en taille et en complexité, il est essentiel d’adopter des modèles d’architecture qui permettent aux systèmes logiciels d’évoluer sans solliciter les ressources disponibles.

L’architecture micro-services offre évolutivité, souplesse de développement et facilité de maintenance, ce qui vous permet de gérer plus facilement des applications complexes.

Kinsta simplifie le processus d’hébergement de vos micro-services. Il vous permet d’utiliser sans effort votre base de données préférée et d’héberger commodément votre application et votre base de données via un tableau de bord unifié.

Jeremy Holcombe Kinsta

Content & Marketing Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems ;).