Lambda Function#
Let create a Lambda function:
- Execution role
- Lambda function
- Inline code
AWSTemplateFormatVersion: 2010-09-09Description: 'a lambda function'Resources:# Create an IAM Role for lambda functionLambdaExecutionRole:Type: 'AWS::IAM::Role'Properties:AssumeRolePolicyDocument:Version: 2012-10-17Statement:- Effect: AllowPrincipal:Service:- lambda.amazonaws.comAction:- 'sts:AssumeRole'Path: /demo/logaccess/Policies:- PolicyName: rootPolicyDocument:Version: 2012-10-17Statement:- Effect: AllowAction:- 'logs:CreateLogGroup'- 'logs:CreateLogStream'- 'logs:PutLogEvents'Resource: 'arn:aws:logs:*:*:*'# Create a Lambda functionLambdaFunction:Type: AWS::Lambda::FunctionProperties:Handler: 'index.handler'Role: !GetAtt LambdaExecutionRole.ArnCode:ZipFile: |import jsondef handler(event, context):return {'statusCode': 200,'body': json.dumps('Hello from Lambda!')}Runtime: 'python3.10'Timeout: 30Outputs:LambdaFunctionArn:Value: !GetAtt LambdaFunction.ArnExport:Name:Fn::Sub: ${AWS::StackName}-LambdaFunctionArn
API Gateway#
Let create a api gateway and integrate with the lambda function
- Create a REST API
- API Gateway execution role
- Create resource and method
- Integrate with lambda
AWSTemplateFormatVersion: '2010-09-09'Description: Create an rest apiParameters:BookLambdaStackName:Type: StringDefault: 'cfn-lambda-demo'ApiName:Type: StringDefault: 'rest-api'Resources:# Create an IAM Role for api gateway to invoke lambda functionsApiGatewayLambdaRole:Type: 'AWS::IAM::Role'Properties:AssumeRolePolicyDocument:Version: 2012-10-17Statement:- Effect: AllowPrincipal:Service:- apigateway.amazonaws.comAction:- 'sts:AssumeRole'Path: /Policies:- PolicyName: rootPolicyDocument:Version: 2012-10-17Statement:- Effect: AllowAction: 'lambda:*'Resource: '*'# Create a rest apiRestApi:Type: AWS::ApiGateway::RestApiProperties:Name: !Ref ApiNameDescription: 'rest api demo'EndpointConfiguration:Types:- REGIONAL# Create a resource named bookBookResource:Type: AWS::ApiGateway::ResourceProperties:RestApiId: !Ref RestApiParentId: !GetAtt RestApi.RootResourceIdPathPart: 'book'# Create a GET method for the book resource and integrate with a Lambda functionGetBookMethod:Type: AWS::ApiGateway::MethodProperties:RestApiId: !Ref RestApiResourceId: !Ref BookResourceHttpMethod: GETAuthorizationType: NONEMethodResponses:- StatusCode: 200ResponseModels:application/json: EmptyIntegration:Type: AWS# get role arn from above created roleCredentials: !GetAtt ApiGatewayLambdaRole.ArnIntegrationResponses:- StatusCode: 200IntegrationHttpMethod: POSTUri: !Sub- arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations- lambdaArn: !ImportValueFn::Sub: ${BookLambdaStackName}-LambdaFunctionArn# Create deployment stage named ProdDeployment:Type: AWS::ApiGateway::DeploymentDependsOn:- GetBookMethodProperties:RestApiId: !Ref RestApiStageName: Prod
Deployment#
aws cloudformation validate-template \--template-body file://role.yamlaws cloudformation create-stack \--stack-name cfn-lambda-demo \--template-body file://lambda.yaml \--capabilities CAPABILITY_NAMED_IAMaws cloudformation update-stack \--stack-name cfn-lambda-demo \--template-body file://lambda.yaml \--capabilities CAPABILITY_NAMED_IAMaws cloudformation create-stack \--stack-name cfn-apigw-demo \--template-body file://apigw.yaml \--capabilities CAPABILITY_NAMED_IAMaws cloudformation update-stack \--stack-name cfn-apigw-demo \--template-body file://apigw.yaml \--capabilities CAPABILITY_NAMED_IAMaws cloudformation delete-stack \--stack-name cfn-demo-demo
Handle Error#
Let update lambda
AWSTemplateFormatVersion: 2010-09-09Description: 'a lambda function'Resources:# Create an IAM Role for lambda functionLambdaExecutionRole:Type: 'AWS::IAM::Role'Properties:AssumeRolePolicyDocument:Version: 2012-10-17Statement:- Effect: AllowPrincipal:Service:- lambda.amazonaws.comAction:- 'sts:AssumeRole'Path: /demo/logaccess/Policies:- PolicyName: rootPolicyDocument:Version: 2012-10-17Statement:- Effect: AllowAction:- 'logs:CreateLogGroup'- 'logs:CreateLogStream'- 'logs:PutLogEvents'Resource: 'arn:aws:logs:*:*:*'# Create a Lambda functionLambdaFunction:Type: AWS::Lambda::FunctionProperties:Handler: 'index.handler'Role: !GetAtt LambdaExecutionRole.ArnCode:ZipFile: |import jsondef handler(event, context):# raise exception to test api code 500if 1==2:raise Exception('Malformed input ...')# returnreturn {'statusCode': 200,'body': json.dumps('Hello from Lambda!')}Runtime: 'python3.10'Timeout: 30Outputs:LambdaFunctionArn:Value: !GetAtt LambdaFunction.ArnExport:Name:Fn::Sub: ${AWS::StackName}-LambdaFunctionArn
Then update api gateway to handle exception from lambda handler.
AWSTemplateFormatVersion: '2010-09-09'Description: Create an rest apiParameters:BookLambdaStackName:Type: StringDefault: 'cfn-lambda-demo'ApiName:Type: StringDefault: 'rest-api'Resources:# Create an IAM Role for api gateway to invoke lambda functionsApiGatewayLambdaRole:Type: 'AWS::IAM::Role'Properties:AssumeRolePolicyDocument:Version: 2012-10-17Statement:- Effect: AllowPrincipal:Service:- apigateway.amazonaws.comAction:- 'sts:AssumeRole'Path: /Policies:- PolicyName: rootPolicyDocument:Version: 2012-10-17Statement:- Effect: AllowAction: 'lambda:*'Resource: '*'# Create a rest apiRestApi:Type: AWS::ApiGateway::RestApiProperties:Name: !Ref ApiNameDescription: 'rest api demo'EndpointConfiguration:Types:- REGIONAL# Create a resource named bookBookResource:Type: AWS::ApiGateway::ResourceProperties:RestApiId: !Ref RestApiParentId: !GetAtt RestApi.RootResourceIdPathPart: 'book'# Enable CORS for this resource# Create a GET method for the book resource and integrate with a Lambda functionGetBookMethod:Type: AWS::ApiGateway::MethodProperties:RestApiId: !Ref RestApiResourceId: !Ref BookResourceHttpMethod: GETAuthorizationType: NONEMethodResponses:- StatusCode: 200ResponseParameters:method.response.header.Access-Control-Allow-Origin: truemethod.response.header.Access-Control-Allow-Methods: truemethod.response.header.Access-Control-Allow-Headers: trueResponseModels:application/json: Empty- StatusCode: 500ResponseParameters:method.response.header.Access-Control-Allow-Origin: truemethod.response.header.Access-Control-Allow-Methods: truemethod.response.header.Access-Control-Allow-Headers: trueResponseModels:application/json: EmptyIntegration:Type: AWS# get role arn from above created roleCredentials: !GetAtt ApiGatewayLambdaRole.ArnIntegrationResponses:- StatusCode: 200ResponseParameters:method.response.header.Access-Control-Allow-Origin: "'*'"method.response.header.Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'"method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"- StatusCode: 500# http status regexSelectionPattern: 'Malformed.*'ResponseParameters:method.response.header.Access-Control-Allow-Origin: "'*'"method.response.header.Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'"method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"IntegrationHttpMethod: POSTUri: !Sub- arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations- lambdaArn: !ImportValueFn::Sub: ${BookLambdaStackName}-LambdaFunctionArn# Create deployment stage named ProdDeployment:Type: AWS::ApiGateway::DeploymentDependsOn:- GetBookMethodProperties:RestApiId: !Ref RestApiStageName: Prod
Enable CORS#
Let update api gateway to enable CORS
AWSTemplateFormatVersion: '2010-09-09'Description: Create an rest apiParameters:BookLambdaStackName:Type: StringDefault: 'cfn-lambda-demo'ApiName:Type: StringDefault: 'rest-api'Resources:# Create an IAM Role for api gateway to invoke lambda functionsApiGatewayLambdaRole:Type: 'AWS::IAM::Role'Properties:AssumeRolePolicyDocument:Version: 2012-10-17Statement:- Effect: AllowPrincipal:Service:- apigateway.amazonaws.comAction:- 'sts:AssumeRole'Path: /Policies:- PolicyName: rootPolicyDocument:Version: 2012-10-17Statement:- Effect: AllowAction: 'lambda:*'Resource: '*'# Create a rest apiRestApi:Type: AWS::ApiGateway::RestApiProperties:Name: !Ref ApiNameDescription: 'rest api demo'EndpointConfiguration:Types:- REGIONAL# Create a resource named bookBookResource:Type: AWS::ApiGateway::ResourceProperties:RestApiId: !Ref RestApiParentId: !GetAtt RestApi.RootResourceIdPathPart: 'book'# Enable CORS for this resource# Create a GET method for the book resource and integrate with a Lambda functionGetBookMethod:Type: AWS::ApiGateway::MethodProperties:RestApiId: !Ref RestApiResourceId: !Ref BookResourceHttpMethod: GETAuthorizationType: NONEMethodResponses:- StatusCode: 200ResponseParameters:method.response.header.Access-Control-Allow-Origin: truemethod.response.header.Access-Control-Allow-Methods: truemethod.response.header.Access-Control-Allow-Headers: trueResponseModels:application/json: Empty- StatusCode: 500ResponseParameters:method.response.header.Access-Control-Allow-Origin: truemethod.response.header.Access-Control-Allow-Methods: truemethod.response.header.Access-Control-Allow-Headers: trueResponseModels:application/json: EmptyIntegration:Type: AWS# get role arn from above created roleCredentials: !GetAtt ApiGatewayLambdaRole.ArnIntegrationResponses:- StatusCode: 200ResponseParameters:method.response.header.Access-Control-Allow-Origin: "'*'"method.response.header.Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'"method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"- StatusCode: 500# http status regexSelectionPattern: 'Malformed.*'ResponseParameters:method.response.header.Access-Control-Allow-Origin: "'*'"method.response.header.Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'"method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"IntegrationHttpMethod: POSTUri: !Sub- arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations- lambdaArn: !ImportValueFn::Sub: ${BookLambdaStackName}-LambdaFunctionArn# Create deployment stage named ProdDeployment:Type: AWS::ApiGateway::DeploymentDependsOn:- GetBookMethodProperties:RestApiId: !Ref RestApiStageName: Prod
Load Test#
Use curl
curl -v https://z7eg72axm4.execute-api.us-west-2.amazonaws.com/Prod/book
Simple frontend to test
<html><head><title>Test</title></head><body><div><h1>Hello</h1></div></body><script>const callAPI = async () => {const response = await fetch('https://z7eg72axm4.execute-api.us-west-2.amazonaws.com/Prod/book',{method: 'GET'})console.log(response)console.log(await response.text())}callAPI()</script></html>
Load test python script.
## artillery quick -n 2100 --count 100 ENDPOINTimport timeimport requestsfrom concurrent.futures import ThreadPoolExecutorURL = "https://9lbz0mxhk5.execute-api.us-west-2.amazonaws.com/Prod/book"NO_CONCUR_REQUEST = 20COUNT = 1def send_request():resp = requests.get(URL,headers={"x-api-key": "YWqb31LYW34UxwsTMCv8l7ECOJjkIgLw6o5OFq6W",},)print(resp.text)def test_concurrent():with ThreadPoolExecutor(max_workers=NO_CONCUR_REQUEST) as executor:for k in range(1, NO_CONCUR_REQUEST):executor.submit(send_request)while True:print(f"{NO_CONCUR_REQUEST} requests {COUNT}")test_concurrent()time.sleep(1)COUNT += 1# send_request()# for k in range(2):