🕌

lambrollでLambda関数をデプロイする

15 min read

はじめに

最近、fujiwaraさん作のlambrollというAWS Lambda専用のGo製デプロイツールを使ってみたので備忘も兼ねて記事にまとめたいと思います(認識の間違い等あればツッコミいただければと)

lambroll is a minimal deployment tool for AWS Lambda.

これが非常に便利で、Lambdaの作成、削除、デプロイ等が非常に簡単にできます。上がったLambda関数をローカルから簡単に呼び出すこともできるため開発も非常にしやすく、git管理も可能です。素晴らしいことにCircleCIのOrbも用意されているのでCircleCIで簡単にlambrollを使うことができます。では、早速最低限の使い方を説明していきます(今回は「見て理解する」スタイルで文字少なめで説明していきます)

レポジトリ

GitHub | fujiwara/lambroll

CircleCI Orb

CircleCI | Orb | fujiwara/lambroll@0.0.8

使い方

準備

環境変数

# ~/.zshrc
export AWS_ACCOUNT_ID=*************
export AWS_ACCESS_KEY_ID=*************
export AWS_SECRET_ACCESS_KEY=*************

インストール

# macOS and Linux
$ brew install fujiwara/tap/lambroll

help

$ lambroll

usage: lambroll [<flags>] <command> [<args> ...]

Flags:
  --help                      Show context-sensitive help (also try --help-long and --help-man).
  --log-level=info            log level (trace, debug, info, warn, error)
  --function="function.json"  Function file path
  --profile=""                AWS credential profile name
  --region=""                 AWS region
  --tfstate=""                URL to terraform.tfstate
  --endpoint=""               AWS API Lambda Endpoint
  --envfile=ENVFILE ...       environment files

Commands:
  help [<command>...]
    Show help.

  version
    show version

  init --function-name=FUNCTION-NAME [<flags>]
    init function.json

  list
    list functions

  deploy [<flags>]
    deploy or create function

  rollback [<flags>]
    rollback function

  delete [<flags>]
    delete function

  invoke [<flags>]
    invoke function

  archive [<flags>]
    archive zip

  logs [<flags>]
    tail logs using `aws logs tail` (aws-cli v2 required)

  diff
    show display diff of function.json compared with latest function

initをhelpしたい場合

$ lambroll init --help

usage: lambroll init --function-name=FUNCTION-NAME [<flags>]

init function.json

Flags:
  --help                         Show context-sensitive help (also try --help-long and --help-man).
  --log-level=info               log level (trace, debug, info, warn, error)
  --function="function.json"     Function file path
  --profile=""                   AWS credential profile name
  --region=""                    AWS region
  --tfstate=""                   URL to terraform.tfstate
  --endpoint=""                  AWS API Lambda Endpoint
  --envfile=ENVFILE ...          environment files
  --function-name=FUNCTION-NAME  Function name for initialize
  --download                     Download function.zip

【Lambda関数未作成の場合】 空のlambda設定ファイルを作成

testFunctionという名前でLambda関数作成。

$ lambroll init --function-name=testFunction

2021/10/27 07:48:12 [info] lambroll v0.11.9
2021/10/27 07:48:13 [info] function testFunction is not found
2021/10/27 07:48:13 [info] creating .lambdaignore
2021/10/27 07:48:13 [info] creating function.json
2021/10/27 07:48:13 [info] completed

$ ls
function.json   # 作成されたもの
$ cat function.json

{
  "FunctionName": "testFunction",
  "Handler": "index.handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/YOUR_LAMBDA_ROLE_NAME",
  "Runtime": "nodejs10.x",
  "Timeout": 3
}

【既にLambdaを作成している場合】 既存の設定を取得

色々ステップを省くために今回はこちらの方法でやっていきます。
(「関数の作成」画面で以下のようにLambda関数をコンソール上から作成しておいてください)

Lambda関数の設定を取得します。

# --function-nameはコンソール上で作成した関数名に合わせてください
$ lambroll init --function-name=testFunction

2021/10/27 08:13:20 [info] lambroll v0.11.9
2021/10/27 08:13:21 [info] function testFunction found
2021/10/27 08:13:21 [info] creating .lambdaignore
2021/10/27 08:13:21 [info] creating function.json
2021/10/27 08:13:21 [info] completed

$ ls
function.json
$ cat function.json

