AWS Lambda en action. Partie 2: se familiariser avec les outils de développement et de test



Ce guide est le résultat d'une expérience personnelle dans le développement et le test d'applications sans serveur, ainsi que dans les manœuvres entre béquilles et vélos lorsque vous essayez de les tester. Lorsque j'ai commencé à développer des applications sans serveur, j'ai dû tout comprendre avec mes mains, il n'y avait pas de guides clairs ou d'outils pratiques pour le développement local et les tests.

Maintenant, la situation est en train de changer: des outils ont commencé à apparaître, mais trouver des manuels sur leur utilisation n'est pas facile. Dans cet article, je vais montrer avec un exemple simple comment travailler avec la nouvelle fonctionnalité. Vous pouvez facilement le maîtriser et contourner les problèmes et les bugs que j'ai eu la «chance» de rencontrer.

Vous apprendrez à développer à l'aide de la console de navigation AWS, SAM-CLI et IntelljIDEA. Je parlerai également des tests: intégration, E2E et tests unitaires. Et enfin, nous discuterons du coût d'une telle solution (spoiler: vous pouvez économiser beaucoup).

Cet article sera utile à ceux qui commencent à développer des applications sans serveur et ne connaissent pas encore ses outils et ses approches.


Ce qui sera discuté


Bonjour à tous! Je m'appelle Alexander Gruzdev et je suis heureux de vous présenter la deuxième partie de l'article sur l'utilisation et le développement de solutions sans serveur basées sur AWS Lambda. Dans la suite, nous parlerons des façons de développer et de tester la solution à partir de la première partie ( AWS Lambda en action dans Java 11. Nous visitons Serverless dans «Production» ).

Depuis la publication du dernier article, l'application de test a été légèrement repensée, mais la signification principale reste la même. Il s'agit d'un formulaire HTML Contactez-nous trivial qui envoie des données du formulaire à l'API de passerelle via des requêtes HTTP, et qui, à son tour, procède à la proxy de la demande de traitement à AWS Lambda. Pendant le traitement, le lambda écrit des données dans la table DynamoDB et envoie une lettre via AWS SES. Le diagramme des composants est présenté ci-dessous.

Diagramme des composants


Contenu


  1. Développement à l'aide de la console du navigateur AWS
  2. Développement à l'aide de SAM-CLI
  3. Développement avec IntelljIDEA
  4. Types de tests
  5. Coûts sans serveur
  6. Liens utiles



1. Développement à l'aide de la console du navigateur AWS



Console AWS


La console AWS permet d'accéder à n'importe quel service AWS, qu'il s'agisse de votre serveur EC2 virtuel, des paramètres de stratégie d'accès / rôle IAM ou AWS Lambda avec tous les paramètres qui peuvent être modifiés en temps réel sans redéploiement via les systèmes de livraison continue / déploiement continu.

Cela accélère considérablement le processus de développement et certains types de tests. Il permet également de contrôler visuellement les paramètres de lancement et de fonctionnement de vos applications.

Console API Gateway


La console vous permet de surveiller l'état actuel de l'application, de gérer vos environnements et de proposer de nouvelles versions. Je vais montrer comment cela peut être utilisé pour vérifier la santé de l'environnement.

Voyons à quoi ressemble cette console «magique» dans la capture d'écran de l'onglet Ressources de l'application Contactez-nous: La capture d'écran montre comment la demande du client via l'API de passerelle entre dans le lambda et comment la réponse retourne au client lorsque la ressource POST est appelée / contact.

Ressources de passerelle



  • Demande de méthode - première étape du traitement de la demande. Dans notre cas, il ne contient aucun paramètre, mais peut contenir, par exemple, des paramètres d'autorisation.
  • La demande d'intégration signifie que la demande HTTP sera transmise au lambda dans un format spécial: elle contient toutes les informations envoyées par le client et des détails sur le contexte appelé.
  • La réponse d'intégration indique que le client HTTP recevra la réponse directement du lambda. Par conséquent, vous êtes vous-même responsable de la formation du corps de réponse dans lambda.


