40分くらいで node.jsインストール -> ServerlessFramework -> AWS Lambda から Slackに通知まで

お仕事でぼちぼちServerlessFrameworkを使い始めました。

###辛かった
lambdaを始めたばかりの時は「いやー、フレームワークとかまだ早いっしょ」とか「もうちょっとWeb UIで慣れてからやろう」と思っていましたが、
CLIでデプロイできるのはやっぱ楽だし、超早いです。　API gatewayとlambdaの連携の設定とか面倒だし。zipを固める作業とか苦行でした。その先になにかあるかなー、と思っていたんですが。

###で、ServerlessFrameworkですが、使い始めてみると
「ちょっとコレなしにデプロイとか、する気おきないなー」とか「これ取り上げられたら、仕事休みたいなー」という程度に普段使いするツールになりました。

という訳で、私がServerlessFramework導入するまでをまとめてみます。
私は下記の状況で使っています。

* ndenvでバージョン指定してインストール
* node関連はシステムワイドにインストールせずに作業ディレクトリのnode_modules配下のものを使う
* ローカル実行したい( テストを手軽に実行したい... )ので lambda-local というツールをインストール
* slackにWebhookの設定

###nodeインストール
ここを参考にさせていただきながらインストールしました! -> 
http://qiita.com/0084ken/items/22ca33032773c6d2f9f4
http://qiita.com/MahoTakara/items/8fdebe32e8f326afa7f8

```bash:anyenvダウンロード
git clone https://github.com/riywo/anyenv ~/.anyenv
```


```bash:bashの場合
echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(anyenv init -)"' >> ~/.bashrc
```


```bash:zshの場合
echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(anyenv init -)"' >> ~/.zshrc
```


```bash:シェルの再実行
exec $SHELL -l
anyenv install ndenv
exec $SHELL -l
anyenv version
```


```bash:AWS_lambdaのnode.jsのバージョン、v4.4.3をインストール
ndenv install -l | grep '4.3'
ndenv install v4.4.3
ndenv global v4.4.3
```

```bash:バージョン確認
ndenv versions
```

```bash:バージョン確認、結果
  system
* v4.4.3 (set by /Users/kaoru_inoue/.ndenv/version)
```

###ServerlessFrameworkのプロジェクト作成

```ディレクトリ作成
mkdir testservice
cd testService
```

```bash:npm_initでpackage.jsonを作成
npm init -y
```

するとローカルにpackage.jsonというファイルができます。
これはnodeで、このアプリに必要なモジュールなどを一気にいれたり、テスト( すみません、まだできておりません... )を設定したりするファイルです。
モジュールを一気に入れる機能、超便利です。

```bash:動作に必要なモジュール、開発に必要なモジュールのインストール
npm install --save slack-node
npm install --save-dev serverless aws-sdk lambda-local
```
--save アプリの動作に必要なモジュール
--save-dev 開発に必要なモジュールです

ServerlessFramework は開発には必要ですが、lambda側にアップする必要はないので --save-dev ですね。
あとnodeモジュールの「aws-sdk」はlambda側に予め入っているので、デプロイ時は同梱しなくてもok

下記のようにpackage.jsonが書き換わります。
npm installコマンドは package.jsonという名前のファイルを元に必要なモジュールをインストールします。
ので、package.jsonを作業ディレクトリにコピーして npm install で一発でインストールでも良いです( 同じようなサービスを複数作る場合は雛形みたいなものがあった方が楽そうですね )

```package.json
{
  "name": "testservice",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babel-polyfill": "^6.23.0",
    "slack-node": "^0.1.8"
  },
  "devDependencies": {
    "aws-sdk": "^2.28.0",
    "lambda-local": "^1.4.2",
    "serverless": "^1.9.0"
  }
}
```

slackに通知するコードです( 下記サンプルの webhookUriは書き換える必要があります! )
ファイル名は「slackTest.js」とします( 任意ですが、後でServerlessFrameworkでデプロイするファイルを指定します )

```slackTest.js
'use strict';

const AWS = require('aws-sdk');
const Slack = require('slack-node');
const slack = new Slack();

const webhookUri = 'https://hooks.slack.com/services/naisho_himitsu';
slack.setWebhook(webhookUri);

function pushSlackChannel(channel, username, text ){
  return new Promise(function(resolve,reject) {
    slack.webhook({
      channel: channel,
      username: username,
      text: text
    }, function(err, response) {
      if (err) {
        console.log(response);
        reject(new Error('Error'));
        return;
      } else {
        resolve(response);
      }
    });
  });
}

module.exports.slackPost = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'slack post in success',
      input: event,
    }),
  };
  pushSlackChannel('#general', 'tin-machine', 'こんにちは！')
    .then((response) => {
      // console.log(response);
      callback(null, `Successfully ON watchdog stream.`);
    })
    .catch(err => {
      // console.log(err);
      callback('Error occured.');
    });
};
```

###slackアカウント作成( 5分程度? )
上記のコードで指定すべきWebhookURIがありません、はりきってSlackアカウントを作成しましょう( 5分程度 )

メールアドレスが必要です( 私は hogehoge+1@gmail.com みたいなアドレスで登録しました )
* https://slack.com/ にアクセス、メールアドレス入れて[ create new team ]
* メールに確認用の数字が届くので入力
* あとはグループ名を決めたり、招待する人を決めたり

