AWS Lambda en acción. Parte 2: conocer las herramientas de desarrollo y prueba



Esta guía es el resultado de la experiencia personal en el desarrollo y prueba de aplicaciones sin servidor, así como de maniobras entre muletas y bicicletas al intentar probarlas. Cuando comencé a desarrollar aplicaciones sin servidor, tuve que resolver todo con mis manos, no había guías claras o herramientas convenientes para el desarrollo y las pruebas locales.

Ahora la situación está cambiando: las herramientas comenzaron a aparecer, pero encontrar manuales sobre su uso no es fácil. En este artículo mostraré con un simple ejemplo cómo trabajar con la nueva funcionalidad. Puede dominarlo fácilmente y evitar esos problemas y errores que tuve la "suerte" de encontrar.

Aprenderá a desarrollar utilizando la consola del navegador AWS, SAM-CLI e IntelljIDEA. También hablaré sobre las pruebas: integración, E2E y pruebas unitarias. Y finalmente, discutiremos cuánto costará dicha solución (spoiler: puede ahorrar mucho en ella).

Este artículo será útil para aquellos que comienzan a desarrollar aplicaciones sin servidor y aún no están familiarizados con sus herramientas y enfoques.


Lo que se discutirá


¡Hola a todos! Mi nombre es Alexander Gruzdev y me complace presentar la segunda parte del artículo sobre el uso y el desarrollo de soluciones sin servidor basadas en AWS Lambda. En la secuela, hablaremos sobre las formas de desarrollar y probar la solución desde la primera parte ( AWS Lambda en acción en Java 11. Visitamos Serverless en "Producción" ).

Desde la publicación del último artículo, la aplicación de prueba se ha rediseñado ligeramente, pero el significado principal sigue siendo el mismo. Este es un formulario HTML trivial Contáctenos que envía datos desde el formulario a la API de Gateway a través de solicitudes HTTP, y a su vez envía la solicitud de procesamiento a AWS Lambda. Durante el procesamiento, la lambda escribe datos en la tabla DynamoDB y envía una carta a través de AWS SES. El diagrama de componentes se presenta a continuación.

Diagrama de componentes


Contenido


  1. Desarrollo con la consola del navegador AWS
  2. Desarrollo usando SAM-CLI
  3. Desarrollo usando IntelljIDEA
  4. Tipos de pruebas
  5. Costos sin servidor
  6. Enlaces útiles



1. Desarrollo utilizando la consola del navegador AWS



Consola AWS


La consola de AWS proporciona acceso a cualquier servicio de AWS, ya sea su servidor EC2 virtual, IAM o la configuración de la política de acceso / rol de AWS Lambda con todos los parámetros que se pueden cambiar en tiempo real sin redistribución a través de los sistemas de Entrega continua / Implementación continua.

Esto acelera enormemente el proceso de desarrollo y algunos tipos de pruebas. También ayuda a controlar visualmente los parámetros de inicio y operación de sus aplicaciones.

Consola API Gateway


La consola le permite monitorear el estado actual de la aplicación, administrar sus entornos y entregar nuevas versiones. Mostraré cómo se puede usar esto para verificar la salud del medio ambiente.

Veamos cómo se ve esta consola "mágica" en la captura de pantalla de la pestaña Recursos de la aplicación Contáctenos: La captura de pantalla muestra cómo la solicitud del cliente a través de la API de Gateway ingresa a la lambda y cómo la respuesta será devuelta al cliente cuando el recurso / contact es llamado por el método POST.

Recursos de puerta de enlace



  • Solicitud de método : el primer paso para procesar la solicitud. En nuestro caso, no contiene ninguna configuración, pero puede contener, por ejemplo, configuraciones para la autorización.
  • Solicitud de integración significa que la solicitud HTTP se transmitirá a la lambda en un formato especial: contiene toda la información enviada por el cliente y detalles sobre el contexto llamado.
  • La respuesta de integración dice que el cliente HTTP recibirá la respuesta directamente de lambda. Por lo tanto, usted mismo es responsable de la formación del cuerpo de respuesta en lambda.


Deshabilitar integración de proxy
API Gateway , Use Lambda Proxy integration Integration Request. . , , .


Muestra de solicitud de integración
SAM init events, , Integration request. , AWS , . AWS Lambda.


Presione el botón TEST para verificar que la aplicación esté funcionando. Esto le dará acceso a las llamadas API de HTTP Gateway.

