CloudWatchとLambdaによるEC2の夜間停止

# 初めに

AWSのアドベントカレンダーもっと盛り上がれ！もう時代遅れか！？

と念じております。


さてさて、前回はWEBアプリケーションの環境を構築しましたが

今回はEC2の夜間停止をCloudWatchとLambdaを利用して実装していきたいと思います。

夜間停止の流れはLambdaで起動用の関数と停止用の関数を作成し

CloudWatchを利用して朝に起動用の関数を、夜に停止用の関数を実行することで実現させます。

第1部　「[AWSを避けていたPGがALBとEC2を利用してWEBアプリケーション環境を構築してみた](http://qiita.com/tiwu_official/items/220e00c9a18c6c8adb54)」

第2部　「LambdaとCloud WatchによるEC2の夜間停止」←今ここ

第3部　「ALBによるパスルーティング」


# 前置き

[LambdaのScheduleイベントでEC2を自動起動＆自動停止してみた#reinvent](http://dev.classmethod.jp/cloud/aws/lambda-scheduled-event-updown/)を基本的に参考にして構築をしております。

解釈や説明の誤りなど、コメント欄に記載していただけると幸いです。

# Lambda

さて、まずLambdaです。

左のメニューから「Function」を選択肢、「Create a Lambda function」をクリック。

<img width="1270" alt="スクリーンショット 2016-12-21 22.17.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/e6d397f1-a400-3ab1-6157-cda8a9073851.png">

Select blueprintと表示されますが無視して、左のメニューから「Configure function」を選択。


<img width="1244" alt="スクリーンショット 2016-12-21 22.20.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/d271254e-20c3-4d05-eea8-2099d6852df8.png">


この画面で起動用の関数と停止用の関数を作成していきます。

まずは起動用の関数から。


| 項目 | 値 |
|:-:|:-:|
| Name | 適当に |
| Description  | 適当に |
|  Runtime |  Node.js 4.3 |
|  Code entry type |  Edit code inline |


<img width="1263" alt="スクリーンショット 2016-12-21 22.27.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/0c0d1e0f-deac-4aba-3b25-0eaa21d2c692.png">

`Lambda function code`には下記、起動用の関数を、記述します。

`INSTANCE_ID`にはEC2のinstance idを、`AWS.config.region`にはリージョンを入れてください。

リージョンに関しては東京なら　`ap-northeast-1`です。

その他リージョンの場合は下記公式から確認してください。

[リージョンとアベイラビリティーゾーン](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)


```javascript
const INSTANCE_ID = '*****';
 
var AWS = require('aws-sdk'); 
AWS.config.region = '*****';
 
function ec2Start(cb){
    var ec2 = new AWS.EC2();
    var params = {
        InstanceIds: [
            INSTANCE_ID
        ]
    };
  
    ec2.startInstances(params, function(err, data) {
        if (!!err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            cb();
        }
    });
}
exports.handler = function(event, context) {
    console.log('start');
    ec2Start(function() {
        context.done(null, 'Started Instance');
    });
};
```

`Environment variables`は空のままで、`Handler`は`index.handler`を指定。

`Role`はセレクトボックスから`Create a custom role`を選択。

選択すると、別タブでロール作成画面が開きます。


<img width="1229" alt="スクリーンショット 2016-12-21 22.58.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/d05c2792-a3c3-9712-2508-65b8cc199958.png">

別タブで開いた画面では下記のとおりに記述します。

| 項目 | 値 |
|:-:|:-:|
| IAM Role | Create a new IAM Role |
| Role Name  | 適当に |

`Policy Document`は下記jsonを記述します。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "ec2:StartInstances"
      ],
      "Resource":[
      "arn:aws:logs:*:*:*",
      "arn:aws:ec2:*"
      ]
    }
  ]
}
```

記述したら右下の「Allow」をクリックします。画面が閉じるので、Lambdaの画面に戻ります。


<img width="1262" alt="スクリーンショット 2016-12-21 23.07.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/50ed122e-39a7-db56-0cd2-29cb5225bb9e.png">


自動で`Role`が`Choose an existing role`になり、`Exisiting role`が先程作成したロールになっていると思います。

もしなっていなかったら手動で設定してください。

<img width="1149" alt="スクリーンショット 2016-12-21 23.18.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/264e9554-4209-3fbc-798d-ff9080e10321.png">


その他設定はいじらず、右下の「Next」をクリック。

<img width="1154" alt="スクリーンショット 2016-12-21 23.20.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/42072626-48e1-ae3b-5c87-613d53bc6859.png">

確認画面に遷移するので、問題なければ右下の「Create function」をクリック。

<img width="1139" alt="スクリーンショット 2016-12-21 23.21.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/fc84512f-4f61-d38a-9fa7-96d304595bd0.png">

作成した関数のテストをして実際にEC2が起動するか確認したいと思います。

「Test」をクリックします。

<img width="1263" alt="スクリーンショット 2016-12-21 23.28.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/5bdba0d9-9d61-3083-30f6-db5d2f6dd40b.png">

とくに値をいじらず、右下の「Save and test」をクリックします。

画面にログが表示され、EC2が起動していたら成功です。


<img width="856" alt="スクリーンショット 2016-12-21 23.29.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/48785707-0ec9-998a-7a23-8d91e6a1fecc.png">

起動用の関数が確認できたら停止用の関数も作成します。

基本的に起動用の関数と同じ設定です。

`Lambda function code`は下記のコードを

```javascript
const INSTANCE_ID = '*****';
 
