Run lambda with custom docker image

This is the 20th day article of AWS & Game Advent Calendar 2020.

agenda

Introduction

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.

Tools to use

sam installation

sam was installed using Homebrew

$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 1.15.0

Create ECR repository

Create an ECR repository using awscli. The repository name is docker-lambda-bash.

$ aws ecr create-repository --repository-name docker-lambda-bash

Local implementation

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.

Custom runtime image

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
}

Run in local environment

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

Deploy to Lambda uses sam. I will omit the detailed usage of sam, but the general procedure is as follows.

Log in to ECR

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

Specify the image with sam and deploy

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
$

Operation check

Now that it's successful, try accessing the Outputs URL.

$ curl 'https://ddcq0ciwhb.execute-api.ap-northeast-1.amazonaws.com/Prod/echo?param1=val1&param2=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.

Summary

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

Run lambda with custom docker image
Run Pico with docker
Run Payara with Docker
Run TAO Core with Docker
Run Rails whenever with docker
Run SQL Server with Docker ToolBox
Make JupyterLab run anywhere with docker
Restart apache with docker php-apache image
Run (provisionally) a Docker image with ShellCommandActivity on AWS Data Pipeline
Proxy server with squid using docker image
Allow image posting with [Docker + WordPress + MySQL]
Image flew when updating Docker with WSL2
Docker Compact Manual (4: Create a custom image)
Run Ubuntu + ROS with Docker on Mac
How to run Blazor (C #) with Docker
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
Run JSP Hello World with Tomcat on Docker
Create jupyter notebook with Docker and run ruby
How to give your image to someone with docker
Update container image with KUSANAGI Runs on Docker
Launch Docker image with initial data injected with docker-compose
Run Scala with GraalVM & make it a native image
Until you try running Apache Kafka with docker image
Run old Vivado with Docker guest OS as CentOS 7.4
Launch MariaDB with Docker
Rails deploy with Docker
Explode Docker with WSL2
Run Android instrumentation unit tests with GitLab CI + Docker
Use Puphpeteer with Docker
Operate Emby with Docker
Delete unused docker image
Try WildFly with Docker
Use ngrok with Docker
[Docker] Connection with MySQL
Php settings with Docker
Run Mosquitto with Docker and try WebSocket communication with MQTT
Getting Started with Docker
Build an Android image for Orange Pi 4 with Docker
Disposable PHP with Docker
Install Composer with Docker
Run phpunit on Docker
Maybe it works! Create an image with Docker and share it!
Until you run Quarkus and run docker image on Amazon ECS
Create a Docker image with the Oracle JDK installed (yum
Register your own Docker image with ECR using AWS CLI
Build Docker Image lightweight and fast with CodeBuild with Santa Banner
AWS Lambda says Container Image Support, so pseudo Cloud Run
Write DiscordBot to Spreadsheets Write in Ruby and run with Docker
Pytorch execution environment with Docker
Use Lambda Layers with Java
What is docker run -it?
Use GDAL with Python with Docker
Run batch with docker-compose with Java batch
Run VS Code on Docker
Deploy with EC2 / Docker / Laravel
Docker management with VS Code
Install yarn in docker image
Run openvpn on Docker (windows)
docker review image creation-container start
Set up GitLab with docker
Run Tensorflow with THETA V