En la siguiente captura de pantalla, envié una solicitud para calentar el lambda: además de responder a la solicitud, la API de Gateway muestra registros detallados. Del mismo modo, puede hacer una solicitud real sustituyendo el cuerpo de la solicitud JSON en el formulario:
Solicitud de prueba de puerta de enlace


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


Por lo tanto, puede probar manualmente su API sin ningún formulario de interfaz de usuario y aplicaciones front-end, separando así el desarrollo front-end y back-end. Como alternativa a la consola integrada, puede usar Postman o cualquier otro cliente HTTP.

Cartero como alternativa
HTTP- Postman. . , API Gateway AWS , API. , , AWS .


Consola Lambda


Para las pruebas y el desarrollo, además de las solicitudes a través de la API de Gateway, puede usar la consola del navegador AWS Lambda.

Su pantalla está dividida en varios bloques:
  • Designer muestra disparadores y capas existentes. En nuestro caso, el desencadenante es API Gateway. Si hacemos clic en él, se abrirá toda la información necesaria: URL, etapa, ruta de recursos.
  • Alias (disponible al seleccionar un alias lambda específico) le permite configurar la distribución del tráfico por versión de lambda. Más sobre esto en el siguiente párrafo.
  • 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 .
  • La invocación asincrónica le permite configurar un manejo especial de solicitudes asincrónicas. Indicamos cuántas llamadas repetidas realizar en caso de error, y durante cuánto tiempo la solicitud puede quedar en la cola. Opcionalmente, puede especificar una cola para procesar las solicitudes que fallan.

  • Los proxies de base de datos es otra característica nueva que le permite configurar el acceso a la base de datos a través de proxies en forma de API Gateway + Lambda.


A continuación, veamos cómo desarrollar desde esta consola. Además de cambiar los parámetros, la funcionalidad Configurar eventos de prueba se puede usar para crear una solicitud de prueba. Hay disponible una lista bastante extensa, pero aprovecharemos lo que ya he adjuntado en el proyecto:
Solicitud de contacto
{
  "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"
  }
}


Después de agregar la solicitud, haga clic en Probar y vea el resultado de lambda: Lambda devuelve JSON, que contiene el código de estado y el cuerpo de respuesta. El valor del cuerpo irá directamente al cliente que llamó a la API de Gateway. Además, la captura de pantalla tiene las características de la llamada: el tiempo de ejecución, el tiempo de ejecución por el que pagaremos, la cantidad máxima de memoria que consumió la lambda. Confiando en estas métricas, podemos configurar la cantidad óptima de memoria para que el lambda coincida con sus requisitos de precio / tasa de respuesta.
Recursos de puerta de enlace



Uso de la consola de AWS para probar la implementación de Canary


Ahora veamos un punto importante relacionado con el despliegue de una nueva versión de lambda. Especifiqué dichos parámetros cuando escribí la plantilla SAM:
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

El primer parámetro agrega automáticamente un alias en vivo a cada nueva versión de lambda .
Un poco sobre 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.


Para probar la implementación canaria, puede usar tanto la consola API de Gateway como la consola Lambda.

Primero debe cambiar el código lambda para que devuelva una respuesta diferente. Por ejemplo, en el proceso de calentamiento, especifique la Versión 2 en lugar de la Versión 1. En la respuesta, este será nuestro marcador.

Ahora, utilizando la funcionalidad API de la Prueba de puerta de enlace, podemos asegurarnos de que dentro de diez minutos al enviar solicitudes de CALENTAMIENTO, solo el 10% de las respuestas tengan la Versión 2 en el cuerpo de la respuesta. Después de este tiempo, la Versión 2 devolverá el 100% de las solicitudes.

Además de la consola API de Gateway, podemos usar la consola Lambda, donde, después de haber seleccionado el alias necesario, vemos una política de distribución de tráfico que se puede controlar.
Alias ​​lambda



2. Desarrollo local utilizando SAM-CLI


Hace algún tiempo, una de las principales desventajas de usar lambda se consideraba un inconveniente en el desarrollo y las pruebas en la máquina local. Ahora la situación ha cambiado mucho: ha aparecido el marco SAM, que permite no solo recopilar e implementar soluciones, sino que también simplifica el desarrollo local.

Por ejemplo, puede llamar a su lambda directamente desde la consola:
sam local invoke -e ./events/warm_up_request.json ContactUsFunction

