LoginSignup
7
4

More than 3 years have passed since last update.

カスタムdockerイメージでlambdaを動かす

Last updated at Posted at 2020-12-21

AWS & Game Advent Calendar 2020 の 20 日目の記事になります。

agenda

  • はじめに
  • 利用するツール
  • ローカルでの実装
  • Lambdaへのdeploy
  • 動作チェック
  • まとめ

はじめに

2020年のre:Inventで、Lambdaがコンテナをサポートする発表がありました。
今までカスタムランタイムでLambdaを動かすには面倒なセットアップが必要でしたがこのアップデートでかなり使いやすくなるのではないでしょうか。

この記事ではコンテナを使ってミニマムの(bashを使ってイベントデータを表示させる)関数をLambdaで動作させる解説を行います。

利用するツール

  • sam: Lambdaのdeploy
  • docker: コンテナイメージのビルドなど
  • ECR: イメージのレジストリ
  • bash: 何でも良いのですが今回はbashでechoをさせます

samのインストール

samはHomebrewを使ってインストールしました

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

ECRのリポジトリ作成

awscliを使ってECRリポジトリを作成します。リポジトリ名はdocker-lambda-bashとします。

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

ローカルでの実装

カスタムランタイムのチュートリアルはこちらです。
bashでLambdaを動作させるために必要なものは以下の3つです。

  • カスタムランタイムのDockerイメージ
  • bootstrap
  • function.sh

カスタムランタイムのイメージ

こちらの公式のイメージを使って実装を進めます。

bootstrap

詳細はチュートリアルを参照してください。
簡単に説明すると、whileを回してLambdaAPIを使ってLambdaのイベントを取得します。
取得したイベントデータをレスポンスとして返却しています。

#!/bin/sh -xv

set -euo pipefail

# ハンドラ関数の読み込み
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

while true
do
  HEADERS="$(mktemp)"

  # イベントの取得
  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)

  # ハンドラ関数の実行
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

  # 結果を返却
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done

function.sh

こちらも詳細はチュートリアルを参照してください。
イベントデータをechoするだけのscriptです。

function.sh
function handler () {
  EVENT_DATA=$1
  echo "$EVENT_DATA" 1>&2;
  RESPONSE="Echoing request: '$EVENT_DATA'"
  echo $RESPONSE
}

ローカル環境での実行

ローカルでこの関数を実行するには、以下のDockerfileを使ってイメージをビルドします。

FROM public.ecr.aws/lambda/provided:al2
COPY bootstrap ${LAMBDA_RUNTIME_DIR}
COPY function.sh ${LAMBDA_TASK_ROOT}
CMD [ "function.handler" ]

内容は至ってシンプルで、AmazonLinux2(al2)のイメージをベースにして、bootstrapをLambdaのランタイムDirに、function.shをLambdaのタスクDirに配置し、function.handlerをCMDで実行するというものです。

Lambdaの関数の指定方法は、(スクリプト名).(関数名)です。
この例では、function.shという名前のスクリプトの中の、handlerという関数を実行させる必要があるので、このような指定方法となります。

dockekrfileが整ったので以下の様にイメージをビルドします。

$ docker build -t docker-lambda-bash .

ビルドしたイメージを使ってrunします。

$ docker run -p 9000:8080 docker-lambda-bash

curlで9000番portにアクセスするとリクエストデータが返却されます。

$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
Echoing request: '{"payload":"hello world!"}'

Lambdaへのdeploy

Lambdaへのdeployはsamを利用します。
samの詳細な使い方は割愛しますが、大まかな手順は以下の通りです。

  • ECRにログイン
  • docker tagでイメージにtagging
  • ECRにpush
  • samでイメージを指定してdeploy(Lambda関数の作成)

ECRにログイン

ECRにログインするには以下のコマンドを実行します。

$ export aws_region=ap-northeast-1 # 環境変数にリージョンを設定
$ export aws_account_id=xxxxxxxxxx # 環境変数にAWSのアカウントIDを設定
$ 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

samでイメージを指定してdeploy

samでLambdaをdeployするにはtemplateを作成する必要があります。
環境変数aws_account_idaws_regionを指定する必要があるので、適宜(YOUR-AWS-ACCOUNT-ID)(YOUR-AWS-REGION)を差し替えてください。

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/'

ImageUriでECRにpushしたコンテナイメージを指定しています。
また、Outputsを指定してApiGatewayを使って確認できるようにエンドポイントを作成しています。

deploy

sam deploy --guidedでdeployします。deploy時のログは以下となります。

$ 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
$

動作チェック

成功したので、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}}
$

Lambdaのイベントデータが表示されました。

まとめ

カスタムランタイムをdockerコンテナで動作させるサンプルを紹介しました。
このプログラム自体はあまり意味のあるものではありませんが、カスタムランタイムのチュートリアルの例と比べたらかなりシンプルになっています。
また、ローカルとクラウドで実行環境が一致するので(同じコンテナの上で動作するので)動作が保証される点も嬉しいです。

sample code

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4