はじめに
Lambdaにはファイルサイズ容量が10MBを超えるとアップロードが出来ません。大きなLambdaの場合にはLambda Layersという収め切れなかったコードをs3経由でマージすることで若干ファイルサイズを増やす事が可能です。それでも250MBが限界です。
例えばNode.jsのLambdaを作る際、インポートするライブラリが増えると250MBを超えるということは存外よく起こります。その場合Lambdaを諦めるのも手ですが強引にLambdaを利用したまま実装する方法もあります。
250MB以上のLambdaをアップロードしたい場合はEFSを使用します。LambdaからNFSプロトコルでEFSをマウントして、EFSにあらかじめアップロードしておいたファイルを共有するということです。今回はEFSを利用したLambda作成方法をまとめたいと思います。
準備
詳細の説明は割愛しますが下記の用意をお願いします。
- VPC
- サブネット
- AZは「-1a」を指定
- EFSを同じAZに作る必要があるがAZによってはEFSを作成出来ないため
- NAT Gateway
- Lambda関数にインターネットアクセスをさせたいのでNAT Gatewayへのルートテーブルを作成して下さい
- セキュリティグループ
- Lambda用
- インバウンド
- ルール無し
- アウトバウンド
- 全てのトラフィック
- インバウンド
- EFS用
- インバウンド
- NFS(2049)/TCP, ソースはLambda用セキュリティグループIDのみ(後でEC2用に追加する)
- アウトバウンド
- 全てのトラフィック
- インバウンド
- Lambda用
EFSを作成
- ファイルシステムを作成
- 既存のVPCを選択
- アクセスポイントを作成
- 上のファイルシステムを選択
- EC2を作成
- EFSにnode_modulesを書き込むため
EFS用のインバウンドルールのソースにEC2のセキュリティグループIDを追加する。
$ sudo yum update -y
$ sudo yum install -y amazon-efs-utils
$ mkdir data
$ sudo mount -t efs [ファイルシステムID] ./data/
$ df -Th ※dfしてマウントされているかを確認する
Node.jsライブラリを作成
nvmをインストールする。
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
$ . ~/.nvm/nvm.sh
最新版のNode.jsをインストールする。
$ nvm install node
npmをインストールする。
$ sudo amazon-linux-extras install epel -y
$ sudo yum install -y npm --enablerepo=epel
package.jsonをマウントディレクトリ配下に作成する。
{
"dependencies": {
"request": "^2.88.2"
}
}
下記コマンドを実行してライブラリをインストールする。
$ sudo npm --prefix ./data install
ディレクトリ階層は下記の通りになります。
data
├── node_modules
└── package.json
Lambdaの作成
- ネットワーク設定
- 作成したVPCを指定
- 作成したサブネットを指定
- セキュリティグループにLambda用のものを指定
- 以下の3ポリシーをアタッチしたLambda用の新規ロールを作成
- AWSLambdaVPCAccessExecutionRole
- AmazonElasticFileSystemClientReadWriteAccess
- CloudWatchLogsFullAccess
ファイルシステム設定
先程作成したファイルシステム、アクセスポイントを指定します。ローカルマウントパスを「/mnt/node_modules」と入力します。
また環境変数に下記を追加します。
NODE_PATH | /mnt/node_modules/node_modules |
Lambdaコードソース
肝心のLambdaのソースコードは下記の通りです。マウントしたファイルシステムからrequestモジュールを読み込み、非同期でGETリクエストを送信します。Lambdaはその結果をresponseとして返すという内容です。
const request = require('request');
exports.handler = async (event, context) => {
const response = {
statusCode: 200,
body: JSON.stringify(await new Promise((resolve, reject) => {
request.get('https://google.com', (err, resp, b1) => {
if (err) {
reject(err)
} else {
resolve(resp)
}
})
})),
};
return response
};
テストを実行してエラーが怒らずそれっぽい長大なレスポンスが返ってきたら成功です。エラーが起きる場合はタイムアウト時間を伸ばしたり、メモリ量を増やしてみて下さい。
API Gateway
ここからは蛇足ですがAPI Gatewayも利用して見たいと思います。
APIタイプをRESTでAPIを作成します。
次に「アクション」からメソッド作成で「POST」メソッドの設定を行います。「統合タイプ」をLambdaに設定して、実行するLambdaに今回作成したものを指定します。ここまでで一旦「アクション」から「APIデプロイ」を行います。ステージは新規作成します。
次にAPIキーでの認証を設定したいので使用量プランを作成します。スロットリング・クォータは無効にします。ステージは先程作成したものを紐付けます。次にAPIキーからAPIキーを自動生成した後、使用量プランに追加します。
最後にPOSTメソッドの設定に戻り、「メソッドリクエスト」から「APIキーの必要性」をtrueにして完了です。
Lambdaの画面に戻り下図のようになっていることを確認します。上手く紐付いている場合は「APIキー」という項目が出現します。設定が完了したらcurl
を叩いてみましょう。
$ curl -X POST --header "x-api-key:[API キー]" [API エンドポイント]
終わりに
Lambdaのファイルサイズ制限は厳しいですが、EFSを利用すれば大きなファイルサイズのライブラリも使う事が出来ます。環境を作るのが一気に面倒になりますが……