{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "testFunction",
  "Handler": "lambda_function.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

補足:既存のソースコードも取得したい場合は--downloadオプションも付けてください。

デプロイするプログラムを用意

  • HelloWorldを出力して、レスポンスとしてHello Responseを返すだけのプログラムを用意
$ cat hello_world.py

import json

def lambda_handler(event, context):
    print("Hello World")

    return {
        'statusCode': 200,
        'body': json.dumps("Hello Response")
    }

# ローカル確認用
if __name__ == '__main__':
    lambda_handler(None, None)
$ python3 hello_world.py 
Hello World

Lambdaから実行する関数を指定するためにHandler修正

$ vim function.json

{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "testFunction",
- "Handler": "lambda_function.lambda_function",
+ "Handler": "hello_world.lambda_handler",  # <file_path>.<function_name>
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

補足:appディレクトリ配下にhello_world.pyを置いていた場合は以下のようになる。

{
  ...
- "Handler": "hello_world.lambda_handler"
+ "Handler": "app/hello_world.lambda_handler"  
  ...
}

Lambda関数をデプロイ

デプロイ先を特定するためにLambdaの設定ファイルであるfunction.jsonを指定し、srcでデプロイしたいソースを指定することでそのディレクトリを自動的にzipしてデプロイする。

$ lambroll deploy --function=function.json --src="."

2021/10/27 08:26:45 [info] lambroll v0.11.9
2021/10/27 08:26:45 [info] starting deploy function testFunction
2021/10/27 08:26:45 [info] creating zip archive from .
2021/10/27 08:26:45 [info] zip archive wrote 366 bytes
2021/10/27 08:26:45 [info] updating function configuration 
2021/10/27 08:26:46 [info] updating function code 
2021/10/27 08:26:46 [info] deployed version 18 
2021/10/27 08:26:46 [info] updating alias set current to version 18
2021/10/27 08:26:46 [info] alias current is not found. creating alias
2021/10/27 08:26:46 [info] alias updated
2021/10/27 08:26:46 [info] completed

補足:--function--srcのデフォルト値はそれぞれfunction.json.となっているため、実は設定する必要はないので以下のようなやり方でも問題はない。

$ lambroll deploy

反映されたか確認

コンソールを確認すると反映されていることが分かります。

Lambda実行し動作確認

Testボタンを押すと結果が画面に出力されます。

Hello ResponseHello Worldが出力されているか確認。

Test Event Name
test

Response
{
  "statusCode": 200,
  "body": "\"Hello Response\""     <-------
}

Function Logs
START RequestId: fbbb422e-9197-4f98-9058-b77375d35b45 Version: $LATEST
Hello World     <-------
END RequestId: fbbb422e-9197-4f98-9058-b77375d35b45
REPORT RequestId: fbbb422e-9197-4f98-9058-b77375d35b45	Duration: 1.69 ms	Billed Duration: 2 ms	Memory Size: 128 MB	Max Memory Used: 38 MB	Init Duration: 178.60 ms

Request ID
fbbb422e-9197-4f98-9058-b77375d35b45

ローカルからLambda関数を呼び出す

実際にLambda関数を実行してテストしたい時に便利です。

# --function=function.jsonはデフォルトなので抜いてます
echo '{"event":"test"}' | lambroll invoke

2021/10/27 09:42:33 [info] lambroll v0.11.9
{"statusCode": 200, "body": "\"Hello Response\""}  <-------
2021/10/27 09:42:33 [info] StatusCode:200
2021/10/27 09:42:33 [info] ExecutionVersion:$LATEST
2021/10/27 09:42:33 [info] completed

レスポンスだけでなく、printで標準出力させたログ(Hello World)も確認したい場合は--log-tailを付けてください。

$ echo '{"event":"test"}' | lambroll invoke --log-tail

2021/10/27 09:50:25 [info] lambroll v0.11.9
{"statusCode": 200, "body": "\"Hello Response\""}
2021/10/27 09:50:25 [info] StatusCode:200
2021/10/27 09:50:25 [info] ExecutionVersion:$LATEST
START RequestId: 57c914e9-56b9-43da-9a89-9a19a9b55a93 Version: $LATEST
Hello World    <------------ 
END RequestId: 57c914e9-56b9-43da-9a89-9a19a9b55a93
REPORT RequestId: 57c914e9-56b9-43da-9a89-9a19a9b55a93	Duration: 0.85 ms	Billed Duration: 1 ms	Memory Size: 128 MB	Max Memory Used: 39 MB	
2021/10/27 09:50:25 [info] completed

存在しない関数名を指定してデプロイしたらどうなる

FunctionNamenewTestFunctionに変更してみる。

{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
- "FunctionName": "testFunction",
+ "FunctionName": "newTestFunction",
  "Handler": "hello_world.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

デプロイ

$ lambroll deploy

2021/10/27 21:19:57 [info] lambroll v0.11.9
2021/10/27 21:19:57 [info] starting deploy function testFunction
2021/10/27 21:19:57 [info] creating zip archive from .
2021/10/27 21:19:57 [info] zip archive wrote 366 bytes
2021/10/27 21:19:57 [info] updating function configuration 
2021/10/27 21:19:58 [info] updating function code 
2021/10/27 21:19:58 [info] deployed version 21 
2021/10/27 21:19:58 [info] updating alias set current to version 21
2021/10/27 21:19:58 [info] alias updated
2021/10/27 21:19:58 [info] completed

関数を一覧表示

$ lambroll list    

2021/10/27 21:20:55 [info] lambroll v0.11.9
{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "newTestFunction",
  "Handler": "hello_world.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}
{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "testFunction",
  "Handler": "hello_world.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

newTestFunctiontestFunctionの二つの関数が存在していることが確認できるかと思います。つまり、存在しない関数名をFunctionNameに設定してデプロイをすると新しく関数が作成されます。

作成したLambda関数を削除

作成したnewTestFunctionはもう必要がないので削除します。function.jsonFunctionNameに紐づいているので、関数名がnewTestFunctionになっていることを確認し、deleteを実行します。

$ lambroll delete

2021/10/27 21:26:13 [info] lambroll v0.11.9
2021/10/27 21:26:13 [info] deleting function newTestFunction 
2021/10/27 21:26:13 [info] completed

newTestFunctionが存在しないことを確認。

$ lambroll list

2021/10/27 21:27:50 [info] lambroll v0.11.9
{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "testFunction",
  "Handler": "hello_world.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

【Terraform】 Lambdaにデプロイするサイズが大きい場合の対処

AWS Lambdaの仕様上、ソースコードを圧縮して50MB以上あるとLambdaへ直接デプロイする時にエラーになるため、サイズが50MB以上となってしまった時の対策としてS3を使用してデプロイさせるようにします。

以下のようなTerraformのコードを用意

$ vim main.tf

provider "aws" {
    version = "~> 3.0"
    profile = "default"
    region = "ap-northeast-1"
}

resource "aws_s3_bucket" "lambda_source" {
    bucket = "lambda-source-xxxxxxx"
    acl = "private"
}

terraform apply実行してS3を作成。

$ terraform init

$ terraform apply

これでaws_s3_bucket.lambda_sourceというリソースが使用可能になったので、function.jsonにこれを組み込んでいきます。

function.jsonに以下の行を追加。

# S3Bucket以外もtfstateから参照させておくべきではありますが、
# 今回はシンプルさを保つためS3Bucketのみにとどめています。

{
  "Architectures": [
    "x86_64"
  ],
  "Description": "",
  "FunctionName": "newTestFunction",
  "Handler": "hello_world.lambda_handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::XXXXXXXXX:role/service-role/testFunction-role-2joa85l4",
  "Runtime": "python3.8",
  "Tags": {},
  "Timeout": 3,
  "TracingConfig": {
    "Mode": "PassThrough"
  },
+ "Code": {
+   "S3Bucket": "{{ tfstate `aws_s3_bucket.lambda_source.id` }}",
+   "S3Key": "lambda_source.zip"
+ }
}

S3経由でLambdaデプロイします。--tfstateオプションでtfstateファイルを指定してください。
補足:Terraformを使わず直接ハードコードすることもできますが、環境毎にデプロイ先のバケットを分けることができなくなるためオススメはしません。

$ lambroll deploy --tfstate=terraform.tfstate

2021/10/27 23:31:10 [info] lambroll v0.11.9
2021/10/27 23:31:10 [info] starting deploy function newTestFunction
2021/10/27 23:31:11 [info] creating zip archive from .
2021/10/27 23:31:11 [info] zip archive wrote 911 bytes
2021/10/27 23:31:11 [info] uploading function 911 bytes to s3://lambda-source-xxxxxxx/lambda_source.zip
2021/10/27 23:31:11 [info] object created
2021/10/27 23:31:11 [info] creating function 
2021/10/27 23:31:11 [info] deployed function version 2
2021/10/27 23:31:12 [info] creating alias set current to version 2 
2021/10/27 23:31:12 [info] alias created
2021/10/27 23:31:12 [info] completed

このようにすれば50MB以上のzipファイルもデプロイすることができます。

ちゃんとS3にアップロードされていますね。

CircleCIからlambrollを使用する

lambrollのCircleCI Orbが用意されているのでこれを使えば簡単にCircleCI上からlambrollが使えるようになります。

version: 2.1
orbs:
  lambroll: fujiwara/lambroll@0.0.8
jobs:
  deloy:
    docker:
      - image: cimg/base
    steps:
      - checkout
      - lambroll/install:
          version: v0.10.0
      - run:
          command: |
            lambroll deploy

参考: GitHub | fujiwara/lambroll

CircleCI Orb関連でエラーが出た場合

Third Party Orbを使用する場合、CircleCIでデプロイしようとすると以下のようなエラーが表示されます。

Orb fujiwara/lambroll@0.0.8 not loaded. To use this orb, an organization admin must opt-in to using third party orbs in Organization Security settings.

このように設定してからRerunをすれば通るようになります。

最後に

lambrollを一通り説明しましたが、これでもまだ必要最低限の機能しか説明していないため、もし細かい機能を知りたい場合はfujiwaraさんのGitHubレポジトリ「fujiwara/lambroll」に分かりやすく説明が書かれているため、こちらを参考にすると良いかと思います。それにしても、よくこれを開発したなと思うと同時に「やっぱりGoはやっておきたいな」と、lambrollのソースコードを読みながらつくづく思いました。今度時間が空いた時にでもやれたら良いですね。

それでは、lambrollの話でした。
皆も時間があれば使ってみると便利さに気づくかもしれません。

Discussion

ログインするとコメントできます