Published on

AWS SAMの使い方|ローカル開発からデプロイまで

本記事では、AWS SAM(Serverless Application Model)について解説します。

SAM は AWS 上でサーバレスアプリケーションを構築しようと思うとよくでてきます。SAM がどんなもので何ができるのかよくわかってなかったので勉強したいと思ってました。

また、サーバレスアプリケーションを構築する際に悩んだのがローカルの開発環境です。

Lambda 単体なら以前紹介した python-lambda-local や lambda-uploder を使った方法があります。

しかし、API Gateway などと組み合わせようと思うと少々無理があります。

そこで、サーバレスのおすすめローカル開発環境はなにか調べてみると AWS SAM に行き着きました。

調べた結果、Lambda 単体の場合でも sam が利用できたので sam が使えればlambda-uploderの方法はいらないかもです。

AWS SAM できること

まずは公式サイトの説明を見てみましょう。

迅速に記述可能な構文で関数、API、データベース、イベントソースマッピングを表現できます。リソースごとにわずか数行で、任意のアプリケーションを定義して YAML を使用してモデリングできます。デプロイ中、SAM が SAM 構文を AWS CloudFormation 構文に変換および拡張することで、サーバーレスアプリケーションの構築を高速化することができます。 (中略) SAM CLI により Lambda に似た実行環境が提供され、SAM テンプレートで定義されたアプリケーションの構築、テスト、デバッグをローカルで実行できます。 引用:AWS サーバーレスアプリケーションモデル

ここからわかるのは SAM でできることは以下の 3 点。

  • サーバレスアプリケーションをYAMLで定義
  • SAM構文で書かれたアプリケーションはCloudFormationに変換され、デプロイ可能
  • SAM CLIではローカルでテスト、デバックを行える

SAM では Infrastructure as Code(IaC)とローカルでの開発が可能になる。

やりたかったのはローカル開発環境構築なので使えそうです。

AWS SAM のインストール(ubuntu)

インストール方法は公式に丁寧に書いてあるので割愛します。

手順に従ってコマンドをコピペして実行すると問題なく sam コマンドが使えました。

Docker と Homebrew のインストールが必要になりますが、これもインストール方法に記載されています。

AWS SAM の使い方

公式テンプレートで学ぶ

最初ということで、公式が提供している template の Hello Wolrd を試してみます。こちらについても公式の解説があります。この記事の内容は公式の解説を噛み砕いたものです。

以下のコマンドでアプリ名やランタイム、template の選択ができます。template の選択で 1 つめの Hello World の template を選択しました。ランタイムは python 3.7 です。

sam init

上記コマンドを実行するといろいろ選択肢が提示されます。書かれている内容に従って選択していけばうまくいきました。

実行後には作成したアプリ名のフォルダができあがっているはずなので、そこに移動します。

フォルダ内は以下のような構成になっています。

.
├── README.md
├── events
│ └── event.json
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── template.yaml
└── tests
└── unit
├── __init__.py
└── test_handler.py

内容はけっこうシンプルです。eventsフォルダには Lambda の入力となるリクエスト情報が入ってました。hello_worldフォルダは python で書かれた Lambda の実体があります。testsはそのままの内容で hello_workd 関数のテストが記述されたファイルがあります。

次にやることは、ビルドを行い、アップロード用のファイル群を生成します。ビルドには以下のコマンドを使用します。

sam build

このコマンドを実行すると.aws_samフォルダが作成されます。

あとばデプロイするだけです。デプロイコマンドは以下です。

sam deploy --guided

コマンドラインでいろいろ聞かれるので素直に答えていけばデプロイができます。**デプロイされるものはそれぞれのリソース単体ではなく、CloudFormation のスタックとしてまとめて作成されます。**また、s3 に sam-cli 用のバケットが作成されます。

途中で CloudFormation のスタック名を指定しますが、この時アンダースコアなど使えない文字があるので注意してください。

なので、HelloWorld のテンプレートを使った場合に作成されるものをまとめると以下になります。

  • 【s3】sam-cli用バケット
  • 【CloudFormation】Lambda・API Gatewayを定義したリソースをまとめたスタック
  • 【API Gateway】Lambdaを呼び出すGET
  • 【Lambda】Pythonで書かれたhelloworld

