Les architectures basées sur les microservices sont l’avenir des applications complexes. Dans ce blog, nous allons nous intéresser à l’un des composants critiques de cette architecture: L’API Gateway (Passerelle API).

Disons que vous essayez de créer un magasin en ligne (web-store). Vous utiliserez probablement plusieurs API pour fournir les actifs et les données nécessaires à la création de chaque page de produits. Les microservices que vous pouvez déployer peuvent inclure : services d’information sur le produit, services de tarification, services de commande, services d’inventaire, etc.

En plus de tous ces services, vous devrez déployer plusieurs versions de ces services pour interagir avec les clients qui communiquent à partir de plates-formes mobiles, de bureau et de navigateurs Web différents. Comment le développeur doit gérer tout ça ? C’est là que les APIs gateway rentrent en jeu.

Pros API Gateway

  • Les Microservices peuvent être dédiés uniquement à l’implémentation de la logique métier. La gestion de l’authentification, la journalisation et la surveillance seront déléguées à l’API Gateway
  • Diriger une requête donnée au microservice adéquat
  • Les clients peuvent obtenir toutes les données en un seul coup (agrégation des résultats de plusieurs services)
  • Exécuter plusieurs versions de la même API simultanément, ce qui permet d’itérer, de tester et de publier rapidement de nouvelles versions

Cons API Gateway

  • Ils peuvent entraîner une dégradation des performances en raison de nombreux événements survenus qu’ils doivent gérer
  • Parfois, ils deviennent des SPOFs
  • La gestion du routage est une surcharge du modèle
  • Trop d’implémentation logique dans cette passerelle entraînera un autre problème de dépendance

Malgré ces arguments qui leur sont opposés, les APIs Gateway continuent de prospérer dans les implémentations. Pour preuve, presque tous les principaux fournisseurs mettent à disposition leurs passerelles. Voici quelques exemples :

  • Azure API Gateway
  • Oracle API Manager
  • AWS API Gateway
  • Tyk API Gateway (open source)
  • Kong API Gateway (open source)
  • Express API Gateway (open source)
ProduitDescriptionDéploiementCode
Azure API GatewayElle fait partie de l’offre cloud Microsoft AzureCloud ou en On promiseSource fermée
Oracle API GatewayProduit par Oracle et étroitement couplé aux services Oracle SOA Suite / SOA CloudCloud SaaS ou On premiseSource fermée
AWS API GatewayElle fait partie de la suite de produits AWSCloudSource fermée
Tyk API GatewayC’est un produit API Gateway open source de nouvelle générationCloud ou On promiseLibre (langage GO)
Kong API GatewayC’est une API Gateway open source basée sur Nginx et OpenRestyOn premiseOpen source (Nginx + Lua)
Express API GatewayC’est un projet open source utilisant Nodejs et ExpressJSOn premiseOpen source (NodeJS)

Dans la continuité de notre série de blogs AWS, nous allons nous intéresser à AWS API Gateway.

AWS API Gateway

AWS propose sa version d’API Gateway. C’est un service managé de proxy d’API, qui permet de créer, publier, surveiller et sécuriser des APIs. vers des services AWS tels que EC2, Lambda, Kinesis, etc.

AWS API Gateway aide les développeurs à gérer les tâches liées aux API comme la gestion du trafic entrant, la restriction du nombre d’appels à une ressource donnée, le contrôle des autorisations et accès (via IAM et Cognito), la surveillance, versionning de l’API, etc. Chaque appel repose sur le concept HTTP « REST », en appelant des ressources créées par un développeur par l’intermédiaire de méthodes HTTP (i.e GET, POST, PUT, DELETE, OPTIONS).

Assez parlé théorie et passons à la pratique. Nous allons découvrir AWS API Gateway au travers d’un cas d’usage simple (architecture ci-dessous). Nous allons utiliser AWS API Gateway pour créer une API et son Endpoint qui doit déclencher (trigger) une fonction lambda en lui envoyant un message POST. Cette fonction va simplement récupérer ce message, le traiter et ensuite le stoker dans une table DynamoDB.

Cas pratique

Création du rôle de la fonction Lambda

