はじめに

docker-lambda で AWS Lambda Function を開発する方法を調べた。

TL;DR

  • docker-lambda を試した
    • 追加パッケージをインストールしてデプロイするまで
  • 試した言語は Python, Ruby, Node.js
  • docker-lambda はいいぞ

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. docker-lambda とは
    2. docker-lambda を試す
      1. Python
      2. Ruby
      3. Node.js
    3. 本番用ファイルを作成する
      1. Python
      2. Ruby
      3. Node.js
    4. 後片付け
  5. まとめ
  6. 参考文献

環境・条件

1
2
3
4
5
6
7
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.5
BuildVersion: 18F132

$ docker --version
Docker version 19.03.1, build 74b1e89

詳細

今回作ったコード類は以下に整理した。

17number/aws-lambda-docker-practice

docker-lambda とは

AWS Lambda Function を開発するための環境。

AWS Lambda Function は、中身は Amazon Linux なので Mac などのローカル環境で開発すると、環境差分でうまく動かないことがある(らしい)。

そこで「Amazon Linux の環境を Docker で構築して、Docker 内で各種パッケージをインストールして、それらをまとめて zip で固めて〜」ということを、簡単に行えるようにしたものが docker-lambda(という認識)。

docker_lambda_supports.png

今回試した Python, Ruby, Node.js 以外のランタイム(golang, Java など)にも対応している。

docker-lambda を試す

事前に Docker のインストールが必要。

基本形は以下の通り。ランタイム、ファイル名、関数名を指定すれば OK。

1
$ docker run --rm -v <code_dir>:/var/task lambci/lambda:<runtime> <filename>.<funcname>

ランタイムは Ruby2.5 で、./function.rbhandler メソッドを使う場合は以下の通り。

1
$ docker run --rm -v "$PWD":/var/task lambci/lambda:ruby2.5 function.handler

docker image がローカルに無い場合には、コマンド実行時に pull してくれる。

Python

function.py として保存、コードの引用元はここ

1
2
3
4
5
6
def my_handler(event, context):
message = 'Hello {} {}!'.format(event['first_name'],
event['last_name'])
return {
'message' : message
}

イベントの情報を利用するので、カスタムイベントを実行時に指定。

1
2
$ docker run --rm -v "$PWD":/var/task lambci/lambda:python3.7 function.my_handler \
'{"first_name": "Kenji", "last_name": "Yamada"}'

実行結果。

1
2
3
4
START RequestId: 12345678-1234-1234-1234-1234567890ab Version: $LATEST
END RequestId: 12345678-1234-1234-1234-1234567890ab
REPORT RequestId: 12345678-1234-1234-1234-1234567890ab Duration: 8.54 ms Billed Duration: 100 ms Memory Size: 1536 MB Max Memory Used: 22 MB
{"message":"Hello Kenji Yamada!"}

Ruby

function.rb を作成。コードはこちらからコピー。

1
2
3
4
5
require 'json'

def handler(event:, context:)
{ event: JSON.generate(event), context: JSON.generate(context.inspect) }
end

docker-lambda で実行。

1
$ docker run --rm -v "$PWD":/var/task lambci/lambda:ruby2.5 function.handler

実行結果。

1
2
3
4
START RequestId: 12345678-1234-1234-1234-1234567890ab Version: $LATEST
{"event":"{}","context":"\"#\u003cLambdaContext:0x0000557f543ed018 @clock_diff=1565777809960, @deadline_ms=1565801560033, @aws_request_id=\\\"12345678-1234-1234-1234-1234567890ab\\\", @invoked_function_arn=\\\"arn:aws:lambda:us-east-1:123456789:function:test\\\", @log_group_name=\\\"/aws/lambda/test\\\", @log_stream_name=\\\"2019/08/14/[$LATEST]01234567890abcdef01234567890abcde\\\", @function_name=\\\"test\\\", @memory_limit_in_mb=\\\"1536\\\", @function_version=\\\"$LATEST\\\"\u003e\""}
END RequestId: 12345678-1234-1234-1234-1234567890ab
REPORT RequestId: 12345678-1234-1234-1234-1234567890ab Duration: 5.71 ms Billed Duration: 100 ms Memory Size: 1536 MB Max Memory Used: 18 MB