これらの情報についてはsam initで作成したフォルダのtemplate.yamlに記述されてます。また、samconfig.tomlにスタック名、バケット名などの情報が記載されてます。

最後に私が気になっていたローカルでの開発方法を試してみます。

今回のテンプレートでは以下の 2 つのコマンドが使えます。

sam local start-api
sam local invoke "HelloWorldFunction" -e events/event.json

1つ目は Docker を使用して、API をホストするアプリケーションが起動します。実行するとアクセス先の URL が表示されるので、そこにリクエストを投げると API から Lambda が呼び出され実行することができます。

2つ目は単純にコマンド引数に指定したevent.jsonを Lambda に渡して Lambda 単体を実行できます。こちらは以前行っていたpython-lambda-localと同等のことができるので便利です。

最後に作成したリソースを削除する際は、CloudFormation で定義されているスタックを削除します。これでスタックで作成されたリソースをまるごと消せます。

SAM + API Gateway + Lambda で S3 のデータを JSON で取得する 

ざっくり使い方がわかったので、応用してみます。

やることは API の GET で Lambda を呼び出し、Lambda では S3 から取得したデータをまとめて json で返すようにします。 ほぼ Hello World の Lambda を変えるだけです。

手順は以下になります。今すぐ使うわけじゃないので、今回デプロイはしません。

  1. sam initで作業フォルダを作成
  2. template.yamlに定義したい関数情報を記述
  3. 関数の記述
  4. ローカルテスト

以下はsam init入力後の出力です。

$ sam init
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Which runtime would you like to use?
1 - nodejs12.x
2 - python3.8
3 - ruby2.7
4 - go1.x
5 - java11
6 - dotnetcore3.1
7 - nodejs10.x
8 - python3.7
9 - python3.6
10 - python2.7
11 - ruby2.5
12 - java8
13 - dotnetcore2.1
14 - dotnetcore2.0
15 - dotnetcore1.0
Runtime: 8
Project name [sam-app]: sam-twitter-process
Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
AWS quick start application templates:
1 - Hello World Example
2 - EventBridge Hello World
3 - EventBridge App from scratch (100+ Event Schemas)
Template selection: 1
-----------------------
Generating application:
-----------------------
Name: sam-twitter-process
Runtime: python3.7
Dependency Manager: pip
Application Template: hello-world
Output Directory: .
Next steps can be found in the README file at ./sam-twitter-process/README.md

次に template を書き換えます。といっても、パスやファイル名を変えるだけです。 あとは、Lmabda で pandas を使うので、requirements.txt に書き加えておきます。

Lambda をテストするにはsam buildしたのちに、sam local start-apiを行い、ブラウザから指定されたアドレスにアクセスすればできます。注意点はコード変更後にsam buildをしないと変更が反映されないです。

GET で取得した JSON の日本語が文字化けする

Lambda の返り値の JSON がコマンドラインでは問題なく出力されていたが、ブラウザで取得した JSON をみると日本語が文字化けしていました。

対策は pandas で直接df.to_jsonとするのではなく、いったん index を振り直してから辞書を作成します。その後 json.dumps で json にします。

これで日本語の文字化けが直りました。

df = df.reset_index(drop=True)
res = df.to_dict(orient="recodes")
return {
"statusCode": 200,
"headers": {
'Content-type': 'application/json;charset=UTF-8'
},
"body": json.dumps(res, ensure_ascii=False),
}

まとめ

SAM をなんとなく、敬遠していましたが試してみると使いやすくてよかったです。公式ドキュメントがけっこう充実していたのが大きいです。lambda-uploderはもう必要ないですね。template.yaml さえ書ければかなり便利に使えそうです。

少し残念だったのは、以下のように作成済みのバケットをイベントソースに指定できないことです。

NOTE: To specify an S3 bucket as an event source for a Lambda function, both resources have to be declared in the same template. AWS SAM does not support specifying an existing bucket as an event source.

SAM を使う場合は、S3 のバケット作成も含めて全体を新しく作る必要がありそうです。