⌨️

今から始めるLambda②「ローカルでの実行とCLIからのデプロイ」

2021/10/20に公開

はじめに

前回の記事ではAWS Lambdaの概要と、コンソールから関数を作成・変更・デプロイする方法を紹介しました。

https://zenn.dev/nekoniki/articles/b1f94bfbb28e29

しかしながら、実際の開発現場ではローカル環境で関数を開発・検証の上デプロイしたいと思います。

そこで今回は、ローカルでLambdaを実行する環境構築とデプロイ方法について紹介します。

AWS CLIのインストール

今回は最終的にローカルからAWSにデプロイを行います。
そのためにAWSCLIを端末にインストールしましょう。

下記はHomebrewを使った例ですが、他のツールを使っている場合はそちらを使用しても構いません。

brew install awscli

以下コマンドでバージョンが表示されればOKです。

aws --version

IAMアクセスキーの発行

以降AWS CLIからコマンドを実行するためにIAMからアクセスキーを発行します。

このアクセスキーを用いて認証を行います。

AWSのダッシュボードの【すべてのサービス】から【IAM】を選択します。

上記のような画面が出てきたら【アクセスキーの作成】を押下することで、アクセスキーとシークレットが出力されます。

この内容を控えておきましょう。

CLIに登録

以下コマンドでAWS CLIに認証情報を登録します。

aws configure

アクセスキー、シークレット、リージョン、出力形式を対話形式で入力していきます。

プロジェクトディレクトリ作成

まずはプロジェクトのディレクトリを作成します。

mkdir lambda-deploy-sample
cd lambda-deploy-sample

ここにindex.jsevent.jsonを作成します。
それぞれの内容は以下の通りです。

index.js
exports.handler = async function (event, context) {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2));
  return context.logStreamName;
};
event.json
{
  "type": "test",
  "value": 100
}

event.jsonLambda関数の引数用のデータです。
実際にはLambdaのトリガーとなったサービスによって構造が異なりますが、ここでは簡単のため上記のようなJSONデータとしています。

ローカル実行

さっそく上記の関数をローカルで実行していきたいと思います。
ローカルでLambdaを実行するために、今回は配布されているDockerイメージを使用します。

使用するDockerイメージ

以下が今回使用するlambci/lambdaというイメージです。

https://hub.docker.com/r/lambci/lambda/

今回はNode.jsですが、他の言語のランタイムも用意されており、DockerHubページのDocker tagsの項目を参照すると対応しているランタイムが分かります。

ここではnodejs12.xというランタイムを使用したいと思います。

実行コマンドは以下のような形式です。

docker run --rm \
  -v <code_dir>:/var/task:ro,delegated \
  [-v <layer_dir>:/opt:ro,delegated] \
  lambci/lambda:<runtime> \
  [<handler>] [<event>]

<code_dir>は実行する関数があるディレクトリです。
従って今回はカレントディレクトリを指したいので$PWDを使用します。

<layer_dir>の節については今回省略します。

<runtime>は先にある通りnodejs12.xを使用し、<handler>index.jshandler関数を、<event>には先のevent.jsonを文字列にパースして渡します。

ローカルで実行

それでは実行してみましょう。
下記コマンドで実行されます。

docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs12.x index.handler $(printf '%s' $(cat event.json))

コンソールに以下のように表示されれば成功です。

START RequestId: 26612a00-1447-14c7-04ef-b612abf62773 Version: $LATEST
2021-10-20T04:23:16.574Z        26612a00-1447-14c7-04ef-b612abf62773    INFO    EVENT: 
{
  "type": "test",
  "value": 100
}
END RequestId: 26612a00-1447-14c7-04ef-b612abf62773
REPORT RequestId: 26612a00-1447-14c7-04ef-b612abf62773  Init Duration: 117.51 ms        Duration: 6.19 ms Billed Duration: 7 ms   Memory Size: 1536 MB    Max Memory Used: 41 MB

console.log()が動作してevent.jsonの中身が表示されました。

応用:APIとして起動する

応用的な内容として、指定のLambda関数をAPIとして起動することができます。

docker run --rm -e DOCKER_LAMBDA_STAY_OPEN=1 -p 9001:9001 -v "$PWD":/var/task lambci/lambda:nodejs12.x index.handler

