re:invent2017で発表されたAWS Cloud9でサーバーレスなAPIを作ってみた

[AbemaTV Advent Calendar 2017](https://adventar.org/calendars/2216) 3日目の記事です。
テーマフリーなので多様な記事が並ぶと思いますよ。

今日は2017年2月にジョインしたiOSエンジニアの服部が担当します。

# はじめに

re:Invent 2017で多数のサービスが発表されました。

外部参考エントリ:
 [【速報】AWS re:Invent 2017 Keynote 1日目で発表された新サービスまとめ #reinvent](https://dev.classmethod.jp/cloud/aws/aws-reinvent-2017-keynote-day-1/)
[【速報】AWS re:Invent 2017 Keynote 2日目で発表された新サービスまとめ #reinvent](https://dev.classmethod.jp/cloud/aws/aws-reinvent-2017-keynote-day-2/)
クラスメソッドさんの怒涛の速報。早過ぎる。

普段iOSエンジニアの私ですが [AWS Cloud9](https://aws.amazon.com/jp/blogs/news/aws-cloud9-cloud-developer-environments/) を試してみたので気軽に見てみてください。
おまけで [Amazon Rekognition Video](https://aws.amazon.com/jp/blogs/news/launch-welcoming-amazon-rekognition-video-service/)もブラウザから動かしてみました。 

AWS Cloud9 画面例: ブラウザ上で開発が完結
<img src="https://qiita-image-store.s3.amazonaws.com/0/63205/f47af084-3b0b-91b1-4650-f103784fabc0.png" width=50%>

# AWS Cloud9

[AWS Cloud9 – クラウド開発環境](https://aws.amazon.com/jp/blogs/news/aws-cloud9-cloud-developer-environments/)

クラウド上のIDEです。
ブラウザで開発が完結します。これがあるべき未来か。
近いうちにElectronでアプリが出る予感。

Lambda Functionのローカル実行、API Gatewayでのデプロイ、デプロイしたAPIの実行が画面切り替えなしにできるのが神です。

ハッカソンのちょっとしたAPI作成や趣味で作るアプリのバックエンド作成など、高速にできそうですね。
ガチサービスのバックエンド開発でも使えるはず。

まだTokyoリージョンでは使えません。

今回は簡易的なサーバーレスAPIモニタリング機能を作ってみました。

ざっくり手順を。

## Step 1. Lambda Function 関連権限付与
Admin権限を持つユーザで進める場合、Step 2. Environment作成へスキップして良いです。

[Working with AWS Lambda Functions in the AWS Cloud9 Integrated Development Environment (IDE)](http://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/lambda-functions.html)

### 1-1. IAMで開発用ユーザorグループに以下の権限をAttach
Adminユーザで、開発用グループにIAMのページから以下を付与しました。
- AWSLambdaFullAccess
- AmazonAPIGatewayAdministrator
- AmazonAPIGatewayInvokeFullAccess

### 1-2. AWS CloudFormationでStack作成
Adminユーザで、CloudFormationからStackを作成。

Choose a template > Specify an Amazon S3 template URL に `https://s3.amazonaws.com/cloud9-cfn-templates/Cloud9LambdaAccessGroup.yaml` をペースト:
<img width="823" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/7da4cf69-6252-4a40-5b4e-b49d4601bc62.png">

Stack nameには `AWSCloud9LambdaAccessStack`、GroupNameには開発用グループ名:
<img width="678" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/964c9d59-1a23-b0f5-e59a-e4d7f6d26407.png">

Optionsは変更なし:
<img width="683" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/23a9e5ae-ba60-0cf1-c2f3-b6089af94b89.png">

Createをクリックし数秒でStack作成完了:
<img width="157" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/d150c874-52a7-15f1-249e-89e621ba541d.png">

## Step 2. Envioronment作成

Create environmentクリック
<img width="822" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/7ba755dc-1a32-2407-21af-054562838267.png">

"Sample0001"等入れてNext Step。(この名前は後から変えられるのだろうか)
<img width="465" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/5c95bbdc-54b6-37c7-5e44-b2aca1995632.png">

EC2インスタンス上に作ります。VPC作っていなかったら作成してください。Next Step。
<img width="468" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/99499dac-814c-56a1-efca-6dbcdba1c112.png">

Create environment
<img width="412" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/9db8fe0c-5f39-50c8-8d74-d0ec343c70fd.png">

数十秒待つと...
<img width="478" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/03439b4a-83e6-4488-afca-71095860fd30.png">

environmentが出来ます！
<img width="820" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/53da2568-d68d-3b01-8f0d-d67678ff6432.png">

## Step 3. Lambda Function作成

environmentのWelcome画面からCreate Lambda Function... 
<img width="273" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/885c535a-b9ec-4c6a-33b2-0adb4f657b3a.png">

Function名入れてNext。
<img width="362" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/1cc0373e-aac9-73a1-65ed-c41c247aef2e.png">

今回はnode.jsで。
<img width="341" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/8f333014-ed53-3ed7-1bfa-83b6eb49085a.png">

Function TriggerにAPI Gateway設定。Resource Pathに`/check`。
<img width="341" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/def0b598-1650-ec94-a4ab-1743ad457b3c.png">

Next。
<img width="345" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/685be060-8f0f-731a-e427-dd0034e44100.png">

Finish！
<img width="341" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/a30c2ccf-58a2-6bf7-e3de-e9d5b4facbe8.png">

以下を実装。
Javascriptは初心者ですみませぬ。
今回はgithubユーザ情報取得APIステータス200を確認しています。

```js
'use strict';

var https = require('https');
var url = require('url');

var targetURLPaths = {
    "githubUsers":"https://api.github.com/users/[githubユーザ名]"
    //,"someAPI": "API URL"
};

var results = {};

function getAPI(key) {
    var urlObj = url.parse(targetURLPaths[key]);
    var options = {
        host: urlObj.host,
        port: 443,
        path: urlObj.pathname,
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "User-Agent": ""
        },
        method: 'GET'
    };
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            var req = https.request(options, function(res) {
                console.log(res.statusCode);
                if (res.statusCode != 200) {
                    results[key] = "Fail: " + res.statusCode;
                    resolve(key);
                    return;
                }
                var body = '';
                res.on('data', (chunk) => body += chunk);
                res.on('end', () => {
                    //console.log(body);
                    results[key] = "Success";
                    resolve(key);
                });
                res.on('error', function(e) {
                    results[key] = "Fail: " + e;
                    resolve(key);
                    return;
                });
            });
            req.end();
        }, 300);
    });
}

exports.handler = (event, context, callback) => {
    var promises = [];
    Object.keys(targetURLPaths).forEach(function (key) {
        promises.push(getAPI(key));
    });
    Promise.all(promises)
        .then(() => {
            var response = {
                statusCode: 200,
                headers: {
                    "Content-type": "application/json; charset=UTF-8"
                },
                body: JSON.stringify(results, null, 2)
            };
            callback(null, response);
        })
        .catch(callback);
};
```

## Step 4. Deploy

Lambda (local)で動作確認した後...
<img width="211" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/06d2b6ca-37da-ce0c-ec48-e04221633801.png">

左から4番目の上向き矢印でデプロイ！簡単だぜ
<img width="253" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/9a1839b4-1959-2485-30ff-dce702c6007b.png">

API Gateway (remote)で動作確認！動いとる
<img width="562" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/a932e782-173f-a1e7-03fe-92bf732b8624.png">

ブラウザからAPI叩いてみても良し。
<img width="233" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/e7307137-8a69-775b-3e8b-93e16a68fc01.png">

Lambdaは定期実行できるので、内部APIの生存確認を回してSlack通知などしたいところ。


# おまけ: Amazon Rekognition Image / Amazon Rekognition Video

画像解析APIです。
2016年に発表された[Amazon Rekognition Image](https://aws.amazon.com/jp/rekognition/)は静止画を解析します。

まずはこちらを試してみます。

(シェア可能な画像を使用。問題あったら言ってください...)
![スクリーンショット 2017-12-02 3.16.59.png](https://qiita-image-store.s3.amazonaws.com/0/63205/d4fd305b-9a33-fc3d-9772-339258872c2b.png)

うむ！
セレブリティとしてばっちり認識されています。

デモなのでブラウザから動かしていますが、APIとしても利用可能です。

続いて先日発表された[Amazon Rekognition Video](https://aws.amazon.com/jp/blogs/news/launch-welcoming-amazon-rekognition-video-service/)で動画を解析してみます。
動画の長さに制限があり1分まで。

研究/調査目的のmp4動画を流し込んでみます。
<img width="823" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/e40bded1-7f40-a0de-a344-1a4b9b905eec.png">

S3の保存先を生成。
<img width="323" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/06e2e9ba-c2e8-3480-e0a3-128aa63f7488.png">

5分程待つと結果が出ました！
<img width="823" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/de11ce67-ca0a-7d44-a8c7-c071aec7beb6.png">

稲垣氏が見事パットを決めた名場面ですが Objects and activities はかなり情景を捉えている気がします。
<img width="323" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/63205/2d5667ec-f06e-5d3e-fc5c-61dddfb7d7c4.png">


# 雑感
IDEがクラウド上にありサービスとつながっているのは想像以上に便利。
AWSのアカウントさえあれば、どこでも開発の続きができるという安心感。
Lambdaのローカルテスト、1クリックでのデプロイ、API Gatewayとの連携は嬉しい機能。
手順が手に馴染めば超高速開発ができるのでは。

Amazon Rekognition Imageのイメージ解析の速度と精度はかなりのもの。
Videoは1分の動画解析に5分程掛かるのは仕方ないか。
取り出すべき場面を見つけるシーケンスとそこから解析するシーケンスに分けているようだ。
返却されるJSONは15MBあった。
これはユースケースをまず考えるべき。

AWS楽しいすね。