AWS Lambda Layersでライブラリを共通化

# Lambda Layersで解決できること

Lambdaの実行環境には追加ライブラリが存在しないため、ライブラリを使用する際はデプロイパッケージにライブラリを含める必要がありました。
しかし[AWS Lambda Layers](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html)を使用することで、ライブラリなどの共通コンテンツをレイヤーとして作成することで、パッケージにライブラリを含める必要がなくなりました。

# Lambda Layers

Lambda Layersとは、複数のLambda関数で外部ライブラリやビジネスロジックを共有できる仕組みです。
イメージはこんな感じです。

![9.png](https://qiita-image-store.s3.amazonaws.com/0/239179/9324220a-e369-1bcb-12dc-40fc37570514.png)


使用するライブラリや共通のビジネスロジックをZIPアーカイブし、Layerに追加することができます。
デプロイパッケージの容量を少なくすることができるためLambdaのソースコードの管理が楽になります。
また依存関係をインストールしてパッケージ化する際に発生するエラーを回避することや、ライブラリのビルドの手順を省くこともできます。

# 制限

1つのLambda関数では5つのLayerのみ使用することができます。
また、Lambda関数とLayerの解凍後の合計サイズが250MB以下となる必要があります。

# Lambda Layersの配置先

作成したLayerは、Lambdaの実行環境の`/opt`ディレクトリに展開されます。
`/opt`ディレクトリ以下に、ランタイムの言語ごとのディレクトリが構成されているので、ランタイムに合わせてLayerを構築する必要があります。
Pythonを例にとると、Lambdaの実行環境は`/opt/python/`とディレクトリが構成されているため、作成するLayerは展開される構成が`/opt/python/"作成したLayer"`となる必要があります。


# Layerを作成しLambdaから実行する

今回はPythonのライブラリ[Paramiko](http://www.paramiko.org/)と、Paramikoを用いてサーバーにSSH接続するロジックをLayerに追加します。

## Layerの作成

Layerに追加するZIPファイルを作成します。
ParamikoをLambdaで実行できるように、Amazon Linux上で作業を行います。
【参考】[AWS LambdaでPythonのparamikoを使う方法](https://dev.classmethod.jp/etc/aws-lambda-paramiko/)
まずはデプロイパッケージを作成します。

```
$ mkdir python/ 
```

Paramikoをローカルのディレクトリ内にインストールします。

```
$ cd python
$ sudo yum -y install gcc gcc-c++ kernel-devel python-devel libxslt-devel libffi-devel openssl-devel
$ pip install -t ./ paramiko
```

SSH接続するPythonモジュールを作成します。

```
$ mkdir sshutils
$ cd sshutils
$ touch sshutils.py
```

```python3:sshutils.py
# coding: utf-8
import paramiko


def connect(hostname, username, password = None, keyfile = None):
    """
    SSHクライアントの作成・SSH接続
    """
    sshcon = paramiko.SSHClient()
    sshcon.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    sshcon.connect(hostname, username=username, password=password, key_filename=keyfile)
    print('接続')
    return sshcon


def exec_command(sshcon, command):
    """
    コマンドの実行
    """
    stdin, stdout, stderr = sshcon.exec_command(command)
    print('実行コマンド：' + command)
    print('実行結果：')
    for line in stdout:
        print(line)


def close(sshcon):
    """
    切断
    """
    print('SSH切断')
    sshcon.close()

```

ZIP化します。

```
$ cd python
$ cd ..
$ zip -r sshutils.zip python/
```

パッケージ構成は以下のようになります。

```
  python/
    ├ sshutils/
    │    ├ sshutils.py
    ├ paramiko/
        ・
        ・
        ・
    
```

以上で追加するLayerを作成できました。

##  Layerの追加

Lambdaのコンソールのタブの`Layer`を選択し、Layerの作成をクリックします。

![1.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/eb522be0-2c76-e25f-5304-ceb8ae8b6056.png)


名前を`ssh-util`として、作成したLayerのZIPファイルをアップロードします。

![2.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/8ac4b803-835c-ba8a-9a6d-97ef3414bf94.png)


Layerを作成するとバージョンが割り当てられます。

![3.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/93ce4830-be44-8db9-4647-12613f538d68.png)


以上でLayerを作成することができました。

## Lambda関数でLayerを使用する

実際にLambda関数からLayerを呼び出します。
手順は省きますが、今回はAWS VPC内のサーバーに接続するため、VCP、サブネット、セキュリティーグループの設定を行います。

### Lambda関数にLayerを追加する

Lambda関数を作成すると`Designer`からLayerを選択することができます。
「参照されるレイヤー」からレイヤーの追加を選択します。

![6.png](https://qiita-image-store.s3.amazonaws.com/0/239179/c3bcb8f0-e96d-88a2-51ce-c5d4e221ebda.png)

レイヤーの追加画面で、「ランタイムと互換性のあるレイヤーのリストから選択」を選択し、先ほど作成したLayerとバージョンを選択し確認を押します。

![7.png](https://qiita-image-store.s3.amazonaws.com/0/239179/7e3bf1dc-53b7-252e-c274-5ac466ac5148.png)

選択したLayerが追加されていることを確認したください。

![8.png](https://qiita-image-store.s3.amazonaws.com/0/239179/bae0122c-ef2e-7fea-ce22-4fa9cf8ff20b.png)

### Lambda関数からLayerを呼び出す

作成したLayerはPythonのimportで呼び出すことができます。
先ほど追加したLayerは、`/opt/python/`直下に配置されているので、`python/`以下から辿ってimportすることができます。

```python3:lambda_function.py
# coding: utf-8
from sshutils import sshutils


def lambda_handler(event, context):
    sshcon = sshutils.connect('XXX.XXX.XXX.XXX', 'XXXXXX', keyfile='XXXXXXX')
    command = 'pwd'
    sshutils.exec_command(sshcon,command)
    sshutils.close(sshcon)
    
```

### Lambda関数を実行

Lambdaのテストから実行します。
Lambdaのログ以下のように実行結果が表示されます。

```
接続
実行コマンド：pwd
実行結果：
/home/'ユーザー名'

SSH切断
```

# まとめ

AWS Lambda Layersを利用して、ライブラリとモジュールをLayerに追加しLambda関数で実行してみました。
これまでは外部ライブラリを関数ごとにパッケージ化しなければならずプロジェクトが肥大化していましたが、Layerを使用することで自作モジュールのみとなるため開発・保守しやすくなります。
共通ロジックなどのモジュールも共有できるので、より効率的に開発することができます。
今回例に出したParamikoを使用するように、Lambdaの実行環境であるAmazno Linux上で開発環境を作成しなくてはならない場合があります。一度ライブラリをAmazon Linuxで構築し、Layerとして追加しAWSアカウント内で使い回すことができるので、環境構築も一度で済みます。
ただし、デプロイパッケージのサイズの制限やLayer数の制限もありますので、共通化する部分を検討して使用する必要があると思います。