Pycharmの力を使って爆速でAWS Lambdaを開発・テスト・デプロイする方法

この記事は[全部俺 Advent Calendar 2018](https://adventar.org/calendars/3702)の3日目の記事です。
爆速でサーバレス開発します！

# tl;dr
- AWS Toolkit for Pycharmを使用してAWS Lambdaを開発しました。
- AWS Toolkitを使うとLambda開発で必要な以下のことがPycharm内で全てできます！
    - デバッグモードを用いたStep実行、ローカルテスト
    - AWS Lambdaへのデプロイ
    - デプロイしたリモート環境でのリモートテスト

# AWS Toolkitについて
re:invent 2018のキーノートで発表されたAWS Lambda開発のためのOSSツールキットです。
現在はPycharm版、IntelliJ版（プレビュー）、VSCode版（プレビュー）があります。
この記事では、2018/12/03現在で唯一プレビュー版でなく一般提供されているPycharmを用いてAWS Lambda開発を行う方法について記載します。

## AWS Toolkitでできること
- ローカルでコードをテストしながら、ステップ実行しつつ実装を進めることができる。
- 選択したAWSリージョンへアプリケーションを簡単にデプロイすることができる。

つまり、AWS Consoleを使用することなく、AWS Lambdaの開発、デプロイ、疎通テストまで全てPycharm上でできるのです！！

## 筆者の環境
| Software         |Version                   |
|:-----------------|:-------------------------|
|macOS Mojave      |10.14.1                   |
|Pycharm           |2018.3 (Community Edition)|
|Docker for macOS  |2.0.0.0-mac81             |

# AWS Toolkit for Pycharmのインストール
## PycharmへAWS Toolkitをインストール
PycharmのPluginsから、「AWS Toolkit」を検索して「Install」を実行します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/b41466b2-fc41-2d45-dc8f-84919feef8fc.png)

注意）Pycharmのバージョンが古い場合、「AWS Toolkit」が表示されない場合があります。その場合はPycharm自体のアップデートから先に実施してください（2018.2だと表示されませんでした）。

## SAM CLIのインストール
AWS Toolkitをインストールしたあと、Pycharmを再起動して「New Project」を作成しようとすると、下記のようにエラーが表示されます。
これはSAM(Serverless Application Model) CLIがインストールされていないためです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/6c424e4b-c47e-1f28-1b26-e99866937120.png)