Désactiver l'intégration du proxy
API Gateway , Use Lambda Proxy integration Integration Request. . , , .


Exemple de demande d'intégration
SAM init events, , Integration request. , AWS , . AWS Lambda.


Appuyez sur le bouton TEST pour vérifier que l'application fonctionne. Cela vous donnera accès aux appels de l'API HTTP Gateway.

Dans la capture d'écran suivante, j'ai envoyé une demande pour réchauffer le lambda: en plus de répondre à la demande, l'API Gateway affiche des journaux détaillés. De la même manière, vous pouvez faire une vraie demande en remplaçant le corps de la demande JSON sous la forme:
Demande de test de passerelle


{
    "subject": "Email subject",
    "question": "Your question…",
    "username": "Your name…",
    "phone": "Your phone…",
    "email": "Your email…"
} 


Vous pouvez donc tester manuellement votre API sans formulaires d'interface utilisateur ni applications frontales, séparant ainsi le développement frontal et le développement principal. Comme alternative à la console intégrée, vous pouvez utiliser Postman ou tout autre client HTTP.

Facteur comme alternative
HTTP- Postman. . , API Gateway AWS , API. , , AWS .


Console Lambda


Pour les tests et le développement, en plus des demandes via l'API Gateway, vous pouvez utiliser la console du navigateur AWS Lambda.