Node.js

function.js として保存、コードの引用元はここ

1
2
3
4
exports.handler =  async function(event, context) {
console.log("EVENT: \n" + JSON.stringify(event, null, 2))
return context.logStreamName
}

イベントの情報を利用するので、カスタムイベントを実行時に指定。

1
2
$ docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs10.x function.handler \
'{"runtime": "nodejs10.x"}'

実行結果。

1
2
3
4
5
START RequestId: 12345678-1234-1234-1234-1234567890ab Version: $LATEST
} "runtime": "nodejs10.x" 12345678-1234-1234-1234-1234567890ab INFO EVENT:
END RequestId: 12345678-1234-1234-1234-1234567890ab
REPORT RequestId: 12345678-1234-1234-1234-1234567890ab Duration: 19.72 ms Billed Duration: 100 ms Memory Size: 1536 MB Max Memory Used: 44 MB
"2019/08/14/[$LATEST]01234567890abcdef01234567890abcd"

本番用ファイルを作成する

追加パッケージをインストールして、bitFlyer Ticker API を実行するだけの Lambda function を作っていく。

bitFlyer - ticker

Python

pip install するパッケージは requirements.txt に書き出す。今回は requests をインストール。

1
requests

lambda_function.py を作成。

1
2
3
4
5
import requests

def lambda_handler(event, context):
res = requests.get("https://api.bitflyer.com/v1/ticker?product_code=BTC_JPY")
return res.json()

Dockerfile を作成。

1
2
3
4
5
6
7
8
9
FROM lambci/lambda:build-python3.7
ENV LANG C.UTF-8
ENV AWS_DEFAULT_REGION ap-northeast-1

ADD . .

CMD pip3 install -r requirements.txt -t /var/task && \
zip -9 deploy_package.zip lambda_function.py && \
zip -r9 deploy_package.zip *

ビルド。

1
2
3
$ docker build -t aws-lambda-python3.7-test .
Sending build context to Docker daemon 12.8kB
...

デプロイファイルを作成。

1
2
3
$ docker run --rm -v "$PWD":/var/task aws-lambda-python3.7-test:latest
Collecting requests (from -r requirements.txt (line 1))
...

成功すると deploy_package.zip ができている。unzip -Z で中身を確認。

1
2
3
4
5
6
7
8
9
10
11
$ unzip -Z deploy_package.zip
Archive: deploy_package.zip
Zip file size: 965384 bytes, number of entries: 309
-rw-r--r-- 3.0 unx 153 tx defX 19-Aug-15 09:54 lambda_function.py
-rw-r--r-- 3.0 unx 391 tx defX 19-Aug-15 21:39 Dockerfile
-rw-r--r-- 3.0 unx 9 tx stor 19-Aug-15 09:48 requirements.txt
drwxr-xr-x 3.0 unx 0 bx stor 19-Aug-15 21:39 bin/
-rwxr-xr-x 3.0 unx 231 tx defX 19-Aug-15 21:39 bin/chardetect
drwxr-xr-x 3.0 unx 0 bx stor 19-Aug-15 21:39 certifi/
-rw-r--r-- 3.0 unx 52 tx stor 19-Aug-15 21:39 certifi/__init__.py
...

aws lambda create-function で Lambda function を作成。ロールは IAM で事前に作成済み。

1
2
3
4
5
6
7
8
$ aws lambda create-function \
--function-name docker-lambda-python-test \
--zip-file fileb://deploy_package.zip \
--handler lambda_function.lambda_handler \
--runtime python3.7 \
--timeout 10 \
--memory-size 1024 \
--role arn:aws:iam::123456789012:role/lambda-execute-role

Lambda Console で開いて、テストを実行。

aws docker lambda python

Ruby

bundle initGemfile を作成。

1
$ bundle init

Gemfile を編集、faraday を追加。

1
2
3
4
5
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "faraday"