[公式サイト](https://aws.amazon.com/jp/serverless/sam/)のInstall SAM CLIに従って、SAM CLIをインストールします。

```
brew tap aws/tap
brew install aws-sam-cli
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/a95d39b8-4c9b-d8e9-c05b-9193ee3250ef.png)

SAM CLIではDockerを必要とするので、[公式インストール手順](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)を参考に、必要に応じてDockerのインストールを行ってください。

# AWS Toolkit for Pycharmを使用したLambda実装
## Create New Project
ここまで来たらPycharmを再起動して、再度「New Project」します。
今度はエラーが表示されることなくProjectが作成されるはずです。
「More Settings」から「SAM Template」として、「AWS SAM Hello World」を選択して「Create」してみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/6fbe6df9-467b-baf5-9ccb-d0afd4ea56ad.png)

## エラー解消
Templateのままだと、いくつかエラーが出てしまっているので、それを解消します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/66dd86e2-f11f-a199-5362-490ab98ef894.png)

### requestsモジュールのインストール
いつものように、importで赤字が出ている箇所で「⌥ + Space」を押してrequestsモジュールを`pip install`します。

### Credential情報の設定
- 見にくいですが、画面右下にAWSのCredential情報を設定する箇所があるのでそこを押し、最下部の「Edit AWS Credential file」を押します。
- `~/.aws/credentials`が存在しない場合、作成しても良いか聞かれるのでYesを選択し、`aws_access_key_id`と`aws_secret_access_key`を設定します。
    - [credential情報](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html)の「IAM ユーザーのアクセスキー ID およびシークレットアクセスキーを取得するには」を参考に取得した値を設定します。
    - credential情報を設定した場合、再度Pycharmの再起動が必要です。
    - credential情報が反映されている場合、右下に`AWS:Profile:default@[リージョン名]`と表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/729de137-1ee9-d04a-da74-c813b0259ba1.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/db46965f-3387-ec7b-d8cd-627abccf3b2a.png)

## ローカルでの実行
`app.py`の`lambda_handler`関数の左側に、λマークがでているのでそれを押して`Run [Local] app.lambda_h...`を実行します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/bc74f2e8-e708-249a-a08e-0bc9d4fc78ce.png)

実行のコンフィグ画面に移行するので、「Input」の「Text」から「API Gateway AWS Proxy」を選択して「Run」を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/a42d8358-c6fe-f7bf-093f-5e5e609156c4.png)

2~3分ほど待つと以下のように実行結果が出力されます。（Docker container imageが作成されるので、結構待ちます。）
結果を見ると、StatusCodeが200でmessageに"hello world"が出力されていることが確認できます！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/8fe283cb-822f-74a3-1b5a-5aac780cd7b6.png)

ソースコードを修正すると当然結果も変わりますし、ローカルでデバッグ実行をすることもできます！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/eaaf1e66-8b9c-1d9f-bd46-f77d24035851.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/86358f2b-3c22-494c-91a6-b76d044b0803.png)

うまくデバッグ実行ができない場合は、以下の[ハマりどころ](#ハマりどころ)を参考にしてみてください。

## デプロイとリモートテスト
`template.yaml`を右クリックし、一番下の`Deploy Serverless Application`を選びます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/3ad04d42-32a8-7665-c82b-89747d834a6d.png)

AWS CloudFormationのStack作成やlambda関数の環境変数設定、デプロイパッケージを保存するS3バケットの作成・指定を行う画面が出てくるので、ここで値を入力することでRemoteへのDeployが実行されます！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/7a1e51b6-cb93-6f84-86f6-72ddad21cb5d.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/318ca554-ea61-bf44-4d02-a7ed3883f9e0.png)

画面左下の「AWS Exploler」を押すと、リモートのAWS Lambdaが作成される様子が見えます。
作成されるのを待って、lambdaを右クリックして`Run [Remote] app.lambda_h...`を押すとローカル実行のときと同様の画面が出てくるので、「Input」の「Text」から「API Gateway AWS Proxy」を選択して「Run」を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/08f03ed3-b159-95eb-9e7a-7265617c5c51.png)

実行結果が帰ってきて、StatusCodeが200でmessageに"hello world"が出力されていることが確認できました！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/ccfcd045-ee6d-1235-33b1-792140a97413.png)

マネジメントコンソールからも、Lambdaがデプロイされていることが確認できます！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/514b7f1f-b87f-fe1a-cedc-c068e9e22496.png)

以上で、AWS LambdaをPycharm上から作成し、デプロイすることができました！
「AWS Exploler」を使用すると、マネジメントコンソールを使わずともリモート実行も可能なため、すべての作業がPycharm上で完結していい感じです！

## 参考：ハマりどころと回避方法
### デバッグ実行で502が帰ってくる場合
デバッグ実行をする際、`docker.errors.APIError: 502 Server Error: Bad Gateway`のようなエラーが出ることがあります。
これは、Dockerにローカルの`/Applications`ディレクトリ以下がVolumeとしてShareされていないために起きるエラーです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/f759742f-b7c9-7c28-e378-946d3c466b2c.png)

これを解決するには、`Docker for macOS`の設定を開き、`File Sharing`から`/Applications`を追加し、「Apply & Restart」を実行して`Docker for macOS`を再起動します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/190807/c0e2a9a7-4152-64c3-b113-8252645888e1.png)

`Docker for macOS`再起動後、再度Pycharmからデバッグ実行をするとうまく動作するはずです！

## 参考
- [公式GitHub](https://github.com/aws/aws-toolkit-jetbrains)