Este comando se inicia desde el directorio con la plantilla SAM y transfiere el contenido del archivo JSON a la función lambda ContactUsFunction (este es el nombre lógico en la plantilla).

Es decir, con este comando, SAM levanta la imagen lambci / lambda: java11 en la ventana acoplable y ejecuta su código en ella. Para acceder a servicios remotos, como SES, se utiliza su configuración de AWS con una clave secreta / de acceso, por lo que debe estar actualizada. Otro punto importante: si no agrega un encabezado para el modo de calentamiento, se llamará a los servicios reales de AWS SES y DynamoDB.

Registro de llamadas aquí
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}


Además del registro lambda, la información de servicio sobre el estado de la llamada y una respuesta en formato JSON se escriben en la consola.

La API de Gateway, como la lambda, se puede ejecutar localmente.
Ejecutamos el comando:
sam local start-api

Registro de consola al inicio
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)


Después de iniciar el servidor, podemos ejecutar solicitudes. Usaré Postman y enviaré una solicitud para calentar la lambda.

En la consola vemos
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 -


Los registros, por supuesto, no están formateados de la mejor manera. Esperemos que esto se arregle en el futuro.

Importante: al cambiar el código lambda, no es necesario reiniciar la API. Es suficiente reconstruir el lambda con el comando sam build .
Un poco sobre errores
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. Llamadas locales desde IDEA


Todo lo descrito en el párrafo anterior es la funcionalidad de la herramienta SAM. Se controla desde la línea de comandos y se puede integrar en cualquier interfaz. Esto es exactamente lo que se hizo en el complemento para IntelljIDEA AWS Toolkit.

Este complemento agrega configuraciones adicionales para iniciar un lambda directamente desde el IDE. Así es como se ve la ventana de configuración. Debe seleccionar una plantilla con una descripción de la lambda o configurar los ajustes manualmente. En el primer caso, la cantidad de memoria e idioma para la compilación se tomará automáticamente de la plantilla. En el segundo caso, puede configurar la cantidad de RAM usted mismo y hacer un cálculo aproximado del límite. Otro complemento le permite ejecutar la configuración directamente desde la ventana del editor de código Java o yaml-template. Capturas de pantalla a continuación:











Eso, de hecho, es todo. Al ejecutar la configuración personalizada, simplemente automatiza las frecuentes llamadas lambda desde la consola. De acuerdo, ¿bastante cómodo?


4. Prueba


Aquí hablamos sobre los tipos de pruebas para lambdas. Como utilicé Java en el ejemplo, las pruebas también incluirán herramientas bastante estándar para pruebas de unidad e integración: Junit 4, Mockito y PowerMockito.

Pruebas unitarias


Para cubrir el código lambda con pruebas unitarias, no necesita tener conocimientos específicos. Es suficiente aplicar las prácticas estándar de Java: bloquee todas las dependencias de la clase e intente probar todos los casos posibles de procesamiento de solicitudes.

No agregué todos los escenarios de prueba y me limité a dos escenarios positivos y dos negativos.

La primera prueba positiva verifica que si hay un encabezado X-WARM-UP, no hay solicitudes en DbService y EmailService. El segundo caso verifica que se llamarán a estos servicios si la solicitud es real. Estas son las pruebas más simples, y omití parte de los controles en ellas.

Los escenarios negativos son una verificación de respuesta en caso de error de procesamiento de un cliente o servidor.

Pruebas de integración


En cuanto a las pruebas de integración, decidí revisar DbService y su trabajo con la tabla DynamoDB.

En este caso, utilicé la herramienta Localstack . Esta solución proporciona mokas gratis para los servicios de AWS que se ejecutan como contenedores acoplables.

Para instalar esta solución, simplemente ejecute los comandos:
pip install localstack
localstack start

O puede usar el archivo docker-compose .
docker-compose para criar solo 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"


El comando de inicio genera todos los servicios de AWS disponibles de forma predeterminada. Para aumentar DynamoDB específicamente, solo configure la variable de entorno:
set SERVICES=dynamodb


Existe un LocalstackTestRunner especial para Junit que le permite iniciar los servicios necesarios utilizando la configuración @LocalstackDockerProperties .
Como resultado, escribir una prueba para DbService se ve así:
  • Agregar sobre la clase de prueba.
    @RunWith(LocalstackTestRunner.class)
    @LocalstackDockerProperties(services = { "dynamodb" })
    

  • Crear una tabla
    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)));
    }
                    


  • Describimos el escenario de prueba
    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"));
                  


  • No olvide eliminar la tabla después de la prueba.
    Java
    @After
    public void tearDown() {
        dynamoDB.getTable("ContactUsTable").delete();
    }
                   



