python3.12 のLambda Layerの作成方法

# 初めに

Lambda のPython 3.12 対応につき、Layer の作成方法を調査したので**備忘録的に残します**
基本的にはpython3.11 の時と変わらないと思いました

:::note
- .zip形式での作成 とします
- コンテナでの作成方法は対象外
:::

https://aws.amazon.com/jp/about-aws/whats-new/2023/12/aws-lambda-support-python-3-12/



ポイント
- Python3.12 はAmazon Linux 2023で動く
- Windows環境でのLayer 作成は困難なので、Windows環境しかない人はどうにかLinux環境を構築する




# 作業の大まかな流れ

大まかな流れは以下の通りです：
- Lambda実行環境の確認
- (Linux 環境を入手)
- Python3.12 と必要なパッケージのインストール
- 目的の pip のパッケージをディレクトリににインストール
- レイヤーの作成


Linux 環境が無い場合は Cloud9 が手っ取り早いです

:::note
python3.11 の時はローカルのUbuntuで作成したLayerも問題なく動作しました（OSが違うにも拘らず）が、python3.12はまだ試せていません
:::

> 補足
> Amazon Linux 2023 は Fedoraベース
> Amazon Linux2 は CentOSベース




# Lambda実行環境の確認

公式が環境情報を公開していますが、念のため Lambda の実行環境を確認します

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-python.html

## 確認方法
適当なLambda関数を作成して確認します

```python
import botocore
import boto3
import sys

def lambda_handler(event, context):
    print(sys.version)			# pythonのバージョン
    print(boto3.__version__)
    print(botocore.__version__)
```


## 2024.3.1の確認結果:
|Lambda Runtime|Python|AMI|botocore|boto3|
|--------------|------|---|---|---|
|Python3.12    |Python 3.12.1|	Amazon Linux 2023| 1.31.72 | 1.28.72|	
|Python3.11    |Python 3.11.6|	Amazon Linux 2	 |	1.30.1 | 1.27.1	|



# pythonのインストール

ここからはLayer を作成するLinux マシンで作業します
python のインストールは pyenv を使うと楽なようです

```bash
# pyenvをインストール
sudo git clone https://github.com/pyenv/pyenv.git /usr/bin/.pyenv

# 必要なディレクトリを作成（補足：情報元不明なため、理由が分かりませんが）
sudo mkdir /usr/bin/.pyenv/shims
sudo mkdir /usr/bin/.pyenv/versions

# 所有者の変更
sudo chown -R myuser:myuser /usr/bin/.pyenv/		# 分からなければwhoamiで確認
sudo chown -R ec2-user:ec2-user /usr/bin/.pyenv/	# ec2/cloud9 の場合

# PATH 関連の作業
echo 'export PYENV_ROOT="/usr/bin/.pyenv"' >> ~/.bash_profile
echo 'export PATH="${PYENV_ROOT}/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile

# .bash_profile を一度実行しておく
source ~/.bash_profile
```

バージョンを指定してpythonをインストール
```bash
# バージョンを指定してpythonをインストール
pyenv install 3.12.1

# デフォルトのpythonの変更
pyenv global 3.12.1

# 確認
python3 -V
```

必要パッケージのインストール
補足：仮想環境(python-venvなど) を使っても良いようです
```bash
# pip のアップデート
python3 -m pip install -U pip

# 必要パッケージのインストール　※ランタイム毎に違う
python3 -m pip install botocore==1.31.72
python3 -m pip install boto3==1.28.72
```


# pip のパッケージをディレクトリにインストール

今回、仮に requests 用のLayerを作成するものとします 
先に以下のようなディレクトリ構成にしておきます


```
~/
+ requests-layer-py312/
  + python/					ディレクトリ名はpythonでないとダメ
```

公式（ディレクトリ名）：

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/packaging-layers.html#packaging-layers-paths

<br>

パッケージインストール
```bash
# この例でいう requests-layer-py312 を現在地(cwd)にする
cd ~/requests-layer-py312

# -t オプションでディレクトリにインストール
python3 -m pip install \
  --platform manylinux2014_x86_64 \		# --platformは必ず指定する
  --implementation cp \
  --only-binary=:all: \
  -t ./python/ \
  requests                              # 目的のパッケージ
```

公式：

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-package.html#python-package-native-libraries

# レイヤーの作成
ここまで来たら簡単です

## .zipファイルを作成 
まずは.zip アーカイブを作成
```bash
# 本ディレクトリをzipアーカイブ（最後ドット=現在ディレクトリ）
zip -r ../requests-layer-py312.zip .		
```

補足
- EC2などを使っているときはscpなどを使ってダウンロード
```scp my-ssh-config:/home/ec2-user/requests-layer-py312.zip ~/```
- Cloud9の場合はファイルエキスプローラから右クリックでダウンロード可能