Création de la relation d’approbation du rôle

Pour permettre à un mandataire de service, lambda.amazonaws.com dans notre cas, d’endosser le rôle et permettre ainsi d’exécuter notre fonction Lambda, il va falloir commencer par créer ce qu’on appelle une relation d’approbation (Trust Relationship) et ensuite créer le rôle en s’y basant.
Nous pouvons créer un fichier, que nous appelons trust-relationship.json pouvant ressembler à ceci:

{
   'Version':'2012-10-17',
   'Statement': [
      {
         'Effect': 'Allow',
         'Principal': {
           'Service': [
               'lambda.amazonaws.com',
               'edgelambda.amazonaws.com'
            ]
         },
         'Action': 'sts:AssumeRole'
      }
   ]
}

Définir le rôle

Utiliser les informations d’approbation pour définir notre rôle IAM. Pour ce faire, on utilise la commande suivante :

aws iam create-role --role-name myprodcatalog-lambda-role --assume-role-policy-document file://trust-relationship.json

Ce qui donne

{
    'Role': {
        'Path': '/',
        'RoleName': 'myprodcatalog-lambda-role',
        'RoleId': 'AROAISKGWVDB5WESR2SKS',
        'Arn': 'arn:aws:iam::123456789800:role/myprodcatalog-lambda-role',
        'CreateDate': '2019-01-25T09:14:51Z',
        'AssumeRolePolicyDocument': {
            'Version': '2012-10-17',
            'Statement': [
                {
                    'Effect': 'Allow',
                    'Principal': {
                        'Service': [
                            'lambda.amazonaws.com',
                            'edgelambda.amazonaws.com'
                        ]
                    },
                    'Action': 'sts:AssumeRole'
                }
            ]
        }
    }
}

Maintenant qu’on a créé le rôle, on doit définir la stratégie d’accès (Policy) de celui-ci. Ainsi, on va déterminer avec quels services notre fonction lambda va pouvoir communiquer.

Création de la stratégie d’accès

Ouvrir un éditeur et créer un fichier de configuration JSON. Ce fichier indique que notre fonction Lambda est autorisée à communiquer avec CloudWatch (pour envoyer des logs) et avec DynamoDb (afin d’écrire nos messsages POST).

{
    'Version': '2012-10-17',
    'Statement': [
        {
            'Sid': 'logandDatas',
            'Effect': 'Allow',
            'Action': [
                'cloudwatch:*',
                'dynamodb:*'
            ],
            'Resource': '*'
        }
    ]
}
aws iam create-policy --policy-name myprodcatalog-policy --policy-document file://policies_lambda.json
{
    'Policy': {
        'PolicyName': 'myprodcatalog-policy',
        'PolicyId': 'ANPAIR2EEPJMUURQD3CBI',
        'Arn': 'arn:aws:iam::123456789800:policy/myprodcatalog-policy',
        'Path': '/',
        'DefaultVersionId': 'v1',
        'AttachmentCount': 0,
        'PermissionsBoundaryUsageCount': 0,
        'IsAttachable': true,
        'CreateDate': '2019-01-25T09:27:40Z',
        'UpdateDate': '2019-01-25T09:27:40Z'
    }
}

Rattacher la stratégie d’accès au rôle

Maintenant que notre stratégie est créée, nous pouvons récupérer son ARN (Amazon Resource Name) pour pouvoir rattacher cette stratégie à notre rôle. Voici la commande à lancer:

aws iam attach-role-policy -- --policy-arn arn:aws:iam::123456789800:policy/myprodcatalog-policy --role-name myprodcatalog-lambda-role

Si aucun message d’erreur ne s’affiche, on peut demander la liste des policies rattachées au rôle pour s’assurer que le rattachement a été bien fait :

aws iam list-attached-role-policies --role-name myprodcatalog-lambda-role
{
    'AttachedPolicies': [
        {
            'PolicyName': 'myprodcatalog-policy',
            'PolicyArn': 'arn:aws:iam::123456789800:policy/myprodcatalog-policy'
        }
    ]
}

Créer la fonction Lambda

