localstackにLambdaのパッケージをディレクトリ指定でインストールしてzipに固めて作る(pythonバージョン)

## 紹介する内容
- localstackでlambdaを作ります
  - localstackのdocker-entrypoint-initaws.dを使う。主にpythonを使います
  - ユーザーが指定したディレクトリにpackageをインストールします
  - インストールしたpackageをzipに固めます

## 結論
- lambdaのpackageのインストールを別のdockerコンテナにしたら、いろんな言語のpackageインストールに対応できます
  - localstackのコンテナにpythonのvenvなどのバージョン管理ツールをインストールして使う手もあります。ですが、言語ごとにツールの使い方を思えるより、dockerの使い方を覚えて全言語をカバーする方が効率的だと思いました

- localstackのdocker-entrypoint-initaws.dでpythonを使うことで二つ以上のリソースが連携する構築が可能です
  - 例えばDynamoDBを生成して、戻り値のARNを使ってlambdaのcreate_event_source_mappingで使用可能です
  - shell scriptなら、戻り値を処理することがややこしくなります


## 紹介始めます
### localstack紹介
実のAWSを使わずにいろいろ試せるものです。  
バージョン0.11.0から大きな変化がありました。本記事は0.11.0バージョンのlocalstackを使います  
https://github.com/localstack/localstack 


### ディレクトリ構成
全体ソースコードは https://github.com/cheekykorkind/qiita-example/tree/master/localstack/0.11.0/init-by-py で確認できます

- 全体図
![treeD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/209c567e-6385-4ee5-aada-08bbf08f11fd.png)



- AWS SAMプロジェクトで生成した部分
![sam-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/05584e8d-2c5c-2e89-28fc-fe9fe8e49d75.png)



- localstackのdocker-entrypoint-initaws.d機能を使うためにマウントした部分
![initaws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/007eb289-c2df-7ed7-5aae-55955168f157.png)




### localstack構築
docker composeを使います。

```
version: '3'

services:
  pip_installer:
    container_name: 'pip_installer'
    image: python:3.7-alpine3.10
    tty: true
    working_dir: /workspace/sam-lambda
    environment:
      LAMBDA_ZIP_PATH: /workspace/sam-lambda/hello_world/lambda.zip
    volumes:
      - ./sam-lambda:/workspace/sam-lambda

  localstack:
    container_name: 'localstack-dev'
    image: localstack/localstack:0.11.0
    ports:
      - '4566:4566'
    environment:
      SERVICES: lambda,logs,dynamodb
      DEBUG: 1
      HOSTNAME_EXTERNAL: localstack
      EDGE_PORT: 4566
      PORT_WEB_UI: 8080

      AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
      AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
      AWS_DEFAULT_REGION: us-east-1
      LAMBDA_ZIP_PATH: /workspace/sam-lambda/hello_world/lambda.zip
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./initaws:/docker-entrypoint-initaws.d
      - ./tmp/localstack:/tmp/localstack

      - ./sam-lambda:/workspace/sam-lambda
    depends_on:
      - pip_installer
```


#### docker-composeのvolumes(マウント)設定
- localstackコンテナ
  - packageインストールやzipファイル共有するために、AWS SAMプロジェクトをマウントしておきます。
     - `./sam-lambda`は絶対パス`qitta-example/localstack/0.11.0/init-by-py/sam-lambda`と同じです
  - localstackの`docker-entrypoint-initaws.d`機能を使うためにマウントして置きます
     - `./initaws`は絶対パス`qitta-example/localstack/0.11.0/init-by-py/initaws`と同じです

- pip_installerコンテナ
  - packageインストールやzipファイル共有するために、AWS SAMプロジェクトをマウントしておきます。
     - `./sam-lambda`は絶対パス`qitta-example/localstack/0.11.0/init-by-py/sam-lambda`と同じです


#### docker-composeの環境変数設定
- localstackコンテナ
  - `SERVICES`、`DEBUG`、`HOSTNAME_EXTERNAL`、`EDGE_PORT`、`PORT_WEB_UI` はlocalstack固有の環境変数です。localstackを調べると理解できると思います
  - `AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY`、`AWS_DEFAULT_REGION` はaws cliを便利に使うために設定しました
  - `LAMBDA_ZIP_PATH` はlambda生成に使うzipファイルの位置を書きました。コンテナ2つが連携して作業するから管理しやすくするために設定しました

- pip_installerコンテナ
  - `LAMBDA_ZIP_PATH` はlambda生成に使うzipファイルの位置を書きました。コンテナ2つが連携して作業するから管理しやすくするために設定しました

#### docker-compose upによる作業の流れ
1. pip_installerコンテナが作れる
 - localstackコンテナが`depends_on`しているから、localstackコンテナより先に作られます
 - `tty: true`オプションによって、作れた後もup状態を維持します


1. localstackコンテナが作れる
 - コンテナが作れた後、localstack固有の`もろもろ初期化`作業が行われます

1. docker-entrypoint-initaws.dディレクトリにあるshファイルを名前順で実行します
 - localstackの固有機能です。
 - `もろもろ初期化`作業が終わったら、マウントしている `qitta-example/localstack/0.11.0/init-by-py/initaws` の`00_create_lambda_zip.sh`、`01_lambda.sh`順で実行してくれます。shではない `lambda_create.py` は無視されます。

1. 00_create_lambda_zip.sh
 - 待機していたpip_installerコンテナにdockerコマンドで入ります
 - pythonのpackageをディレクトリ`qitta-example/localstack/0.11.0/init-by-py/sam-lambda/hello_world/packages` に指定し、インストールします
 - pythonコード及びインストールしたpackageたちをzipで固めて`$LAMBDA_ZIP_PATH`に置きます
 - pip_installerコンテナを終了し、削除します

1. 01_lambda.sh
 - `python /docker-entrypoint-initaws.d/lambda_create.py`を実行されるためのファイルです。
 - `docker-entrypoint-initaws.d`はshだけ実行するから、書き方が踏み台のようになりました。
 - lambda_create.pyがlambdaを作ります

## localstackで作ったlambdaを試します
### `docker-compose up -d && docker attach localstack-dev` します
```
cd qitta-example/localstack/0.11.0/init-by-py
docker-compose up -d && docker attach localstack-dev
```

docker attach localstack-devしているあからlocalstackコンテナの作業が見れます
![dockerComposeUp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/4f956cd2-193d-08a0-0ee8-43d3f5268c57.png)


### pythonのpackageがpipによってインストールされてます
![pipInstall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/ea6ae329-fbae-c3a1-2e2d-6701a4c37cb1.png)


### インストール終了後、zipで固めてます
![MakeZip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/a056884f-1e0d-5f1a-78d2-886491b52eaf.png)


### lambdaが生成されました
![lambdaCreated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/6b0077c2-0c89-c912-066e-57053646536f.png)


### コンソールをもう一個開いて、localstackのlambdaにinvokeします
左側が `docker attach localstack-dev` している方、右側が `docker exec -it localstack-dev /bin/bash` してlambdaをinvokeする方です。
200返すことからちゃんと動いた確認が取れました。
![lambdaInvoke.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/832b892a-8bc9-b5ff-5c53-68e3d59ba65c.png)



lambdaのコードはrequestsパッケージを使ってGET一回投げることをしています。
`qitta-example/localstack/0.11.0/init-by-py/sam-lambda/hello_world/app.py`