Como resultado, al usar Localstack, obtenemos una amplia gama de simulacros de AWS que le permiten emular el trabajo de estos servicios.

Pruebas E2E


Para jugar escenarios E2E, es mejor aumentar los servicios de prueba de AWS específicamente para ejecutar pruebas, si, por supuesto, existe esa oportunidad. Esto se puede hacer usando la plantilla opcional CloudFormation, en la cual los recursos serán copias de la plantilla principal.

La prueba se puede realizar llamando a la lambda a través de AWS-CLI y procesando el resultado con cualquier herramienta disponible. Todavía puede hacer una llamada en pruebas unitarias, pero sin crear mobs.

Pruebas de rendimiento


Dado que las pruebas de rendimiento merecen un artículo separado o incluso un libro, no intentaré poner toda la información aquí. En resumen, cuando se habla de pruebas de estrés, debe recordar que el uso de casi todos los servicios de AWS no es gratuito, y debe estar preparado para estimar los posibles costos por adelantado.

Si desea evaluar los recursos necesarios para una lambda, use la consola de la interfaz de usuario y realice algunas llamadas de prueba. Luego, usando la lógica y las matemáticas comunes, evalúe cuánto aumenta el rendimiento de su lambda dependiendo de la cantidad de recursos disponibles.

No te olvides de CloudWatch. Con su ayuda, puede crear paneles bastante indicativos y, en función de ellos, determinar qué parámetros de lambda y gastos puede pagar.


5. El costo de las soluciones sin servidor


La evaluación de una solución siempre es específica, porque debe considerar no solo el costo de una llamada, sino también el tráfico entrante / saliente, el costo del almacenamiento en caché, el registro y la supervisión. Si está considerando usar lambda, intente predecir la carga que manejará su servicio.

Si se trata de llamadas individuales o un proceso ETL que se ejecuta cada pocas horas / días, es más probable que el lambda valga la pena. Si predice una carga de varias consultas por segundo, asegúrese de hacer una estimación precisa.

A continuación, doy estimaciones aproximadas del costo de la lambda y el servidor EC2 más barato, para que comprenda qué valores están en cuestión: Se calcularon los siguientes parámetros para la lambda:




  • Plazo de entrega: 150 milisegundos
  • Precio: $ 0.00001667 por 1GB-s
  • Memoria: 256 MB

Para EC2 t2.nano:

  • Precio: $ 4.75 por mes
  • Memoria: 512 MB
  • vCPU: 1
  • Carga máxima: 5% de CPU por mes

Tomé datos de carga EC2 de aquí . Según estos cálculos, el servidor especificado puede soportar una carga constante de 0.3 solicitudes por segundo. A medida que aumenta la carga, aumentará el tiempo de respuesta, lo que reducirá la eficiencia de este servidor.

Todos estos cálculos son bastante abstractos y no tienen en cuenta muchos factores: descuentos en un pago por adelantado o una gran cantidad de solicitudes, tiempo de ejecución lambda, etc. Pero muestran que con una carga de menos de 1 solicitud por segundo, lambda cuesta menos que el servidor EC2 más barato.

Por cierto, no olvide agregar el precio de ALB al precio de EC2 si necesita mantener varias réplicas de aplicaciones o IP elásticas.


Las pruebas y el desarrollo local de soluciones sin servidor son menos populares que los mismos microservicios. Sin embargo, cada vez más personas estudian y aplican estas tecnologías y le dan la oportunidad a varios productos de código abierto de ser mejores y más eficientes. Dichas soluciones simplifican enormemente la vida de las empresas que necesitan lanzar un producto lo antes posible y no tienen que configurar toda la infraestructura. Pero incluso en productos ya desarrollados, creo que hay un lugar para bases de datos sin servidor o lambdas.

No pretendo ser un experto en el campo de la prueba de soluciones sin servidor, pero, en base a mi experiencia personal positiva, decidí destacar los puntos principales de este artículo. Espero que sean útiles para alguien, y tal vez algunos de ustedes puedan complementarme o incluso corregirme, lo cual me alegrará mucho.


Enlaces útiles



All Articles