import json
import boto3
dynamodb = boto3.resource('dynamodb')
tableProduct = dynamodb.Table('ProductCatalog')
def lambda_handler(event, context):
    tableProduct.put_item(Item=event)
    return {'code':200, 'message':'Produit ajoute'}

Pour créer la fonction lambda avec AWS CLI, nous devons indiquer quelques paramètres:

  • nom de la région : eu-west-1 pour l’Irlande
  • nom du package zip : Il suffit de créer un fichier zip à partir de la fonction Lambda (lambda_function.py)
  • nom du handler: voir votre fonction Lambda
  • nom et version du runtime : Dans notre cas, on a utilisé python3.7 pour définir le corp de la fonction lambda, mais il possible d’utiliser d’autres runtimes (Java, Node.Js, Ruby, C#, Go, PowerShell à date)
aws lambda create-function \
--region eu-west-1 \ 
--function-name AddProduct \
--zip-file fileb://lambda_function.zip \
--role arn:aws:iam::123456789800:role/myprodcatalog-lambda-role --handler lambda_function.lambda_handler \ 
--runtime python3.7
{
    FunctionName: AddProduct,
    FunctionArn: arn:aws:lambda:eu-west-1:123456789800:function:AddProduct,
    Runtime: python3.7,
    Role: arn:aws:iam::123456789800:role/myprodcatalog-lambda-role,
    Handler: lambda_function.lambda_handler,
    CodeSize: 342,
    Description: ,
    Timeout: 3,
    MemorySize: 128,
    LastModified: 2019-01-25T14:34:24.665+0000,
    CodeSha256: DpjG2LWxOcgt5C50tpe12WFRjEP/BEPujfvBqwd0Qbg=,
    Version: $LATEST,
    TracingConfig: {
        Mode: PassThrough
    },
    RevisionId: 8f4f2ac4-d790-47dc-9605-fa8f9852b0fa
}

Créer la table dynamodb

aws dynamodb create-table \
--table-name ProductCatalog --attribute-definitions \
  AttributeName=idProd,AttributeType=S AttributeName=ProductCategory,AttributeType=S \
--key-schema AttributeName=idProd,KeyType=HASH AttributeName=ProductCategory,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
C:\Travail\tools\serverless\blogApiGatewaygt;aws dynamodb create-table --table-name ProductCatalog --attribute-definitions AttributeName=idProd,AttributeType=S AttributeName=ProductCategory,AttributeType=S --key-schema AttributeName=idProd,KeyType=HASH AttributeName=ProductCategory,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
{
    TableDescription: {
            AttributeDefinitions: [
            {
                AttributeName: ProductCategory,
                AttributeType: S
            },
            {
                AttributeName: idProd,
                AttributeType: S
            }
        ],
        TableName: ProductCatalog,
        KeySchema: [
            {
                AttributeName: idProd,
                KeyType: HASH
            },
            {
                AttributeName: ProductCategory,
                KeyType: RANGE
            }
        ],
        TableStatus: CREATING,
        CreationDateTime: 1548232357.28,
        ProvisionedThroughput: {
            NumberOfDecreasesToday: 0,
            ReadCapacityUnits: 1,
            WriteCapacityUnits: 1
        },
        TableSizeBytes: 0,
        ItemCount: 0,
        TableArn: arn:aws:dynamodb:eu-west-1:123456789800:table/ProductCatalog,
       TableId: d063eb88-702b-4f3b-a77f-588ae8e5be1a
    }
}

Créer l’Api REST en utilisant l’API Gateway

aws apigateway create-rest-api --name prodcatalog-api --region eu-west-1
{
    id: ntc3qlrlvh,
    name: prodcatalog-api,
    createdDate: 1548427095,
    apiKeySource: HEADER,
    endpointConfiguration: {
        types: [
            EDGE
        ]
    }
}

Afficher les ressources de l’API

aws apigateway get-resources --rest-api-id ntc3qlrlvh --region eu-west-1

Ajouter la ressource «Product» à l’API REST

aws apigateway create-resource --rest-api-id ntc3qlrlvh --parent-id apgmkc7a82 --path-part Product
{
    id: 3a2321,
    parentId: apgmkc7a82,
    pathPart: Product,
    path: /Product
}

Créer la méthode HTTP POST

Nous voulons maintenant ajouter une méthode POST à notre ressource Product qu’on vient de créer. Pour ce faire, nous aurons besoin de l’id de L’API REST et de l’id de la ressource Product.

aws apigateway put-method --rest-api-id ntc3qlrlvh --resource-id 3a2321 --http-method POST --authorization-type NONE --region eu-west-1
{
    httpMethod: POST,
    authorizationType: NONE,
    apiKeyRequired: false
}

Intégrer l’API Gateway et la fonction Lambda

Il s’agit de définir notre fonction Lambda comme destination de la méthode POST
Note- La syntaxe est la suivante :

aws apigateway put-integration --rest-api-id $API --resource-id $RESOURCE \
--http-method POST --type AWS --integration-http-method POST \
--uri arn:aws:apigateway:$REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$REGION:$ACCOUNT:function:LambdaFunctionOverHttps/invocations
{
    type: AWS,
    httpMethod: POST,
    uri: arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:123456789800:function:AddProduct/invocations,
    passthroughBehavior: WHEN_NO_MATCH,
    timeoutInMillis: 29000,
    cacheNamespace: 3a2321,
    cacheKeyParameters: []
}

Autoriser l’API Gateway à appeler la fonction Lambda

aws lambda add-permission --region eu-west-1 \
--function-name AddProduct --statement-id apigateway-statement-1 
--action lambda:InvokeFunction --principal apigateway.amazonaws.com \
--source-arn arn:aws:execute-api:eu-west-1:123456789800:ntc3qlrl*vh//POST/Product*
{
Statement: {\Sid\:\apigateway-statement-1\,\Effect\:\Allow\,\Principal\:{\Service\:\apigateway.amazonaws.com\},\Action\:\lambda:InvokeFunction\,\Resource\:\arn:aws:lambda:eu-west-1:123456789800:function:AddProduct\,\Condition\:{\ArnLike\:{\AWS:SourceArn\:\arn:aws:execute-api:eu-west-1:123456789800:ntc3qlrlvh/*/POST/Product\}}}
}

Configurer l’API

Ajouter une « Method Response » au POST

Une « Method Response » d’API encapsule le résultat d’appel d’une méthode d’API que le client va recevoir. Afin de formater le résultat de la méthode POST en JSON, on utilise la configuration suivante :

aws apigateway put-method-response \
--rest-api-id ntc3qlrlvh --resource-id 3a2321 \
--http-method POST --status-code 200 \
--response-models application/json=Empty
{
    statusCode: 200,
    responseModels: {
        application/json: Empty
    }
}

Pour approfondir les concepts de Method Response:

Ajouter une « Integration Response » au POST

C’est ici qu’on peut définir des paramètres de mappages entre la réponse renvoyée par le backend et celle devant être renvoyée par l’API Gateway.
Dans notre cas, on souhaite garder le format JSON avec les mêmes résultats renvoyés par la fonction Lambda.

aws apigateway put-integration-response \
--rest-api-id ntc3qlrlvh --resource-id 3a2321 \
--http-method POST --status-code 200 \
--response-templates application/json=
{
    statusCode: 200,
    responseTemplates: {
        application/json: null
    }
}

Déployer l’API

Dans cette dernière étape, nous allons déployer l’API que nous avons créé dans un environnement appelé dev (on peut créer plusieurs environnements : preprod, prod, test, …Etc)

aws apigateway create-deployment –rest-api-id ntc3qlrlvh –stage-name dev
{
    id: dl9wnx,
    createdDate: 1548433834
}

Tester

Nous allons utiliser PostMan pour effectuer le bon fonctionnement.

Et si on regarde du côté de notre table Dynamodb, on trouve que le produit qu’on vient de poster est bien ajouté.

Pour aller plus loin

  • Enrichir l’API en ajoutant d’autres méthodes: Recherche, suppression et mise à jour de produit (GET, DELETE, PUT)
  • Sécuriser l’API en utilisant une clé API par exemple.
  • Dépolyer rapidement les développements en utilisant un framework dédié tel que CloudFormation, Serverless Framework, Terraform, etc.

0 commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.