This is the 20th day article of AWS & Game Advent Calendar 2020.
agenda
At re: Invent in 2020, it was announced that Lambda will support containers. Until now, running Lambda with a custom runtime required a tedious setup, but I think this update will make it much easier to use.
In this article, I will explain how to use a container to operate a minimum function (using bash to display event data) on Lambda.
sam was installed using Homebrew
$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 1.15.0
Create an ECR repository using awscli. The repository name is docker-lambda-bash
.
$ aws ecr create-repository --repository-name docker-lambda-bash
The custom runtime tutorial is here (https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html). The following three items are required to operate Lambda with bash.
We will proceed with the implementation using the official image of here.
bootstrap
See the tutorial for details. Simply put, turn while to use the Lambda API to get Lambda events. The acquired event data is returned as a response.
#!/bin/sh -xv
set -euo pipefail
#Loading handler function
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
while true
do
HEADERS="$(mktemp)"
#Get the event
EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
#Execution of handler function
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
#Return the result
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
function.sh
See the tutorial for more details. It is a script that just echoes the event data.
function.sh
function handler () {
EVENT_DATA=$1
echo "$EVENT_DATA" 1>&2;
RESPONSE="Echoing request: '$EVENT_DATA'"
echo $RESPONSE
}
To run this function locally, build the image using the following Dockerfile.
FROM public.ecr.aws/lambda/provided:al2
COPY bootstrap ${LAMBDA_RUNTIME_DIR}
COPY function.sh ${LAMBDA_TASK_ROOT}
CMD [ "function.handler" ]
The content is very simple, based on the image of Amazon Linux2 (al2), bootstrap
is placed in Lambda's runtime Dir, function.sh
is placed in Lambda's task Dir, and function.handler
is executed in CMD.
The Lambda function is specified as (script name). (Function name)
.
In this example, it is necessary to execute the function handler
in the script named function.sh
, so this is the specification method.
Now that the dockekrfile is ready, build the image as follows.
$ docker build -t docker-lambda-bash .
Run using the built image.
$ docker run -p 9000:8080 docker-lambda-bash
Request data will be returned when you access port 9000 with curl.
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
Echoing request: '{"payload":"hello world!"}'
Deploy to Lambda uses sam. I will omit the detailed usage of sam, but the general procedure is as follows.
To log in to ECR, execute the following command.
$ export aws_region=ap-northeast-1 #Set region in environment variable
$ export aws_account_id=xxxxxxxxxx #Set AWS account ID in environment variable
$ aws ecr get-login-password --region $aws_region | docker login --username AWS --password-stdin $aws_account_id.dkr.ecr.$aws_region.amazonaws.com
tagging & push
$ docker tag docker-lambda $aws_account_id.dkr.ecr.$aws_region.amazonaws.com/docker-lambda-bash
$ docker push $aws_account.dkr.ecr.$aws_region.amazonaws.com/docker-lambda-bash
You need to create a template to deploy Lambda with sam.
Since it is necessary to specify the environment variables aws_account_id
and aws_region
, replace (YOUR-AWS-ACCOUNT-ID)
and (YOUR-AWS-REGION)
as appropriate. Please give me.
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: lambda-custom-docker-sample
Globals:
Function:
Timeout: 3
Resources:
MyCustomDocker:
Type: AWS::Serverless::Function
Properties:
FunctionName: CustomDockerWithBash
ImageUri: (YOUR-AWS-ACCOUNT-ID).dkr.ecr.(YOUR-AWS-REGION).amazonaws.com/docker-lambda-bash:latest
PackageType: Image
Events:
CustomDockerEcho:
Type: Api
Properties:
Path: /echo
Method: get
Outputs:
MyCustomDockerApi:
Description: 'API Gateway endpoint URL'
Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/echo/'
The container image pushed to ECR is specified by ImageUri
.
Also, I am creating an endpoint so that I can check it using ApiGateway by specifying Outputs
.
deploy
Deploy with sam deploy --guided
. The log at the time of deploy is as follows.
$ sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: lambda-custiom-docker-sample
AWS Region [us-east-1]: ap-northeast-1
Image Repository for MyCustomDocker: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-lambda-bash
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: y
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: y
MyCustomDocker may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: y
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
Looking for resources needed for deployment: Not found.
Creating the required resources...
Successfully created!
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1b4wws4k1oxg
A different default S3 bucket can be set in samconfig.toml
Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
Learn more about samconfig.toml syntax at
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Deploying with following values
===============================
Stack name : lambda-custiom-docker-sample
Region : ap-northeast-1
Confirm changeset : True
Deployment image repository :
{
"MyCustomDocker": "xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-lambda-bash"
}
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-1b4wws4k1oxg
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
MyCustomDocker may not have authorization defined.
Uploading to lambda-custiom-docker-sample/e1f6334b9552a49d5e9e07a9970bbae3.template 697 / 697.0 (100.00%)
Waiting for changeset to be created..
CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
-------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add MyCustomDockerCustomDockerEchoPermiss AWS::Lambda::Permission N/A
ionProd
+ Add MyCustomDockerRole AWS::IAM::Role N/A
+ Add MyCustomDocker AWS::Lambda::Function N/A
+ Add ServerlessRestApiDeploymentc8d8b9cf15 AWS::ApiGateway::Deployment N/A
+ Add ServerlessRestApiProdStage AWS::ApiGateway::Stage N/A
+ Add ServerlessRestApi AWS::ApiGateway::RestApi N/A
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxx:changeSet/samcli-deploy1608550235/9b8db82c-4197-4d15-9624-fc35f2e35bd8
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2020-12-21 20:31:50 - Waiting for stack create/update to complete
CloudFormation events from changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
-------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::IAM::Role MyCustomDockerRole -
CREATE_IN_PROGRESS AWS::IAM::Role MyCustomDockerRole Resource creation Initiated
CREATE_COMPLETE AWS::IAM::Role MyCustomDockerRole -
CREATE_IN_PROGRESS AWS::Lambda::Function MyCustomDocker -
CREATE_IN_PROGRESS AWS::Lambda::Function MyCustomDocker Resource creation Initiated
CREATE_COMPLETE AWS::Lambda::Function MyCustomDocker -
CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi -
CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi Resource creation Initiated
CREATE_COMPLETE AWS::ApiGateway::RestApi ServerlessRestApi -
CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeploymentc8d8b9cf15 -
CREATE_IN_PROGRESS AWS::Lambda::Permission MyCustomDockerCustomDockerEchoPermiss -
ionProd
CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeploymentc8d8b9cf15 Resource creation Initiated
CREATE_IN_PROGRESS AWS::Lambda::Permission MyCustomDockerCustomDockerEchoPermiss Resource creation Initiated
ionProd
CREATE_COMPLETE AWS::ApiGateway::Deployment ServerlessRestApiDeploymentc8d8b9cf15 -
CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage -
CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage Resource creation Initiated
CREATE_COMPLETE AWS::ApiGateway::Stage ServerlessRestApiProdStage -
CREATE_COMPLETE AWS::Lambda::Permission MyCustomDockerCustomDockerEchoPermiss -
ionProd
CREATE_COMPLETE AWS::CloudFormation::Stack lambda-custiom-docker-sample -
-------------------------------------------------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Key MyCustomDockerApi
Description API Gateway endpoint URL
Value https://ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com/Prod/echo/
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - lambda-custiom-docker-sample in ap-northeast-1
$
Now that it's successful, try accessing the Outputs
URL.
$ curl 'https://ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com/Prod/echo?param1=val1¶m2=val2'
{"message": "Go Serverless v1.0! Your function executed successfully!", "input": {"resource": "/echo", "path": "/echo", "httpMethod": "GET", "headers": {"Accept": "*/*", "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": "JP", "Host": "ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com", "User-Agent": "curl/7.64.1", "Via": "2.0 06f6824c0d57ccd48408cb017c7bce76.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "-lVoexDRq5sBmapw-aMSE8l0ypW4IXMZK3kECxkp4qZVT2ZpLgsAZQ==", "X-Amzn-Trace-Id": "Root=1-5fe08a9d-26bf7d24044bf6127630150f", "X-Forwarded-For": "124.213.96.14, 70.132.19.137", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https"}, "multiValueHeaders": {"Accept": ["*/*"], "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": ["JP"], "Host": ["ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com"], "User-Agent": ["curl/7.64.1"], "Via": ["2.0 06f6824c0d57ccd48408cb017c7bce76.cloudfront.net (CloudFront)"], "X-Amz-Cf-Id": ["-lVoexDRq5sBmapw-aMSE8l0ypW4IXMZK3kECxkp4qZVT2ZpLgsAZQ=="], "X-Amzn-Trace-Id": ["Root=1-5fe08a9d-26bf7d24044bf6127630150f"], "X-Forwarded-For": ["124.213.96.14, 70.132.19.137"], "X-Forwarded-Port": ["443"], "X-Forwarded-Proto": ["https"]}, "queryStringParameters": {"param1": "val1", "param2": "val2"}, "multiValueQueryStringParameters": {"param1": ["val1"], "param2": ["val2"]}, "pathParameters": null, "stageVariables": null, "requestContext": {"resourceId": "co37s7", "resourcePath": "/echo", "httpMethod": "GET", "extendedRequestId": "X5qYlFZ8tjMFcbg=", "requestTime": "21/Dec/2020:11:44:29 +0000", "path": "/Prod/echo", "accountId": "800832305859", "protocol": "HTTP/1.1", "stage": "Prod", "domainPrefix": "ddcq0ciwhb", "requestTimeEpoch": 1608551069250, "requestId": "f7c4fb6d-9621-44c3-8c60-03a0d259ce67", "identity": {"cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "sourceIp": "124.213.96.14", "principalOrgId": null, "accessKey": null, "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "curl/7.64.1", "user": null}, "domainName": "ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com", "apiId": "ddcq0ciwhb"}, "body": null, "isBase64Encoded": false}}
$
The Lambda event data is displayed.
I introduced a sample to run a custom runtime in a docker container. The program itself doesn't make much sense, but it's a lot simpler than the custom runtime tutorial example. Also, I am happy that the operation is guaranteed because the execution environment matches locally and in the cloud (because it runs on the same container).
sample code
https://github.com/ohr486/lambda-container-image-sample
Recommended Posts