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

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

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

- localstackのdocker-entrypoint-initaws.dでshell scriptでプロビジョニングは限界がある
  - 二つ以上のリソースが連携する形では戻り値のAWSのARNなどの扱いが大変です

## 紹介始めます
### 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-shell で確認できます

- 全体図
![treeD.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/290c607c-adff-5f1a-13cc-405f58c4ab89.png)


- AWS SAMプロジェクトで生成した部分
![samInit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/c4905a5f-1dfb-37bd-9cd1-41b307e62e0b.png)


- localstackのdocker-entrypoint-initaws.d機能を使うためにマウントした部分
![dockerAwsInit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/86035016-28e5-03f9-21ed-b68e6097596b.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-shell/sam-lambda`と同じです
  - localstackの`docker-entrypoint-initaws.d`機能を使うためにマウントして置きます
     - `./initaws`は絶対パス`qitta-example/localstack/0.11.0/init-by-shell/initaws`と同じです

- pip_installerコンテナ
  - packageインストールやzipファイル共有するために、AWS SAMプロジェクトをマウントしておきます。
     - `./sam-lambda`は絶対パス`qitta-example/localstack/0.11.0/init-by-shell/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-shell/initaws` の`00_create_lambda_zip.sh`、`01_lambda.sh`順で実行してくれます

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

1. 01_lambda.sh
 - aws cliを使って、lambdaを作ります。zipの位置は`00_create_lambda_zip.sh`の作業で指定した`$LAMBDA_ZIP_PATH`にします
 - lambdaが作られます

## localstackで作ったlambdaを試します
### `docker-compose up -d && docker attach localstack-dev` します
```
cd qitta-example/localstack/0.11.0/init-by-shell
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/5a019c33-4410-38de-c3c3-4c43750fe666.png)

### pythonのpackageがpipによってインストールされてます
![pipInstall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/6dc1a441-9f57-bd7a-01d5-a72dfcfde8a0.png)

### インストール終了後、zipで固めてます
![makingZip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/a7c981aa-1853-e25c-f3ab-992b5f06f6dd.png)

### lambdaが生成されました
![lambdaCreated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/ec0ea3c7-055a-bb9c-c49b-5661caef50d7.png)

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

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