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

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

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

- localstackのdocker-entrypoint-initaws.dでterraformを使うことで開発環境プロビジョニングするコストが減ります
  - 例えば本番環境用terraformを持っていたら、開発環境用に少し修正してlocalstackですぐ使えます



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

### terraform紹介
コードでインフラを管理できます。
https://www.terraform.io/


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

- 全体図
![treeD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/8907b724-f52a-b220-8e33-2b57e8bca0ce.png)



- AWS SAMプロジェクトで生成した部分
![sam-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/88433e94-d717-dc3b-cd00-5020d1b9b68b.png)


- terraformファイルを置く部分
![terraformD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/b633da77-5087-e6ba-34ae-2719733f2aff.png)



- localstackのdocker-entrypoint-initaws.d機能を使うためにマウントした部分
![initaws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/023bb60e-ae28-5502-2cc5-ecbc3a015f3d.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
      TERRAFORM_PATH: /workspace/terraform
    volumes:
      - ./sam-lambda:/workspace/sam-lambda
      - ./terraform:/workspace/terraform

  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-terraform/sam-lambda`と同じです
  - localstackの`docker-entrypoint-initaws.d`機能を使うためにマウントして置きます
     - `./initaws`は絶対パス`qitta-example/localstack/0.11.0/init-by-terraform/initaws`と同じです

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

#### 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つが連携して作業するから管理しやすくするために設定しました
  - `TERRAFORM_PATH` はterraformファイルのパスです。terraformコマンド実行するためにterraformファイルがいるところに移動する必要があります。便利な移動のために事前に指定しておきました

#### 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-terraform/initaws` の`00_create_lambda_zip.sh` を実行してくれます。

1. 00_create_lambda_zip.sh
 - 待機していたpip_installerコンテナにdockerコマンドで入ります
 - pythonのpackageをディレクトリ`qitta-example/localstack/0.11.0/init-by-terraform/sam-lambda/hello_world/packages` に指定し、インストールします
 - pythonコード及びインストールしたpackageたちをzipで固めて`$LAMBDA_ZIP_PATH`に置きます
 - terraformをインストールしあます
 - terraformファイルがいる `$TERRAFORM_PATH` に移動します
 - terraform initします
 - terraform applyします。同時にterraformでlambdaのzipパス(`$LAMBDA_ZIP_PATH`)を渡します
 - terraformによってlambdaが作られます
 - pip_installerコンテナを終了し、削除します


## localstackで作ったlambdaを試します
### `docker-compose up -d && docker attach localstack-dev` します
```
cd qitta-example/localstack/0.11.0/init-by-terraform
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/df4d81c1-693c-531c-ea55-1eb434b35a71.png)


### terraformをインストールします
![terraformInstall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/f46ccb85-0be6-8064-2e41-6ac57b60b724.png)


### pythonのpackageがpipによってインストールされてます
![pipInstall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/5754da30-3a52-bf58-41f1-7aaf52c7372d.png)


### インストール終了後、zipで固めてます
![makeZip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/00fd9646-6df1-2464-3957-c1addcd5917e.png)


### terraform initします
![terraformInit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/bbf37233-19b1-0fcd-1592-36dd47b5f298.png)


### lambdaが作られました(terraform apply)
![terraformApply.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247584/daf9885f-904b-eef4-0e45-26b60154954e.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/852da853-6364-4b76-5098-6a1eae7731c4.png)



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