var AWS = require('aws-sdk'); 
AWS.config.region = '*****';
 
function ec2Stop(cb){
    var ec2 = new AWS.EC2();
    var params = {
        InstanceIds: [
            INSTANCE_ID
        ]
    };
  
    ec2.stopInstances(params, function(err, data) {
        if (!!err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            cb();
        }
    });
}
exports.handler = function(event, context) {
    console.log('start');
    ec2Stop(function() {
        context.done(null, 'Stoped Instance');
    });
};
```

ロールの`Policy Document`は下記jsonを

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:StopInstances"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*",
                "arn:aws:ec2:*"
            ]
        }
    ]
}
```

これで、起動用の関数と停止用の関数が作成できました。

# CloudWatch

さて、次に（最後ですが）CloudWatchを利用して朝と夜にLambdaの関数を実行したいと思います。


CloudWatchのページで、左のメニューから「Rules」を選び、「Create rule」をクリック

<img width="1262" alt="スクリーンショット 2016-12-22 20.35.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/ac84c910-0167-da54-92e7-b08fc91d1b6d.png">



`Event selector`は`Schedule`を選択し、`Cron expression`のラジオボタンにチェックを入れます。

`Cron expression`の値は、`0 0 ? * MON-FRI *`を入力してください。

cronの詳細な説明は省きますが、AWSの時間と日本の時間は9時間ずれていて

平日の朝9時に実行したい場合は`0 0 ? * MON-FRI *`になります。

`Targets`には`Lambda function`を選択し

`Function`には先程作成した開始用の関数を選択してください。

入力が終わったら「Configure details」をクリック。


<img width="1056" alt="スクリーンショット 2016-12-22 20.38.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/75860d30-42fc-7327-488f-ad1d53d313be.png">

最後にスケジュールの名前や説明文を入力します。


| 項目 | 値 |
|:-:|:-:|
| Name | 適当に |
| Description  | 適当に |
| State  | チェックを入れる |

入力したら、「Create rule」をクリックして完了です。

<img width="1054" alt="スクリーンショット 2016-12-22 20.44.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/81308/d65820cd-586c-a9d6-3dca-762fc2f1a1d2.png">


停止用は平日の18時に停止するように`Cron expression`に

`0 9 ? * MON-FRI *`を入力して、`Function`に停止用の関数を設定してください。

これで、平日朝９時にEC2が起動し、平日夜18時にEC2が停止するようになりました。




# 最後に

夜間停止は思ったより簡単で、AWSのサービスを組み合わせるだけで実現ができました！

あとは、ALBの夜間停止がしたいのですが、どうやらALBは停止という概念はなく

起動しているか、壊すかの２択っぽいです・・・

terraformとかで実現すれば良いのでしょうか・・・調査中です(´・ω・｀)