###slackのwebhook urlをコピペ
####画面右上のアカウント名をクリック
![スクリーンショット 2017-03-17 20.11.05.png](https://qiita-image-store.s3.amazonaws.com/0/124921/9c12afbe-5316-eb3c-265b-3db3e5357621.png)

####[ Apps & integrations ]をクリック
![スクリーンショット 2017-03-17 20.11.21.png](https://qiita-image-store.s3.amazonaws.com/0/124921/7fcc32ae-fd01-31da-877e-8d07c3cb6d96.png)

####上部の[ Manage ]をクリック
![スクリーンショット 2017-03-17 20.11.36.png](https://qiita-image-store.s3.amazonaws.com/0/124921/da48abca-08b3-be00-13a6-533bbd1b2785.png)

####[ incomming webhooks ]で検索
![スクリーンショット 2017-03-17 20.13.31.png](https://qiita-image-store.s3.amazonaws.com/0/124921/83df7b0b-3079-caf9-8eb4-b311b55f98ab.png)

####[ Add Configuretion ]をクリック
![スクリーンショット 2017-03-17 20.13.54.png](https://qiita-image-store.s3.amazonaws.com/0/124921/c1cec644-18ee-634a-7492-483939b7886a.png)

####投稿するチャンネルを設定
![スクリーンショット 2017-03-17 20.14.23.png](https://qiita-image-store.s3.amazonaws.com/0/124921/cada32e0-5b52-eb61-823d-5e95bdc1247a.png)

####[ Webhook URL ]の[ Copy URL ]をクリックしてWebhook URLをコピー
![スクリーンショット 2017-03-17 20.15.29.png](https://qiita-image-store.s3.amazonaws.com/0/124921/23126346-b206-3ace-5f96-1b2be84a3750.png)


###lambda-local でローカルでテスト
lambdaは何かイベントで発動しますが、適当なダミーのデータを作成します。

```bash:ダミーのイベントファイルを作成します
mkdir event-samples
vi event-samples/testEvent.json
```

```bash:testEvent.jsonの内容
{
  "key3": "value3",
  "key2": "value2",
  "key1": "value1"
}
```

```bash:lambda-localを実行します
./node_modules/lambda-local/bin/lambda-local -l slackTest.js -h slackPost -e event-samples/testEvent.json
```

ローカルで動きましたかね? で、これをサーバレスで動かしたい訳です。

###ServerlessFrameworkでデプロイ!
ServerlessFrameworkでデプロイしてみます。
これをお読みの方はAWSのWebUIで書いたり、zipに固めてアップしたりといった苦行をされている方だとお見受けします。そろそろ楽になっていいはず！
まずは簡単に『時刻で実行されるlambda関数』を設定してみます。　ひとまずリスクが少ないバッチ処理などをlambdaで書いてみるか、といった需要はあるはず。
時刻の指定は日本時間ではなく『UTC』です。いつも混乱しますが、[AWS Lambda - Scheduled EventのCron書式](http://qiita.com/pyr_revs/items/abd29d9472f4cba9032c)を参考にさせていただきつつ、
直近の時刻を指定してみましょう。 日本の方がUTCに対して9時間すすんでいるので... 9時間、引きます。 今が21時なので、echo $((21 - 9)) で 12時ですね。

```bash:ServerlessFrameworkでのデプロイを設定
vi serverless.yml
```

```yaml:serverless.yml
service: slackPostService
provider:
  name: aws
  runtime: nodejs4.3
  iamRoleStatements:
    - Effect: Allow
      Action:
        - cloudwatch:*
      Resource: "*"

package:
  exclude:
    - jsconfig.json
    - event-samples
    - package.json
    - node_modules/**
    - '!node_modules/aws-sdk/**'
    - '!node_modules/slack-node/**'

functions:
  slackPostFunction:
    handler: slackTest.slackPost
    events:
      - schedule: cron(5 12 ? * MON-FRI *)
```

###いざデプロイの前に AWS CLI アカウント設定
すみません、pythonのパッケージ管理ツール pipのインストールと、AWSアカウント取得は既に設定ずみとさせてください。これ結構時間かかった記憶があります( が、持ってますよね...? )

AWSのアカウント情報を入力します。AWSにログインし、一番上の[ サービス ]->[ IAM ]->左側の[ ユーザー ]->使えるユーザ名をクリック->[ 認証情報 ]のタブ->[ アクセスキーの作成 ]
ここで作成された
* アクセスキー ID
* シークレットアクセスキー
をメモします。すぐ使います

[aws-cliをインストール](http://docs.aws.amazon.com/cli/latest/userguide/installing.html)

```bash
pip install --upgrade --user awscli
aws configure
```

```bash:現れるダイアログで入力
AWS Access Key ID [None]: アクセスキー ID
AWS Secret Access Key [None]: シークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]:
```

###デプロイ
上記で設定したAWSアカウントでデプロイします。

```bash:デプロイ！！
./node_modules/serverless/bin/serverless deploy --region ap-northeast-1 --stage production
```

Slackへの通知きましたでしょうか?　

これにプラスして[AWS node.jsライブラリ](https://aws.amazon.com/jp/sdk-for-node-js/)と組み合わせて使うと、
EC2を定期的に立ち上げたり、spotfleet群を立ち上げたりできます。

ささっとコピペでいけるようにまとめてみました。　意外と手軽に始められますよServerlessFramework。

次はAWS API Gatewayとの連携をまとめてみます！
( 冒頭にも書きましたが、あれ手動でやると心折れそうになるくらいつらい、なんでlambdaとAPI gatewayの画面いったり来たりしないといけないんだ、という )