LambdaのPythonライブラリ追加にEFSを使ったときのメモ

AWSのLambdaでPythonのライブラリを読み込みたいときに、一番簡単な方法として、ライブラリをzipにしてレイヤーにアップロードするという方法があります。
ただしこれには制限があって、解凍後に500MBまでのファイルしかアップロードできません。
その解決策としては、
1. EFS(Elastic File System)を使う。
2. LamdaをDockerにする。

という方法があります。
ここでは、より簡単な、EFSを使う方法をとった時のメモを残したいと思います（自分がやった時のメモなので、より良い方法があるかもしれません）。

まず、インバウンドルールをタイプ:NFS、ポート範囲:2049にしたセキュリティグループを作ります。
![スクリーンショット 2022-12-07 6.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/4780f6ad-b871-f3a4-4517-adaf0c1dbc40.png)
使用するEFSを用意します。
EFSの管理画面にアクセスし、右上の「ファイルシステムの作成」をクリックします。
![スクリーンショット 2022-12-07 6.26.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/a92f79cc-9953-be58-975b-6ddc9aabde65.png)
適当な名前をつけて保存します。VPCはLambdaと同じにします。
![スクリーンショット 2022-12-07 6.39.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/6ce43bdc-8df6-56e1-35dc-11b232458c56.png)
ファイルシステムができたら、詳細画面の「ネットワーク」タブに移動し、「管理」をクリックします。
![スクリーンショット 2022-12-07 7.00.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/1ff76c5d-578c-84b0-8481-34cca5c79eaf.png)
先ほど作ったセキュリティグループを割り当てて保存します。
![スクリーンショット 2022-12-07 7.08.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/9d9c04b7-b115-d468-49a2-1cb8f196fb43.png)
「アクセスポイント」タブでアクセスポイントを作成します。例ではアクセス権限を777にしていますが、通常は755で良いと思います。
![スクリーンショット 2022-12-07 7.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/d308cbbb-caaa-d5e0-b399-ca1cd955b8cb.png)
アクセスポイントのDNSをコピーしておきます。
![スクリーンショット 2022-12-07 7.34.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/2b6b029d-5b38-6949-f265-43f144ae13d6.png)
EFSの作成はこれで完了です。

次に、EFS内に、ライブラリをインストールするディレクトリを用意します。
EFSへのライブラリのインストールはローカルからもできますが、Linuxからの方が良いとどこかで読んだので、今回はAWSのcluod9を使いました。
![スクリーンショット 2022-12-07 8.10.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/9ab93566-804b-704f-2e2f-d37f0d710122.png)
一応アップデート。
```
$ sudo yum -y update
```
この原稿の執筆時点ではcloud9にプリインストールされているPythonのバージョンは3.7でした。Lambdaでは3.9を使うので、cloud9にPython3.9をインストールします（Lambdaで使うバージョンに合わせます）。

まず、pythonのバージョン管理を行うpyenvをインストールします。
```
$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
```
pyenvにパスを通します。
```
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
```
確認。
```
$ pyenv --version
```
pyenvでpythonをインストールする際に必要な依存パッケージをインストールします。
```
$ sudo yum -y install gcc zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel tk-devel libffi-devel
```
インストールできるPythonのバージョンを確認。
```
$ pyenv install -l
```
python3.9をインストール（例では3.9.15をインストールしています）
```
$ pyenv install 3.9.15
```
バージョンを切り替えます。
```
$ pyenv global 3.9.15
```
切り替わったか確認。
```
$ pyenv versions
```
これで下準備は整ったので、次はEFSをマウントします。
EFSをマウントするディレクトリを作成します。
```
$ sudo mkdir -p /mnt/efs
```
作成したディレクトリの所有者とグループを変更。
```
$ sudo chown -R ec2-user:ec2-user /mnt
```
先ほど作成したEFSのディレクトリをマウントします。
[EFSのアクセスポイントのDNS]には、先ほどコピーしたEFSのアクセスポイントのDNSを入れてください。
```
sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport [EFSのアクセスポイントのDNS]:/ /mnt/efs
```
マウントされていることを確認。
```
$ df -Th
```
マウントしたディレクトリに移動し、ライブラリをインストールするディレクトリを作成。
ライブラリをインストールするディレクトリ名はpythonにしないといけないという説があるので（試していないので真偽は不明）、そのようにしています。
```
$ cd /mnt/efs
$ mkdir -p lib/python
```
インストールディレクトリに移動。
```
cd lib/python
```
必要なライブラリをインストールします（以下は例です）。
```
$ pip install numpy -t ./
$ pip install pandas -t ./
$ pip install scikit-learn -t ./
```
次にLambda側の設定を行います。
設定タブのアクセス権限のところにEFSへのアクセス権限がある実行ロールを設定します。
![スクリーンショット 2022-12-07 9.37.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/5e811806-b45b-f3fc-c1b0-0bf50d8c7636.png)
次に、VPCを確認します。
セキュリティグループの設定で、EFSとの通信ができるようになっていなければなりません。
![スクリーンショット 2022-12-07 9.44.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/e2966eaa-63e4-a5ad-7617-8c36287c91be.png)
最後に、ファイルシステムで、先ほど作成したEFSを設定します。
![スクリーンショット 2022-12-07 9.49.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3025503/187f23f3-7251-2199-9a6a-46517d62783d.png)

ここまでできたら、LambdaにEFSを読み込む記述を追加します。
```
import sys
sys.path.append("/mnt/efs/lib/python")

import numpy as np
import pandas as pd
etc...
```

これでEFSにインストールしたライブラリがLamdaから使えるようになります。
ただし落とし穴があって、S3への読み書きをするためのs3fsというライブラリだけは、EFSからの読み込みができませんでした。
原因は不明ですが、とりあえずs3fsだけレイヤーにアップロードするという方法で回避しました。

あと、余談ですが、EFSにファイルを保存するときは、/tmpというディレクトリしか使えません。
これは名前の通り一時保存用のディレクトリで、あるLambdaで保存したものを別のLamdaで読み出して使う、といった使い方はできません。
そういうことをしたい場合は、LambdaのDocker化を行うと良いと思います（そうすれば10GBまでのファイルを保持できます）。
LambdaのDocker化については、別の機会に書きたいと思います。