## レイヤーの作成
マネージメントコンソールで作業するものとします
Lambda -> ナビゲーションペイン：レイヤー -> レイヤーの作成
-> 作成した .zip ファイルをアップロード


以下、任意で（但し推奨）
- 「互換性のあるアーキテクチャ」　x86_64にチェック（もちろんx86_64の時）
- 「互換性のあるランタイム」で任意のPythonバージョンを指定

-> 作成


## レイヤーの追加
Lambda -> ナビゲーションペイン：関数 -> 任意の関数を選択 -> 「コード」タブ -> 「レイヤー」セクション（コードソースの下の方）-> レイヤーの追加  

レイヤーソースは「カスタムレイヤー」を選択 -> 先ほど作成したレイヤー（とバージョン）を選択


## 確認
最後に Lambda で動作確認をします

```python
import json
import requests

def lambda_handler(event, context):
    # urlはなんでも良いですが
    res = requests.get('http://httpbin.org/ip')
    return {
        'statusCode': 200,
        'body': json.dumps(res.text)
    }
```

# 補足

## 自作モジュールをLayerに含めたい場合
これは全然難しい事はなく、他のpipモジュール群と一緒にpython ディレクトリに.pyファイルを作成するだけで良いようです

```
~/
+ requests-layer-py312/
  + python/
    + requests/        　　　# 今回インストールした pip モジュール
    ...                      # 他にもディレクトリなどがあるとする
    + my_custom_module.py    # 自作モジュール
```

蛇足ですが、下の「Windows での .zip化」問題があり、この事実に気が付くのに時間をかなり取られました...


## Windows での .zip化について
Windows のファイルエキスプローラーから「.zipファイルに圧縮をする」を行うとディレクトリ構成が変化してしまう為、上手く Layer が作成できません
（前から思ってましたが、M〇製品にはたびたび奇妙な仕様が見受けられますね...)

<br>

【Linux環境で作ったファイルのディレクトリ構成】
```
requests-layer-py312/
  + python/
```
↓　Windows環境で.zipファイルを作成すると以下のようになってしまう！！ 
 
```
[ requests-layer-py312.zip ]
requests-layer-py312/
  + requests-layer-py312/
    + python/
```

### 対応方法
間抜けな方法しか思いつきませんでしたが、（Windows 環境にあまり明るくない:sweat_smile:）
思いついたのは python ディレクトリをアーカイブ化する方法

【Linux環境で作ったファイルのディレクトリ構成】
```
requests-layer-py312/
  + python/        　　　　← 【これを .zip アーカイブする】
```
↓
すると以下のようなディレクトリ構成になる事が期待されます
```
[ python.zip ]
python/
  + python/
```
なんとも間抜けな方法ですが、動作確認できましたので、これを解決方法としたいと思います。


## Amazon Linux 2 での OpenSSL 問題
簡単に言うと、Amazon Linux 2 初期状態では ```pyenv install 3.11.6``` が失敗します
python3.11 のインストールには OpenSSL が必要です

- 厳密にはpython3.10 以降でOpenSSL 1.1.1以上が必要
資料：https://peps.python.org/pep-0644/
- 簡単に言うと openssl11 をインストールすれば良い


### 対策1
【 Amazon Linux 2023 で ```pyenv install 3.11.6``` を実行してLayerを作成する 】
=>「多分うまく行かんだろうが...」と思ってましたが動作しました:joy:

（Layer関係で最初に読んだ記事に「環境を合わせろ」とありましたが、上述のように Ubuntu でも成功したし、案外何でも良いのでは？様子をみますが）

### 対策2
【 Amazon Linux 2 に OpenSSL を（再）インストール 】

以下でうまく行きました：
```
sudo yum remove -y openssl-devel
sudo yum install -y openssl11 openssl11-devel
```

参考：https://qiita.com/rururu_kenken/items/8c2491b41ff20fa57f20

蛇足：Amazon Linux 2　での最初の一歩でうまく行かなかったので「対策１」を発見した訳ですが、対策2で作成したLayerも上手く動作しました

## Linux 環境にWSLを使う場合

Layer 作成に必要な Linux ですが、Windowsの方は WSL でも良いようです。
昔使って使いにくい印象のままでしたが、近年では VSCode の Remote Developmentで使えたり、大分使いやすくなっているようです。（WSLの使用方法は割愛）

ポイント
- Layer 作成時にEC2インスタンスの料金を払いたくない場合
- Cloud9の方が遥かに楽ではある（作業工数の削減になる）
- 以下のパッケージを補う必要がある（ディストリビューションが Debian/Ubuntu の場合）
```bash
sudo apt install build-essential libssl-dev libffi-dev liblzma-dev zlib1g-dev 
 bzip2-dev zip
```

# 参考資料
公式

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/chapter-layers.html


主に作成手順

https://qiita.com/__DASHi__/items/268062f0dba0e93170f2



python 3.10をAWS EC2にinstall

https://qiita.com/c0ridrew/items/0ea97e1c7ef42c97ce80