Serverless Framework + TypeScriptでデバッグを行う

## 概要
AWS Lambda + TypeScriptで開発を行う際にデバッグを行う方法をまとめた記事です。

## 環境情報
この記事では以下の環境で開発を行っています。

- HostsOS（MacOS 10.12.3）
- [vagrant](https://www.vagrantup.com)（1.9.1）
- [VirtualBOX](https://www.virtualbox.org/)（5.1.12）
※GuestOSは[CentOSの7系](https://atlas.hashicorp.com/centos/boxes/7)を利用
- typescript(2.3.4)
- serverless(1.15.3)

今回詳しい環境構築の方法は割愛させて頂きます。（私自身がまだ試行錯誤している段階なので、情報をまとめ次第環境構築の記事を別途投稿します。）

2017-06-21 追記
serverlessが動作するローカル環境を [Ansible Playbook](https://github.com/keita-nishimoto/aws-lambda-ansible) として公開しました。

下記に現在利用しているgithubのrepositoryを記載しておきます。
※このrepository自体まだ試行錯誤を繰り返している段階ですので内容は徐々に変化していく点はご了承下さい。

- [ソースコード本体](https://github.com/keita-nishimoto/aws-serverless-prototype)
- [実行している開発環境](https://github.com/keita-nishimoto/aws-lambda-ansible)

なお、npm packageを管理する為に [yarn](https://yarnpkg.com/lang/en/) を利用しています。

## ローカルでAWS Lambdaを実行出来る環境を作る

効率良くデバッグを行うにはローカルでAWS Lambdaを実行出来る環境を構築する必要があります。

ローカルでAWS Lambdaを実行する為には今のところ2種類の方法があります。

- [serverless-offline](https://github.com/dherault/serverless-offline) を利用する方法
- [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) の ```serverless webpack serve``` を利用する方法

今回この記事で扱うのは [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) の ```serverless webpack serve``` を利用する方法です。

## [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) のインストールを行う

まずは [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) のインストールを行います。

プロジェクトルートで以下のコマンドを実行しましょう。

```bash:serverless-webpackのインストール
yarn add serverless-webpack
```

次にserverless.ymlに以下の記述を追加します。

```yaml:serverless.yml
plugins:
  - serverless-webpack
```

## [node-inspector](https://github.com/node-inspector/node-inspector) をインストールする

[node-inspector](https://github.com/node-inspector/node-inspector) はNodeをChrome上でステップ実行出来るようにするツールです。

プロジェクトルートで以下のコマンドを実行しましょう。

```bash:node-inspectorのインストール
yarn add node-inspector
```

## [source-map-support](https://github.com/evanw/node-source-map-support) のインストール

TypeScriptはその性質上、TypeScriptのコードがそのまま解釈されて実行されている訳ではなく、一旦JavaScriptにコンパイルされ、コンパイルされたコード（JavaScript）が実行されるという流れになります。

その為、ブレークポイントを設定する際にTypeScriptのコードに対して設定出来ないと非常に不便です。

それを可能にするのが [sourceMap](https://developer.mozilla.org/ja/docs/Tools/Debugger/How_to/Use_a_source_map) という仕組みです。

[source-map-support](https://github.com/evanw/node-source-map-support) はNode上でそれを利用出来るようにする仕組みです。

以下のコマンドでインストールを実行しましょう。

```bash:source-map-supportのインストール
yarn add source-map-support
yarn add @types/source-map-support
```

さらにLambdaが実行される前に以下のコードを追加してしておきます。

```ts:Lambdaが実行される前に以下のコードを追加しておく
import * as sourceMapSupport from "source-map-support";

sourceMapSupport.install();
```

これで [sourceMap](https://developer.mozilla.org/ja/docs/Tools/Debugger/How_to/Use_a_source_map) が出力されるようになります。

ここまでで事前準備は完了となります。

## [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) でローカルサーバを起動する

以下のコマンドでローカルサーバを起動させます。

```bash:ローカルサーバの起動
serverless webpack serve
```

成功すると下記のような形でサーバが起動します。

<img width="749" alt="serverless_webpack_serve.png" src="https://qiita-image-store.s3.amazonaws.com/0/71899/7b649135-8bbd-4213-4502-525c0439f3a8.png">

## ローカルサーバのprocessIDを取得する

次にローカルサーバのprocessIDを取得します。

```bash:processIDを取得
pgrep -l node
```

ここでは ```2110 node``` が出力されたと仮定します。

## 実行中のprocessIDに対してUSR1シグナルを送信する

Node でデバッガを起動する為にはNodeの実行processに対してUSR1シグナルを送信します。
そこで先程取得したprocessIDに対してUSR1シグナルを送信します。

```
kill -s USR1 2110
```

すると実行中のサーバコンソールに ```Starting debugger agent.
Debugger listening on port 5858``` と表示されるかと思います。

<img width="815" alt="serverless_webpack_serve2.png" src="https://qiita-image-store.s3.amazonaws.com/0/71899/f9fecf6b-b39c-a72b-6a22-2c66ba2a7eda.png">

## [node-inspector](https://github.com/node-inspector/node-inspector) を起動

先程インストールしたnode-inspectorを起動します。

```bash:node-inspectorの起動
./node_modules/.bin/node-inspector
```

<img width="617" alt="node-inspector.png" src="https://qiita-image-store.s3.amazonaws.com/0/71899/f7360fa5-06d4-7fac-d3cf-9d7c55a22446.png">

これで http://127.0.0.1:8080/?port=5858 にChromeでアクセスすれば良いのですが、私の場合はvagrantのIPに192.168.33.60を割り当てていたので以下のURLで接続を行います。

http://192.168.33.60:8080/?port=5858

接続してしばらく時間が経つと下記のような画面が表示されます。

![node-inspector2.png](https://qiita-image-store.s3.amazonaws.com/0/71899/39cd2709-25be-e6c1-da09-1ae6e023df2f.png)

## ブレークポイントを指定して実行する

左側に表示されているディレクトリツリーに webpack:// と記載されている部分があります。
ここからブレークポイントを設定してLambdaの実行を行ってみます。

今回の例では下記の画像のように src/functions/auth.ts 125行目にブレークポイントを設定しています。

![node-inspector4.png](https://qiita-image-store.s3.amazonaws.com/0/71899/6813d7c3-3ef8-71cb-4bf6-e07f1ed88cb0.png)

実行すると下記のようにブレークポイントで止まっている事が確認出来るかと思います。
右側のメニューから変数の中身等も視覚化されています。

![node-inspector5.png](https://qiita-image-store.s3.amazonaws.com/0/71899/dfa51ee8-b7d8-0b93-32d4-b06651a8edfa.png)

※右上の再生ボタンを押すと最後まで実行が完了します。

## 最後に

以上がServerless Framework + TypeScriptでデバッグを行う為の手順でした。
この記事では [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) を利用していますが、近々 [serverless-offline](https://github.com/dherault/serverless-offline) を利用する方法に切り替えようと思っています。

理由は下記の通りです。

- [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) の開発速度が2017年に入ってから若干落ちている。
- ```serverless webpack serve``` はAPI Gatewayの動作と若干異なる部分がある。（Custom Authorizerが無効になっている等）

[serverless-offline](https://github.com/dherault/serverless-offline) を利用する場合、 [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) に頼らずに、普通にwebpackを使ってbuildを行う形に変更しようと考えています。

※これらの内容は構築が完了次第、別の記事を投稿しようと思います。

とは言えこの記事の内容でも、都度AWSにデプロイしてconsoleでデバッグを行うよりは遥かに効率が良いので、同じ組み合わせで開発を行っている方々の参考になれば幸いと思い、この記事を書かせて頂きました。

それから私自身、TypeScriptやServerlessアーキテクチャに対して高度な技術を持っている訳ではありません、これを見て何かお気づきの方はコメントを貰えると嬉しいです。

以上になります。最後まで読んで頂きありがとうございました。

## 2017-06-21 追記

### [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) の利用を停止しました（2017-06-21 追記1）
[serverless-webpack](https://github.com/elastic-coders/serverless-webpack) は更新が止まってしまったようなので、現状、webpackの利用を辞めて ```tsc``` を使う方法に変更してあります。
詳しくは [Amazon API Gateway の Custom Authorizer で ServerlessなAPIを作ってみた](http://qiita.com/keita-nishimoto/items/508744fe7dc3b5f75d4c) に追記しました。

ただしこの代償としてデプロイパッケージのサイズが大きくなってしまいました。
```serverless.yml``` に余計なファイルはexcludeする設定を書きましたが、それでもwebpackによる最適化には遠く及びません。

webpackによる最適化の恩恵を受けつつ最新のwebpackの利用したいという場合は [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) 相当のPluginを自作するしかないのかもしれません。

### デバッグ方法の簡略化（2017-06-21 追記2）
簡単なデバッグ方法を発見したので [こちらのページ](https://github.com/keita-nishimoto/aws-serverless-prototype/wiki/Local-Debugging) に記載してあります。

この方法であれば自分でprocessIDを調べて ```USR1``` シグナルを送ったりする必要はありません。（SourceMapが利用出来ない問題があるのでそれは別途解決する必要がありますが・・・）