LambdaでEKS Worker Nodeの夜間・休日停止を行う

## はじめに
検証環境やステージング環境でEKSを使用するときには夜間・休日には停止させて料金を抑えたいことがあると思います。
しかしながらEKSのWorker Nodeの数は宣言的であるがために停止させても新たに立ち上がります。
Masterを消すにしても元の状態に戻すためのアプリケーションのバックアップ・リストア作業を全て自動化しておかないと面倒です。

今回はLambdaでAutoscaling Groupのパラメータを変更することでWorker Nodeを夜間・休日にシャットダウンさせます。

## 実施手順
### １. Lambda関数の作成
ランタイムはPython 3.7とします。
<img width="1031" alt="スクリーンショット 2020-02-19 0.14.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/5cec3ad8-c595-c201-35ff-c402a531975b.png">

関数は次のようになります。
停止と起動の両方に対応できるようにします。

```python:lambda_function.py
import boto3

autoscaling = boto3.client("autoscaling")

def lambda_handler(event, context):
  args = dict(AutoScalingGroupName=event["AutoSaclingGroupName"], 
              MinSize=event["MinSize"], 
              MaxSize=event["MaxSize"], 
              DesiredCapacity=event["DesiredCapacity"])
  autoscaling.update_auto_scaling_group(**args)
```

### ２. CloudWatch Eventsルールの作成
起動停止の2種類のルールをCloudWatch Eventsで作成していきます。
Nodeは平日7:00-23:00(JST)で起動しているものとします。
<img width="1440" alt="スクリーンショット 2020-02-19 0.29.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/e238010b-9acb-3451-9e8d-19ee559455e7.png">

#### 停止ルール
スケジュールではcron式`(0 14 ? * MON-FRI *)`(日本時間で平日23:00)を入れますがUTCであることに注意しましょう。
関数は手順1で作成したものです。
<img width="1400" alt="スクリーンショット 2020-02-19 0.44.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/37f4e340-e136-de53-8081-3962df2ea81e.png">


関数への入力は次のJsonを指定します。
Node数が0になるようにします。

```
{
  "AutoSaclingGroupName": "pulumi-eks-cluster-5b04401d-NodeGroup-1OC0O09KCF829",
  "MinSize": 0,
  "MaxSize": 0,
  "DesiredCapacity": 0
}
```

ルール名を指定して作成を完了させます。
<img width="1403" alt="スクリーンショット 2020-02-19 0.37.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/c7af95f0-65e6-d353-3aae-47bbe5906233.png">

#### 起動ルール
起動ルールではcron式`(0 22 ? * SUN-THU *)`(日本時間で平日7:00)とします。
こちらもUTCであることに注意しましょう。

<img width="1403" alt="スクリーンショット 2020-02-19 0.45.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/7a4ed393-a5d6-ef41-0d17-f7bb7eced033.png">

入力のJsonは次のようになります。
デフォルトNode数を2としています。

```
{
  "AutoSaclingGroupName": "pulumi-eks-cluster-5b04401d-NodeGroup-1OC0O09KCF829",
  "MinSize": 1,
  "MaxSize": 2,
  "DesiredCapacity": 2
}
```

ルール名を指定して作成を完了させます。
<img width="1402" alt="スクリーンショット 2020-02-19 0.47.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/d9f010c1-1116-9c76-9419-d682a72e5f9b.png">

### ３. IAM Policyのアタッチ
手順1で自動で作成されたIAM RoleにAutoScalingGroupを操作するためのIAM Policyをアタッチします。

<img width="1402" alt="スクリーンショット 2020-02-19 0.49.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/d4b33e34-8ca0-fd66-d48f-a5d9f509d9f5.png">

updateするだけなのでオーバーキルではありますが、事前に用意されている`AutoScalingFullAccess`を選択することとします。

<img width="1400" alt="スクリーンショット 2020-02-19 0.54.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/ee097e91-bd28-41c5-7fcb-20c9e5ca9933.png">

問題なくアタッチされたことを確認します。

<img width="1400" alt="スクリーンショット 2020-02-19 0.57.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/3448a0de-6541-a228-7626-3e72704c1d4a.png">


## テスト
Lambdaの画面でテストをします。

テストイベント設定でNode停止ルールを設定します。

<img width="1400" alt="スクリーンショット 2020-02-19 1.01.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/e89fd228-1edc-f046-9b3a-2514d411f977.png">

実際にテストしてみましょう。
成功となることが確認できます。

<img width="1402" alt="スクリーンショット 2020-02-19 1.03.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/f628a9bb-9848-15ec-e71e-14e55d63fc3c.png">

AutoScalingGroupの画面でNode数が0になることが確認できます。

<img width="1402" alt="スクリーンショット 2020-02-19 1.06.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/49ab34e8-5b8b-4edf-f181-2a411b36184e.png">


## おわりに
LambdaでEKS Worker Nodeに紐付くAutoscaling GroupのNode数を変更し夜間・休日に停止させる手順を説明しました。

本当に料金を抑えるなら下記3つの設定を全てコード化しておき毎日削除・作成を行うことが理想だと思います。

* AWSリソース
* Kubernetesリソース
* アプリケーション

ただ、コード化する時間が取れない場合はとりあえず今回の手順に沿ってWorker Nodeだけ落とすだけでも料金は抑えられます。