AWS SAMでコンテナLambdaを作成する

概要

LambdaのコードをZipではなく、コンテナ環境で実行することができるようになり、
ローカルでは動いたけどLambdaに置いたらライブラリがうまく動作しないなど、
依存関係がよくわからない問題が避けられそうに感じたので、
コンテナLambdaを構築してみることにしました。

参考にしたページは下記になります。

Lambda コンテナイメージの作成

AWS SAM でコンテナイメージのLambdaのベースを作成

SAMのインストール方法などは過去記事を参照

まず、 sam init コマンドでテンプレートを作成します。
今回は下記のように選択してコンテナタイプのLambdaを作成しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Hello World Example With Powertools
8 - Infrastructure event management
9 - Serverless Connector Hello World Example
10 - Multi-step workflow with Connectors
11 - Lambda EFS example
12 - DynamoDB Example
13 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: n

Which runtime would you like to use?
1 - aot.dotnet7 (provided.al2)
2 - dotnet6
3 - dotnet5.0
4 - dotnetcore3.1
5 - go1.x
6 - go (provided.al2)
7 - graalvm.java11 (provided.al2)
8 - graalvm.java17 (provided.al2)
9 - java11
10 - java8.al2
11 - java8
12 - nodejs18.x
13 - nodejs16.x
14 - nodejs14.x
15 - nodejs12.x
16 - python3.9
17 - python3.8
18 - python3.7
19 - ruby2.7
20 - rust (provided.al2)
Runtime: 16

What package type would you like to use?
1 - Zip
2 - Image
Package type: 2

Based on your selections, the only dependency manager available is pip.
We will proceed copying the template using pip.

Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: n

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: n

Project name [sam-app]: scanocr-sam

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

-----------------------
Generating application:
-----------------------
Name: scanocr-sam
Base Image: amazon/python3.9-base
Architectures: x86_64
Dependency Manager: pip
Output Directory: .
Configuration file: scanocr-sam/samconfig.toml

Next steps can be found in the README file at scanocr-sam/README.md


Commands you can use next
=========================
[*] Create pipeline: cd scanocr-sam && sam pipeline init --bootstrap
[*] Validate SAM template: cd scanocr-sam && sam validate
[*] Test Function in the Cloud: cd scanocr-sam && sam sync --stack-name {stack-name} --watch

生成されたプロジェクトのディレクトリ構成は以下のようになっています

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ tree
.
├── events
│ └── event.json
├── hello_world
│ ├── app.py
│ ├── Dockerfile
│ ├── __init__.py
│ └── requirements.txt
├── __init__.py
├── README.md
├── samconfig.toml
├── template.yaml
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_handler.py

4 directories, 12 files

生成されたDockerfileは以下ようになっています。

1
2
3
4
5
6
7
8
FROM public.ecr.aws/lambda/python:3.9

COPY app.py requirements.txt ./

RUN python3.9 -m pip install -r requirements.txt -t .

# Command can be overwritten by providing a different command in the template directly.
CMD ["app.lambda_handler"]

試しにローカル実行してみます。

1
2
3
4
$ cd scanocr-sam/
$ sam build
$ sam local invoke
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}END RequestId: 770d2ec0-6d05-4ee2-a2b6-df18fdefc43c

ローカルAPI実行も試してみます。

1
2
3
4
5
6
7
$ sam local start-api
Initializing the lambda functions containers.
Building image.................
Using local image: helloworldfunction:rapid-x86_64.

Containers Initialization is done.
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
1
2
$ curl http://localhost:3000/hello
{"message": "hello world"}