Son écran est divisé en plusieurs blocs:
  • Designer affiche les déclencheurs et les calques existants. Dans notre cas, le déclencheur est API Gateway. Si nous cliquons dessus, toutes les informations nécessaires s'ouvriront: URL, étape, chemin d'accès aux ressources.
  • Les alias (disponibles lors de la sélection d'un alias lambda spécifique) vous permettent de configurer la répartition du trafic par version de lambda. Plus d'informations à ce sujet dans le paragraphe suivant.
  • Function code — . zip- , . , Function code . Python/NodeJs.
  • Environment variables. , . , , . , . , , .
  • Tags — , , , , .
  • Execution role — ( ) , .
  • Basic settings — .
  • Network: - , VPC. — .
  • AWS X-Ray . AWS-.
  • Reserve concurrency , . .
    Provisioned concurrency
  • Provisioned concurrency — . , . , — . Free Tier .
  • L'appel asynchrone vous permet de configurer un traitement spécial des demandes asynchrones. Nous indiquons le nombre d'appels répétés à effectuer en cas d'erreur et pendant combien de temps la demande peut se bloquer dans la file d'attente. Facultativement, vous pouvez spécifier une file d'attente pour le traitement des demandes qui échouent.

  • Les proxys de base de données sont une autre nouvelle fonctionnalité qui vous permet de configurer l' accès à la base de données via des proxys sous la forme d'API Gateway + Lambda.


Voyons ensuite comment développer à partir de cette console. Outre la modification des paramètres, la fonctionnalité Configurer les événements de test peut être utilisée pour créer une demande de test. Une liste assez complète est disponible, mais nous profiterons de ce que j'ai déjà joint dans le projet:
Demande de contact
{
  "body": "{\"subject\": \"Question\",\"question\": \"How much does it cost\",\"username\": \"Alex\",\"phone\": \"+79999999999\",\"email\": \"alex@gmail.com\"}",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": false,
  "queryStringParameters": {
    "foo": "bar"
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}


Après avoir ajouté la demande, cliquez sur Test et voyez le résultat de lambda: Lambda renvoie JSON, qui contient le code d'état et le corps de la réponse. La valeur du corps ira directement au client qui a appelé l'API de passerelle. De plus, la capture d'écran présente les caractéristiques de l'appel: le temps d'exécution, le temps d'exécution pour lequel nous allons payer, la quantité maximale de mémoire qui a été consommée par le lambda. En nous appuyant sur ces mesures, nous pouvons configurer la quantité optimale de mémoire afin que le lambda corresponde à vos exigences de prix / taux de réponse.
Ressources de passerelle



Utilisation de la console AWS pour tester le déploiement Canary


Abordons maintenant un point important lié au déploiement d'une nouvelle version de lambda. J'ai spécifié de tels paramètres lorsque j'ai écrit le modèle SAM:
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

Le premier paramètre ajoute automatiquement un alias en direct à chaque nouvelle version du lambda .
Un peu sur les alias
, . , , API Gateway. 1, 2, 3 . LATEST, . 2 3 API Gateway (Stage AWS), Gateway-Lambda. API Gateway (ARN ), LATEST.

— ( ), , , ARN . , Stage API Gateway . , API Gateway: dev, qa prod .

, , . , . , live, . live- , , , , CloudWatch.


Pour tester le déploiement de Canary, vous pouvez utiliser à la fois la console Gateway API et la console Lambda.

Vous devez d'abord modifier le code lambda afin qu'il renvoie une réponse différente. Par exemple, dans le traitement de préchauffage, spécifiez la version 2 au lieu de la version 1 dans la réponse. Ce sera notre marqueur.

Maintenant, en utilisant la fonctionnalité API du test de passerelle, nous pouvons nous assurer que dans les dix minutes lors de l'envoi des demandes WARM-UP, seulement 10% des réponses auront la version 2 dans le corps de la réponse. Passé ce délai, la version 2 renverra 100% des demandes.

En plus de la console Gateway API, nous pouvons utiliser la console Lambda, où, après avoir sélectionné l'alias nécessaire, nous voyons une politique de distribution du trafic qui peut être contrôlée.
Alias ​​Lambda



2. Développement local avec SAM-CLI


Il y a quelque temps, l'un des principaux inconvénients de l'utilisation de lambda était considéré comme un inconvénient lors du développement et des tests sur la machine locale. Maintenant, la situation a beaucoup changé: le framework SAM est apparu, ce qui permet non seulement de collecter et de déployer des solutions, mais simplifie également le développement local.

Par exemple, vous pouvez appeler votre lambda directement depuis la console:
sam local invoke -e ./events/warm_up_request.json ContactUsFunction

Cette commande est lancée à partir du répertoire avec le modèle SAM et transfère le contenu du fichier JSON vers l'entrée lambda ContactUsFunction (il s'agit du nom logique dans le modèle).

C'est-à-dire qu'en utilisant cette commande, SAM lève l'image lambci / lambda: java11 dans docker et y exécute votre code. Pour accéder à des services distants, tels que SES, votre configuration AWS avec une clé secrète / d'accès est utilisée, elle doit donc être à jour. Autre point important: si vous n'ajoutez pas d'en-tête pour le mode de préchauffage, les vrais services AWS SES et DynamoDB seront appelés.

Journal d'appels ici
F:\aws\projects\contact-us-sam-app>sam local invoke -e ./events/warm_up_request.json ContactUsFunction
Invoking com.gralll.sam.App::handleRequest (java11)
Fetching lambci/lambda:java11 Docker container image......
Mounting F:\aws\projects\contact-us-sam-app\.aws-sam\build\ContactUsFunction as /var/task:ro,delegated inside runtime container
?[32mSTART RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2 Version: $LATEST?[0m
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 INFO  App - Request was received
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 DEBUG App - {
  "body" : "{\"subject\": \"Question\",\"question\": \"How much does it cost\",\"username\": \"Alex\",\"phone\": \"+79999999999\",\"email\": \"alex@gmail.com\"}",
  "resource" : "/{proxy+}",
  "requestContext" : {
    "resourceId" : "123456",
    "apiId" : "1234567890",
    "resourcePath" : "/{proxy+}",
    "httpMethod" : "POST",
    "requestId" : "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "extendedRequestId" : null,
    "accountId" : "123456789012",
    "identity" : {
      "apiKey" : null,
      "apiKeyId" : null,
      "userArn" : null,
      "cognitoAuthenticationType" : null,
      "caller" : null,
      "userAgent" : "Custom User Agent String",
      "user" : null,
      "cognitoIdentityPoolId" : null,
      "cognitoIdentityId" : null,
      "cognitoAuthenticationProvider" : null,
      "sourceIp" : "127.0.0.1",
      "accountId" : null,
      "accessKey" : null
    },
    "authorizer" : null,
    "stage" : "prod",
    "path" : "/prod/path/to/resource",
    "protocol" : "HTTP/1.1",
    "requestTime" : "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch" : 1428582896000,
    "elb" : null
  },
  "multiValueQueryStringParameters" : { },
  "multiValueHeaders" : {
    "X-WARM-UP" : [ "13" ]
  },
  "pathParameters" : {
    "proxy" : "/path/to/resource"
  },
  "httpMethod" : "POST",
  "stageVariables" : {
    "baz" : "qux"
  },
  "path" : "/path/to/resource",
  "isBase64Encoded" : false,
  "requestSource" : "API_GATEWAY"
}
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 INFO  App - Lambda was warmed up
?[32mEND RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2?[0m
?[32mREPORT RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2     Init Duration: 1984.55 ms       Duration: 42.22 ms      Billed Duration: 100 ms Memory Size: 256 MB     Max Memory Used: 102 MB ?[0m
{"statusCode":201,"multiValueHeaders":{"Access-Control-Allow-Origin":["*"]},"body":{"response":"Lambda was warmed up. V1"},"isBase64Encoded":false}


En plus du journal lambda, des informations de service sur l'état de l'appel et une réponse au format JSON sont écrites sur la console.

L'API de passerelle, comme lambda, peut être exécutée localement.
Nous exécutons la commande:
sam local start-api

Journal de la console au démarrage
F:\aws\projects\contact-us-sam-app>sam local start-api
Mounting ContactUsFunction at http://127.0.0.1:3000/contact [POST, OPTIONS]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-01-06 22:20:30  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)


Après avoir démarré le serveur, nous pouvons exécuter des requêtes. J'utiliserai Postman et enverrai une demande pour réchauffer le lambda.

Dans la console, nous voyons
Invoking com.gralll.sam.App::handleRequest (java11)
Fetching lambci/lambda:java11 Docker container image......
Mounting F:\aws\projects\contact-us-sam-app\.aws-sam\build\ContactUsFunction as /var/task:ro,delegated inside runtime container
?[32mSTART RequestId: 27868750-3637-1f80-3d80-53a724da16ef Version: $LATEST?[0m
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef INFO  App - Request was received
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef DEBUG App - {
  "body" : "{\r\n  \"subject\": \"Question\",\r\n  \"question\": \"How much does it cost\",\r\n  \"username\": \"Alex\",\r\n  \"phone\": \"+79999999999\",\r\n  \"email\": \"alex@gmail.com\"\r\n}",
  "resource" : "/contact",
  "requestContext" : {
    "resourceId" : "123456",
    "apiId" : "1234567890",
    "resourcePath" : "/contact",
    "httpMethod" : "POST",
    "requestId" : "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "extendedRequestId" : null,
    "accountId" : "123456789012",
    "identity" : {
      "apiKey" : null,
      "apiKeyId" : null,
      "userArn" : null,
      "cognitoAuthenticationType" : null,
      "caller" : null,
      "userAgent" : "Custom User Agent String",
      "user" : null,
      "cognitoIdentityPoolId" : null,
      "cognitoIdentityId" : null,
      "cognitoAuthenticationProvider" : null,
      "sourceIp" : "127.0.0.1",
      "accountId" : null,
      "accessKey" : null
    },
    "authorizer" : null,
    "stage" : "Prod",
    "path" : "/contact",
    "protocol" : null,
    "requestTime" : null,
    "requestTimeEpoch" : 0,
    "elb" : null
  },
  "multiValueQueryStringParameters" : null,
  "multiValueHeaders" : {
    "Accept" : [ "application/json" ],
    "Accept-Encoding" : [ "gzip, deflate, br" ],
    "Accept-Language" : [ "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7" ],
    "Cache-Control" : [ "no-cache" ],
    "Connection" : [ "keep-alive" ],
    "Content-Length" : [ "150" ],
    "Content-Type" : [ "text/plain;charset=UTF-8" ],
    "Host" : [ "127.0.0.1:3000" ],
    "Origin" : [ "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop" ],
    "Postman-Token" : [ "5cadaccd-6fae-5bc2-b4ef-63900a1725ff" ],
    "Sec-Fetch-Mode" : [ "cors" ],
    "Sec-Fetch-Site" : [ "cross-site" ],
    "User-Agent" : [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" ],
    "X-Forwarded-Port" : [ "3000" ],
    "X-Forwarded-Proto" : [ "http" ],
    "X-Warm-Up" : [ "123" ]
  },
  "pathParameters" : null,
  "httpMethod" : "POST",
  "stageVariables" : null,
  "path" : "/contact",
  "isBase64Encoded" : false,
  "requestSource" : "API_GATEWAY"
}
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef INFO  App - Lambda was warmed up
?[32mEND RequestId: 27868750-3637-1f80-3d80-53a724da16ef?[0m
?[32mREPORT RequestId: 27868750-3637-1f80-3d80-53a724da16ef     Init Duration: 1951.87 ms       Duration: 42.91 ms      Billed Duration: 100 ms Memory Size: 256 MB     Max Memory Used: 102 MB ?[0m
2020-01-06 22:28:11 127.0.0.1 - - [06/Jan/2020 22:28:11] "POST /contact HTTP/1.1" 201 -


Les journaux, bien sûr, ne sont pas formatés de la meilleure façon. J'espère que cela sera corrigé à l'avenir.

Important: lors du changement du code lambda, le redémarrage de l'API n'est pas nécessaire. Il suffit de reconstruire le lambda avec la commande sam build .
Un peu de bugs
Java- aws-serverless-java-container-core, : 502 . :
Invalid API Gateway Response Keys: {'base64Encoded'} in {'statusCode': 200, 'headers': {'Content-Type': 'text/plain'}, 'body': 'Hello World', 'base64Encoded': False} 

- , API Gateway AWS JsonProperty. 'isBase64Encoded' 'base64Encoded', , , . , Java- ContactUsProxyResponse AwsProxyRequest.



3. Appels locaux d'IDEA


Tout ce qui est décrit dans le paragraphe ci-dessus est la fonctionnalité de l'outil SAM. Il est contrôlé depuis la ligne de commande et peut être intégré dans n'importe quelle interface. C'est exactement ce qui a été fait dans le plugin pour IntelljIDEA AWS Toolkit.

Ce plugin ajoute des configurations supplémentaires pour lancer un lambda directement depuis l'IDE. Voici à quoi ressemble la fenêtre de configuration. Vous devez soit sélectionner un modèle avec une description du lambda, soit configurer les paramètres manuellement. Dans le premier cas, la quantité de mémoire et de langue pour la compilation sera automatiquement prise à partir du modèle. Dans le second cas, vous pouvez configurer vous-même la quantité de RAM et effectuer un calcul approximatif de la limite. Un autre plugin vous permet d'exécuter la configuration directement à partir de la fenêtre de l'éditeur de code Java ou du modèle yaml. Captures d'écran ci-dessous:











En fait, c'est tout. En exécutant la configuration personnalisée, vous automatisez simplement les appels lambda fréquents à partir de la console. D'accord, assez confortable?


4. Test


Ici, nous parlons des types de tests pour les lambdas. Puisque j'ai utilisé Java dans l'exemple, les tests incluront également des outils assez standard pour les tests unitaires et d'intégration: Junit 4, Mockito et PowerMockito.

Tests unitaires


Pour couvrir le code lambda avec des tests unitaires, vous n'avez pas besoin d'avoir des connaissances spécifiques. Il suffit d'appliquer des pratiques Java standard: verrouillez toutes les dépendances de la classe et essayez de tester tous les cas possibles de traitement des requêtes.

Je n'ai pas ajouté tous les scénarios de test et je me suis limité à deux scénarios positifs et deux négatifs.

Le premier test positif vérifie que s'il existe un en-tête X-WARM-UP, il n'y a aucune demande dans DbService et EmailService. Le second cas vérifie que ces services seront appelés si la demande est réelle. Ce sont les tests les plus simples et j'ai omis une partie des vérifications.

Les scénarios négatifs sont une vérification des réponses en cas d'erreur de traitement client ou serveur.

Tests d'intégration


En ce qui concerne les tests d'intégration, j'ai décidé de vérifier DbService et son travail avec la table DynamoDB.

Dans ce cas, j'ai utilisé l'outil Localstack . Cette solution fournit des mokas gratuits pour les services AWS qui s'exécutent en tant que conteneurs Docker.

Pour installer cette solution, exécutez simplement les commandes:
pip install localstack
localstack start

Ou vous pouvez utiliser le fichier docker-compose .
docker-compose pour lever uniquement DynamoDB
version: '2.1'
services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    ports:
      - "4567-4599:4567-4599"
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
    environment:
      - SERVICES=dynamodb
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"


La commande start déclenche tous les services AWS disponibles par défaut. Pour augmenter DynamoDB spécifiquement, il suffit de définir la variable d'environnement:
set SERVICES=dynamodb


Il existe un LocalstackTestRunner spécial pour Junit qui vous permet de démarrer les services nécessaires à l'aide de la configuration @LocalstackDockerProperties .
Par conséquent, l'écriture d'un test pour DbService ressemble à ceci:
  • Ajoutez la classe de test.
    @RunWith(LocalstackTestRunner.class)
    @LocalstackDockerProperties(services = { "dynamodb" })
    

  • Créer une table
    Java
    @Before
    public void setUp() {
        AmazonDynamoDB clientDynamoDB = TestUtils.getClientDynamoDB();
        dynamoDB = new DynamoDB(clientDynamoDB);
        dbService = new DbService(dynamoDB);
    
        dynamoDB.createTable(
                new CreateTableRequest()
                        .withTableName("ContactUsTable")
                        .withKeySchema(new KeySchemaElement("Id", KeyType.HASH))
                        .withAttributeDefinitions(new AttributeDefinition("Id", ScalarAttributeType.S))
                        .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)));
    }
                    


  • Nous décrivons le scénario de test
    Java
    // given
    ContactUsRequest contactUsRequest = new ContactUsRequest("subject", "name", "+79991234545", "123@mail.ru", "Qeustion");
    
    // when
    dbService.putContactUsRequest("123", contactUsRequest);
    
    // then
    Item item = dynamoDB.getTable("ContactUsTable").getItem(new PrimaryKey("Id", "123"));
    assertEquals(contactUsRequest.getSubject(), item.get("Subject"));
    assertEquals(contactUsRequest.getUsername(), item.get("Username"));
    assertEquals(contactUsRequest.getPhone(), item.get("Phone"));
    assertEquals(contactUsRequest.getEmail(), item.get("Email"));
    assertEquals(contactUsRequest.getQuestion(), item.get("Question"));
                  


  • N'oubliez pas de supprimer le tableau après le test.
    Java
    @After
    public void tearDown() {
        dynamoDB.getTable("ContactUsTable").delete();
    }
                   



Par conséquent, en utilisant Localstack, nous obtenons une gamme assez étendue de simulations AWS qui vous permettent d'émuler le travail de ces services.

Tests E2E


Pour jouer aux scénarios E2E, il est préférable d'augmenter les services de test AWS spécifiquement pour l'exécution de tests, si, bien sûr, il existe une telle opportunité. Cela peut être fait en utilisant le modèle CloudFormation facultatif, dans lequel les ressources seront des copies du modèle principal.

Le test peut être effectué en appelant le lambda via AWS-CLI et en traitant le résultat avec n'importe quel outil disponible. Vous pouvez toujours faire un appel dans les tests unitaires, mais sans créer de mobs.

Des tests de performance


Étant donné que les tests de performances méritent un article séparé ou même un livre, je n'essaierai pas de mettre toutes les informations ici. En bref, lorsque vous parlez de tests de résistance, vous devez vous rappeler que l'utilisation de presque tous les services AWS n'est pas gratuite et vous devez être prêt à estimer les coûts possibles à l'avance.

Si vous souhaitez évaluer les ressources nécessaires pour un lambda, utilisez la console d'interface utilisateur et effectuez des appels de test. Ensuite, à l'aide d'une logique et de mathématiques courantes, évaluez l'augmentation des performances de votre lambda en fonction de la quantité de ressources disponibles.

N'oubliez pas CloudWatch. Avec son aide, vous pouvez créer des tableaux de bord assez indicatifs et en fonction de ceux-ci déterminer les paramètres de lambda et les dépenses que vous pouvez vous permettre.


5. Le coût des solutions sans serveur


L'évaluation d'une solution est toujours spécifique, car vous devez prendre en compte non seulement le coût d'un appel, mais également le trafic entrant / sortant, le coût de la mise en cache, de la journalisation et de la surveillance. Si vous envisagez d'utiliser lambda, essayez de prédire la charge que votre service va gérer.

S'il s'agit d'appels uniques ou d'un processus ETL qui s'exécute toutes les quelques heures / jours, le lambda est plus susceptible de porter ses fruits. Si vous prévoyez une charge de plusieurs requêtes par seconde, assurez-vous de faire une estimation précise.

Ci-dessous, je donne des estimations approximatives du coût du lambda et du serveur EC2 le moins cher, afin que vous compreniez quelles valeurs sont en question: Les paramètres suivants pour le lambda ont été pris pour calculer:




  • Délai: 150 millisecondes
  • Prix: 0,00001616 $ pour 1 Go-s
  • Mémoire: 256 Mo

Pour EC2 t2.nano:

  • Prix: 4,75 $ par mois
  • Mémoire: 512 Mo
  • vCPU: 1
  • Charge maximale: 5% CPU par mois

J'ai pris les données de charge EC2 d'ici . D'après ces calculs, le serveur spécifié peut supporter une charge constante de 0,3 requêtes par seconde. À mesure que la charge augmente, le temps de réponse augmentera, ce qui réduira l'efficacité de ce serveur.

Tous ces calculs sont assez abstraits et ne prennent pas en compte de nombreux facteurs: remises sur un acompte ou un grand nombre de demandes, délai d'exécution lambda, etc. Mais ils montrent qu'avec une charge inférieure à 1 demande par seconde, lambda coûte moins cher que le serveur EC2 le moins cher.

Soit dit en passant, n'oubliez pas d'ajouter le prix de l'ALB au prix de l'EC2 si vous devez conserver plusieurs répliques d'applications ou IP élastiques.


Les tests et le développement local de solutions sans serveur sont moins populaires que les mêmes microservices. Néanmoins, de plus en plus de personnes étudient et appliquent ces technologies et donnent la chance à divers produits open source de devenir meilleurs et plus efficaces. De telles solutions simplifient considérablement la vie des entreprises qui doivent publier un produit dès que possible et ne pas avoir à configurer l'ensemble de l'infrastructure. Mais même dans les produits déjà développés, je pense qu'il y a une place pour les bases de données sans serveur ou les lambdas.

Je ne prétends pas être un expert dans le domaine des tests de solutions sans serveur, mais, sur la base de mon expérience personnelle positive, j'ai décidé de souligner les principaux points de cet article. J'espère qu'elles seront utiles à quelqu'un, et peut-être que certains d'entre vous pourront me compléter ou même me corriger, ce dont je serai très heureux.


Liens utiles



All Articles