AWS LambdaでAmazon EFSを利用してみた

本記事は [株式会社ピーアールオー（あったらいいな！を作ります） Advent Calendar 2020](https://qiita.com/advent-calendar/2020/pro-japan1) の22日目です

昨日(21日目)は @koinori さんの「[【2020年12年版】Flutter + Android SDK + VS Code にてリモートホストにある Remote Container から別ネットワークの実機でデバッグ](https://qiita.com/koinori/items/a1cd4df34ce599a59175)」でした

是非、よろしければこちらもどうぞ
[2019年のカレンダーはこちら](https://qiita.com/advent-calendar/2019/pro-japan1)

# 経緯
プロジェクトでAWS Lambdaを利用してデプロイしているのだが、Lambda実行時に利用する「/tmp ディレクトリのストレージ」が上限の「512MB」を超えてしまいデプロイができなくなった

今後も開発は継続する予定で緊急な対応が必要となった

```
エラー原因：[Errno 28] No space left on device
```

## 対策
静的コンテンツをS3に配置し、デプロイするソースのファイル自体を減らせるといいのだが、5年以上開発しているプロダクトのため、その策を容易には取れないということで、**「AWS LambdaからAmazon EFSをマウントする」**にすることにした

## AWS Lambda × Amazon EFS
### Lambdaから利用するEFSを準備
すでに存在していたEFSにアクセスポイントを追加

| 項目名 | 設定値 | 補足 |
|:-----------|:------------|:------------|
| ファイルシステムID       | EFSのID        | -        |
| ルートディレクトリパス     | デフォルト      | EFSへアクセス後のルートディレクトリ。EFSを複数機能で共有してるときは設定して、他の機能で利用しているディレクトリに影響がないようにしておくのがいいかもしれない。        |
| ユーザーID     | 1001      | 必要に応じて、ユーザーIDを指定        |
| グループID     | 1001      | 必要に応じて、ユーザーIDを指定        |
| セカンダリグループID     | 設定なし      | -        |
| 所有者ユーザーID     | 1001      | 必要に応じて、ユーザーIDを指定        |
| 所有者グループID     | 1001      | 必要に応じて、ユーザーIDを指定        |
| アクセス許可     | 755      | 容易に編集はさせない        |

#### AWS Lambda 用セキュリティグループの作成
| 項目名 | 設定値 | 補足 |
|:-----------|:------------|:------------|
| セキュリティグループ名       | Lambdaが属するセキュリティグループと分かるような名前        | -        |
| 説明     | Lambdaが利用するセキュリティグループと分かるような名前      | -        |
| VPC     | Lambdaが属するVPC      | EFSと同じVPCを指定        |
| インバウンドルール     | 変更なし      | -        |
| アウトバウンドルール     | 変更なし      | -        |

### EFSのセキュリティグループにLambdaからのアクセスを許可する
| 項目名 | 設定値 | 補足 |
|:-----------|:------------|:------------|
| タイプ       | NFS        | -        |
| プロトコル     | TCP      | NFS選択で自動入力        |
| ポート範囲     | 2049      | NFS選択で自動入力        |
| ソース     | カスタム - AWS Lambda用に作成したセキュリティグループを指定      | -        |

### IAM設定
下記3つをLambdaのロールに追加

* AWSLambdaVPCAccessExecutionRole
* AmazonElasticFileSystemClientReadWriteAccess
* CloudWatchLogsFullAccess

### LambdaにVPC設定
| 項目名 | 設定値 | 補足 |
|:-----------|:------------|:------------|
| VPC       | AWS Lambda用セキュリティグループを作成時に指定したVPC        | ここ間違えるとLambdaからEFSにアクセスできません        |
| サブネット     | Lambdaが存在するサブネット      | 新たに作成してもいいし、既存のサブネットに混ぜてもいい        |
| セキュリティグループ     | AWS Lambda用に作成したセキュリティグループを指定      | -        |
| インバウンドルール     | 変更なし      | -        |
| アウトバウンドルール     | 変更なし      | -        |

### Lambdaにファイルシステム設定
| 項目名 | 設定値 | 補足 |
|:-----------|:------------|:------------|
| EFSファイルシステム       | LambdaからアクセスしたいEFSを指定        | -        |
| アクセスポイント     | Lambdaから利用するEFSを準備で作成したアクセスポイントを指定      | -        |
| ローカルマウントパス     | Lambda上でマウントするディレクトリパス      | -        |

### 参考
> https://docs.aws.amazon.com/ja_jp/efs/latest/ug/efs-access-points.html
> https://qiita.com/leomaro7/items/cfebe8a566b4aa8ee8e3

### :clap:これで無事にデプロイができるようになりました:clap:

## 乗り越えられなかった課題
Lambdaの中でディレクトリを作成して、処理完了後にそのディレクトリ削除をすることができなかった

```
エラー原因：[Errno 39] Directory not empty: 'pack'
[ERROR] OSError: [Errno 16] Device or resource busy: 'nfsXXXX' 
```
ちなみにPythonで「shutil.rmtree」を使っても消せませんでした
NFSプロトコルがディレクトリを掴んでいて削除できないという結論として、
処理完了後にディレクトリを削除する方式から処理開始時に前回分のディレクトリを削除するように修正することにしました

知識不足なのか、デプロイ処理がよくないのか、AWSの仕様なのか、ここだけは心残りです
っが、無事にデプロイできるようになったので助かりました:raised_hands:

#### 2020/12/24　追記
どうやら直接EFSにgitCloneをすると時間がかかることが判明
一度、LambdaのtmpストレージにgitCloneを実施してからEFSで作業をすることで回避
これで上記の「乗り越えられなかった課題」は回避され、EFS上のディレクトリは処理完了後に削除できた
※ただし、実際に「乗り越えられなかった課題」は解決されたわけではないと思われる(単に使い方を変えただけ)

### 参考
> https://www.drk7.jp/MT/archives/001341.html
> https://kurukuruwolf.hatenadiary.org/entry/20090713/p1

## 最後に
EFSなので、ランニングコストは試算しておきましょう:point_up: