- 新卒ブログとは?
- 2024年4月に新卒入社したLIGメンバーが、日々の学びや気づきを綴るブログです。彼らがふだんどんなことを学んでいるのか、気軽にのぞいてみてください。
Technology部テクニカルディレクターの山岡です。
今回は、案件で利用する機会の多い「AWS Lambda」について勉強したことの備忘録です。重要そうな用語のまとめ、簡単な実装手順のおさらいを目標としています。
Lambdaはサーバーレス
LambdaはAWSの提供するFaaS(Function as a Service)の一種で、サーバーレスアーキテクチャの提供を目的としたサービスです。
初期設定を済ませるだけで、仮想サーバー+OS+実行環境のセットを構築してくれるため、機能単位で関数を実行させたい場合(バッチ処理だとか、APIだとか)に向いています。
- 備忘録①
- サーバーレスは別にサーバーを使わないわけではない。サーバー周りをそれほど気にしなくていいだけ。
2024年8月現在、サポートされている実行環境は以下になります。これらの環境で動くプログラムであれば、特別なモジュールを必要としない限り、実行可能です。
名前 | サポートされているバージョン |
---|---|
Node.js | Node.js 18, Node.js 20 |
Python | Python 3.8, Python 3.9, Python 3.10, Python 3.11, Python 3.12 |
Java | Java 8, Java 11, Java 17, Java 21 |
.NET | .NET 6, .NET 8 |
Ruby | Ruby 3.2, Ruby 3.3 |
OS | Amazon Linux 2, Amazon Linux 2023 |
課金体系
Lambdaは料金体系もサーバーレスの概念に乗っ取り、関数を実行した時間✕Lambdaの性能(処理メモリ数)に応じて課金されます。
他のサーバーマネジメントサービス(EC2など)は、サーバーを起動している時間に応じた課金体系となりますが、Lambdaは使った分だけ課金となるため、そもそもサーバーの起動や終了を意識する必要がありません。
- 備忘録②
- 実行時間✕性能で利用料金が決まる
- 備忘録③
- 性能を上げて実行時間を短縮する方法もあり
Lambdaの重要用語集
次に、案件でよく耳にした重要用語をまとめます。Lambdaの基本設定やカスタマイズ性に関わる部分なので、知っておくと構築の際にちょっと役立つかもしれません。
デフォルトで設定が必要な箇所
トリガーとイベントソース
トリガーはLambdaを動かすイベントのことです。例を挙げると「S3のフォルダにファイルがアップロードされたらLambdaを起動」というように、柔軟にLambdaの起動タイミングを操作することができます。トリガーに設定するイベントのことを、イベントソースと呼びます。
- 備忘録④
- トリガーはLambdaの実行タイミングを設定する
Lambda関数とハンドラ関数
Lambdaで実行したいソースコードのことをLambda関数と呼びます。ハンドラ関数はその中でも、最初に実行されるソースコードです。
ランタイム
Lambda関数の実行環境を指します。上述したデフォルトでサポートされているランタイム以外にも、カスタムランタイムとして柔軟な設定が可能です。
利便性のためにカスタマイズする箇所
環境変数
Lamdaでは、ソースコードであつかう変数を環境変数として外部化することができます。機密情報やデフォルトでよく使われる設定値を環境変数化することで、セキュアかつ管理しやすいLambdaの作成が可能です。
レイヤー
複数のLambda関数でソースコードやモジュールを共有するための機能です。Lambdaは機能単位で作成することが一般的ですが、一部共通機能を持つ場合には、レイヤー化をしておくことで管理が楽になります。
タイムアウト時間
Lambda関数が実行を続けることのできる最大時間を指します。処理の大きなLambdaの場合には、デフォルトのタイムアウト設定だと途中で関数が強制的に終了されてしまうので、タイムアウト時間を伸ばしておきましょう。
メモリ
その名の通り、Lambda関数を実行する際の使用されるメモリ量です。処理が大きくて時間がかかってしまう場合には、増やしておきましょう。
Lambdaの実装
ここからは、公式ドキュメントに則ってLambdaの実装練習をしてみたいと思います。基本的なRESTful APIとして機能するLambdaの作成を目標とします。
構築の流れ
Lambdaの作成は以下の手順でおこないます。
- 関数名とランタイムの設定(※コンソール上では、デフォルトでLambda関数が自動作成されます)
- トリガーとカスタマイズ設定
- 必要時に応じて関数の更新
今回は学習目的のため、コンソール上の操作ではなく、AWS CLIを用いた構築に挑戦します。
事前準備
AWS CLIのためのアクセスキーとシークレットキーを作成してください。AWS CLIってなんぞやという方は、こちらの記事をご参照ください。
実装
Lambda関数用のソースコードの作成
今回はLambdaをAPI Gatewayと統合して、クエリパラメータで任意の数字を与えたら進数変換をしてくれるAPIを作成したいと思います。ランタイムはNode.jsを用います。
関数用に作成したコードは以下になります。
index.mjs
export const handler = async (event) => {
// クエリパラメータを取得する
const queryParams = event.queryStringParameters || {};
console.log('query:', queryParams);
// クエリパラメータから値を取得する
// 数値に変換
const number = parseInt(queryParams.number, 10);
// 基数として使うために数値に変換
const string = parseInt(queryParams.string, 10);
if (isNaN(number) || isNaN(string)) {
// クエリパラメータが不正な場合のエラーハンドリング
return {
statusCode: 400,
body: JSON.stringify({ error: 'Invalid input' }),
};
}
// 数値を指定された基数で文字列に変換
const binaryString = number.toString(string);
// レスポンスの作成
const response = {
statusCode: 200,
body: JSON.stringify(binaryString),
};
return response;
};
LambdaにアップロードするためにはZIPファイル化します。
>zip function.zip index.mjs
サービスロールの設定
Lambdaにアタッチするサービスロールを作成します。サービスロールは簡単にいうと、AWSサービスに権限を与える設定です。今回は、Lambdaの実行ログをCloud Watchで監視できるように、Cloud Watchにアクセスできるようにしました。
初期ロールの設定( role.json )
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
初期ロールの構築
>aws iam create-role --role-name {ロール名} --assume-role-policy-document file://role.json
基本的なLambda用権限をアタッチ
>aws iam attach-role-policy \
--role-name {ロール名}\
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
ここまでで、Lambda構築の準備は完了です。
Lambdaの作成
ここからは、Lambdaの作成手順に移ります。構築には以下のコマンドを使用します。
>aws lambda create-function \
--function-name {Lambda名} \
--zip-file fileb://{zipファイル} \
--handler index.handler \
--runtime nodejs20.x \
--role arn:aws:iam::{ユーザーアカウントID}:role/{ロール名}
- 備忘録⑤
- オプションメモ(公式ドキュメント)
–function | Lambdaの名前(最大64文字) |
---|---|
–zip-file | ZIPファイルをアップロード |
–handler | ハンドラ関数を定義(ZIP形式以外のアップロードなら不要らしい) |
–runtime | ランタイムを定義(ZIP形式以外のアップロードなら不要らしい) |
–role | IAMロールをアタッチ |
API Gatewayの構築
次に先ほど作成したLambdaをAPI形式で呼び出すために、エンドポイントを作成します。まずはAPI Gatewayの初期設定をおこないましょう。
>aws apigateway create-rest-api --name {API名}
以下のJsonが出力されるので、このうち{APIGatewayId}と{RootResorceId}をメモしておきます。
{
"id": {APIGatewayId},
"name": {API名},
"createdDate": {作成日時},
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"EDGE"
]
},
"disableExecuteApiEndpoint": false,
"rootResourceId": {RootResorceId}
}
次に、エンドポイントを作成します。一つのAPI Gatewayで複数のエンドポイントの管理が可能なため、なるべく識別しやすい{パス名}をつけるようにしましょう。
>aws apigateway create-resource \
--rest-api-id { APIGatewayId } \
--parent-id { RootResponceId } \
--path-part {パス名}
以下のJsonが出力されるので、{ResourseId}をメモしておきます。
{
"id": {ResourceId},
"parentId": {RootResponceId}、
"pathPart": {パス名}、
"path": "/{パス名}"
}
最後にエンドポイントのメソッドを設定します。今回はデータの取得を目指すので GETにします。
>aws apigateway put-method \
--rest-api-id {APIGatewayId} \
--resource-id {resourceId} \
--http-method GET \
--authorization-type "NONE"
LambdaとAPI Gatewayの紐付け
ここからは、API Gatewayで作成したエンドポイントをLambdaに紐づける作業をおこないます。
まずは、API Gatewayが受け取ったリソース(クエリパラメータなど)をLambdaに渡せるようにしましょう。
>aws apigateway put-integration \
--rest-api-id {APIGatewayId}
--resource-id {ResourceId} \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:{リージョン名}:{アカウントID}:function:{API名}/invocations
- 備忘録⑥
- オプションメモ(こちらの公式ドキュメント)
–integration-http-method | API GatewayからLambdaにデータを渡すときのメソッド |
---|---|
–uri | リクエストの転送先。最後のinvocationsはLambdaを実行してくださいという意味 |
次にLambda側にAPI Gatewayを認識させます。{stateId}は他と被らない任意の値を入力してください。
>aws lambda add-permission \
--function-name {Lambda名} \
--statement-id {stateId} \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:ap-northeast-1:{リージョン名}:{APIGatewayId}/*/GET/{パス名}"
- 備忘録⑦
- –source-arn の部分は、*(ワイルドカード)を入れる場合には””で囲んでおかないと怒られる。StringLikeのせい。
最後にAPI Gatewayをデプロイして終わりです。aws lambda add-permission コマンドの前におこなうことも可能です。その場合は、–source-arn の*部分が{stage名}でも認識されます。
>aws apigateway create-deployment \
--rest-api-id {APIGatewayId} \
--stage-name {stage名}
テスト
作成したAPIが正常に機能するかテストしてみました。結果はこのようになりました。
>curl "https://{APIGatewayId}.execute-api.{リージョン名}.amazonaws.com/{ステージ名}/{パス名}?number=6&string=2"
(結果)"110"
>curl "https://{APIGatewayId}.execute-api.{リージョン名}.amazonaws.com/{ステージ名}/{パス名}?number=8&string=2"
(結果)"1000"
しっかりとパラメータを受け取って、計算結果を受け取ることができました。
CloudWatch側でも、関数の実行がログとして記録されています。
おまけ
Lambdaの関数を更新したいときのコマンド
>aws lambda update-function-code \
--function-name {Lambda名} \
--zip-file {ZIPファイル名} \
--region {リージョン名} \
Lambdaの名前を部分してリスト化するコマンド(環境ごとにAPIを一括取得するのに便利)
>aws lambda list-functions \
--query 'Functions[?contains(FunctionName, `ここで名前を指定`)].FunctionName' --output text
最後に
今回はAWS Lambdaについて勉強したことと、APIとして利用する際に必要なコマンドについて備忘録的にまとめました。
AWS CLIで構築ができるようになると、コードを使い回すことができるので便利ですね。時間があるなら、シェルスクリプト化して爆速で構築できるようにしたい……。
LambdaはAWS SAMなどでも構築やテストが可能ですので、いつか触ってみたいなと思います。
目指せ、爆速Lambda構築。
ご拝読、ありがとうございました。
- 日本・フィリピン・ベトナムでの活躍チャンス
- 最先端技術と多言語環境での成長
- 有識者による月1回の勉強会
現在、海外拠点(フィリピン、ベトナム)に関われるエンジニアを募集しています。日本だけじゃなく世界で活躍することに興味のある方は、以下よりぜひご応募ください!