lambda_function.rb を作成。

1
2
3
4
5
6
require("faraday")

def handler(event:, context:)
res = Faraday.get "https://api.bitflyer.com/v1/ticker?product_code=BTC_JPY"
JSON.parse res.body
end

Dockerfile を作成。

1
2
3
4
5
6
7
8
FROM lambci/lambda:build-ruby2.5
ENV LANG C.UTF-8
ENV AWS_DEFAULT_REGION ap-northeast-1

ADD . .

CMD bundle install --path vendor/bundle && \
zip -r9 deploy_package.zip .

ビルド。

1
2
$ docker build -t aws-lambda-ruby2.5-test .
Sending build context to Docker daemon 4.096kB

デプロイファイルを作成。

1
2
3
$ docker run --rm -v "$PWD":/var/task aws-lambda-ruby2.5-test:latest
Fetching gem metadata from https://rubygems.org/.........
...

成功すると deploy_package.zip ができている。

aws lambda create-function で Lambda function を作成。

1
2
3
4
5
6
7
8
$ aws lambda create-function \
--function-name docker-lambda-ruby-test \
--zip-file fileb://deploy_package.zip \
--handler lambda_function.handler \
--runtime ruby2.5 \
--timeout 10 \
--memory-size 1024 \
--role arn:aws:iam::123456789012:role/lambda-execute-role

Lambda Console で開いて、テストを実行。Python と同じ結果なので、画像は省略。

Node.js

npm initpackage.json を生成。

1
2
3
$ npm init
package name: (nodejs) docker-lambda-nodejs-test
...

npm-add-dependencies で、axiospackage.json に追加。

npm-add-dependencies については npm でパッケージをインストールせずに package.json の dependencies を更新する方法 にまとめてある。

1
2
3
4
$ npm-add-dependencies axios
Adding dependencies to 'dependencies'...
Processed: axios, latest version: 0.19.0
Done.

npm-add-dependencies 実行後の package.json の diff はこうなる。

1
2
3
4
5
6
-  "license": "ISC"
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^0.19.0"
+ }
}

index.js を作成。

1
2
3
4
5
6
7
const axios = require("axios");

exports.handler = async function(event, context) {
const res = await axios.get("https://api.bitflyer.com/v1/ticker?product_code=BTC_JPY");
console.log(res.data);
return res.data;
}

Dockerfile を作成。

1
2
3
4
5
6
7
FROM lambci/lambda:build-nodejs10.x
ENV LANG C.UTF-8
ENV AWS_DEFAULT_REGION ap-northeast-1

ADD . .

CMD npm install && zip -r9 deploy_package.zip .

ビルド。

1
2
3
$ docker build -t aws-lambda-nodejs10.x-test .
Sending build context to Docker daemon 4.096kB
...

デプロイファイルを作成。

1
2
3
$ docker run --rm -v "$PWD":/var/task aws-lambda-nodejs10.x-test:latest
added 5 packages from 8 contributors and audited 5 packages in 1.483s
...

成功すると deploy_package.zip ができている。

aws lambda create-function で Lambda function を作成。

1
2
3
4
5
6
7
8
$ aws lambda create-function \
--function-name docker-lambda-nodejs-test \
--zip-file fileb://deploy_package.zip \
--handler index.handler \
--runtime nodejs10.x \
--timeout 10 \
--memory-size 1024 \
--role arn:aws:iam::123456789012:role/lambda-execute-role

Lambda Console で開いて、テストを実行。Python と同じ結果なので、画像は省略。

後片付け

docker-lambda の使い方はわかったので、不要な Lambda Function は削除しておく。

1
2
3
$ aws lambda delete-function --function-name docker-lambda-python-test
$ aws lambda delete-function --function-name docker-lambda-ruby-test
$ aws lambda delete-function --function-name docker-lambda-nodejs-test

まとめ

  • docker-lambda を試した
    • 追加パッケージをインストールしてデプロイするまで
  • 試した言語は Python, Ruby, Node.js
  • docker-lambda はいいぞ

参考文献

関連記事