この状態でlocalhost:9001curl等でリクエストを投げることでLambdaを動かすことができます。

curl -d '{"aaa": "bbb"}' http://127.0.0.1:9001/2015-03-31/functions/index/invocations

実査にはAPI Gatewayなどを使うことになりますが、擬似的にその状況を再現することができます。

デプロイする

ここまでで、ローカル環境でLambda用の関数を作成し動かすことができるようになりました。

最終的にはこの関数をAWSにデプロイする必要があります。
今回はより実務的な内容に近づけるため、前述のAWS CLIを用いて行いますが、コンソール上からソースをデプロイすることもできます。

いずれにせよ、ソース一式をZIPで固める必要があります。

zip -r9 deploy_package.zip .

こうすることでdeploy_package.zipが作成されます。

Roleの作成

新規のLambdaをデプロイするには、対応したロールを作成する必要があります。

ロールの作成もコマンドで行えますが、いくつかの情報をJSON形式で渡す必要があるため、先に以下のようなrole.jsonを作成しておきます。

role.json
{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
}

内容の細かい解説については割愛しますが、Lambdaに限らずここに「どんなサービスを使うか」等の情報を記載していきます。

ロールの作成は下記コマンドで行えます。
ここではtestFunction2という名称で作成を行います。

aws iam create-role --role-name testFunction2 --assume-role-policy-document file://role.json

すると、以下のようなJSONが出力されました。

{
    "Role": {
        "Path": "/",
        "RoleName": "testFunction2",
        "RoleId": "【ロールID】",
        "Arn": "【ARN】",
        "CreateDate": "2021-10-20T07:21:53+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}

このJSONの中のARNを使ってデプロイを行います。

デプロイ

AWS側で未作成のLambdaをデプロイするにはcreate-functionコマンドを使います。

オプションとしてZIPファイルパス、先ほどのロールのARN、ランタイムの情報などを付けて実行します。

aws lambda create-function --role 【ARNの値】 --function-name testFunction2 --zip-file fileb://deploy_package.zip --handler index.handler --runtime nodejs12.x

実行後JSONが返って来れば成功です。
AWSLambdaのダッシュボード上にtestFunction2が作成されているかと思います。

別パターン:lambci/lambdaを使ってデプロイ用ファルを作成

https://qiita.com/anfangd/items/bb448e0dd30db3894d92

上記記事で、Lambdaのローカル実行に用いたlambci/lambdaを使ってデプロイする方法が紹介されていました。

今回の例ではライブラリを使いませんでしたが、一般には何かしらのライブラリが使われる(=package.jsonnode_modulesがある)ケースが殆どだと思います。

そういった諸々を包括してデプロイするにはデプロイ用のDockerイメージを作った方が良いかと思います。

以下のようなDockerfileを作成しましょう。

# 使用するイメージ
FROM lambci/lambda:build-nodejs12.x
# エンコード設定
ENV LANG C.UTF-8
# リージョン設定
ENV AWS_DEFAULT_REGION us-east-2

COPY . .

# 依存パッケージのインストール
CMD npm install
# ZIPの作成
CMD zip -r9 deploy_package.zip .

Dockerfileを作ったら、イメージを作成し実行します。

# Dockerfileからイメージの作成
docker build -t deploypackage .
# 実行
docker run --rm -v "$PWD":/var/task deploypackage:latest

実行後ZIPファイルが作成されていればOKです。
デプロイについては先ほどのパターンと同じ手順になります。

おまけ「環境変数の設定」

ローカルで実行した際に使用していた環境変数をデプロイ後は切り替えたい場合のTipsです。
以下コマンドで、対象のLambda関数の環境変数を設定することができます。

例えば以下はHOGEという環境変数にtesttestという値を格納している例です。

aws lambda update-function-configuration --function-name 【FUCTION_NAME】 \
    --environment "Variables={HOGE=testtest}"

まとめ

今回はLambda用の関数をローカルで作成・検証を行い、AWS CLIからデプロイするまでの手順をまとめました。

想定していたより必要な手順が多く、ちょっととっつきにくい印象でしたが、後半のデプロイ部分などは実運用的にはCIでやってしまうパターンが殆どなので、そこまで大変ではないのかもしれません。

今回の内容が役立てば幸いです。

参考

Discussion