AWS cloudformationを使ってAuto Scaling設定したらクラウド破産しかけた

罠が多い気がする。
Auto Scalingの設定は注意しましょう、という事で。

## やりたい事

今回やりたかった事は以下の感じです

- CloudFormationとAuto ScalingとELBを使ってローリングデプロイしたい
- CloudFormationのUpdate Stackがトリガーでローリングデプロイを実行

##LaunchConfigurationに注意

LaunchConfigurationで設定しているAMIから起動したインスタンスが正常に稼働しないと破産します。
AMIのテストを行ってれば安心とおもいきや、CloudFormationを使ってLaunchConfigurationを設定したときにえらい事になりました。

AMIのチェックも兼ねてまず手動でLaunchConfigurationを作りテスト。
問題なく動作したのでその設定をみながらCloudFormation用テンプレートに書き起こした。

それがこちら。

```json
    "LaunchConfig": {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "KeyName": {
                "Ref": "KeyName"
            },
            "ImageId": {
                "Ref": "ASAMI"
            },
            "InstanceMonitoring": "false",
            "AssociatePublicIpAddress": "false",
            "SecurityGroups": ["sg-xxxxxx"],
            "InstanceType": {
                "Ref": "InstanceType"
            }
        }
    }
```

この設定をCloudFormationで実行するとえらい事になりました。

Auto ScallingがAMIからインスタンス起動時にデバイスがない(スナップショットからデバイスを作成できない)というエラーが出て、インスタンスが立ち上がらない状態になっていました。

AutoScalingGroup 側では最低サーバー台数を保持しようと次々とサーバーの立ち上げを実行するが、次々と失敗して、サーバーの立ち上げてはTerminateしていくという魔のスパイラル。

このエラーはブロックデバイスの設定を入れてやると解決しました。
公式のテンプレ等には書かれていないのでデフォルトで作成してくれると思って不要かと思っていたのですが・・・。
もしかしたらSSDのEBSが出た影響かもしれません。

```json
            "BlockDeviceMappings": [
                {
                   "DeviceName" : "/dev/sda1",
                   "Ebs" : {
                          "VolumeSize" : "8",
                          "VolumeType" : "standard",
                          "DeleteOnTermination" : "true"
                   }
                }
            ]
```



##HealthCheckTypeをELBにする場合に注意

HealthCheckTypeをELBにする場合いろいろ考慮する点が増えます。
インスタンスが無事に立ち上がってもインスタンス稼働後にミドルウェアが立ち上がり、ELBからInServiceとなる状態にならなければ破産します。
VPC等を使ってる場合はそちらの考慮も必要です。

- VPCとサブネット、AZの確認
- LanchConfigurationのセキュリティグループの設定
- HealthCheckGracePeriodの設定を確認

この中でHealthCheckGracePeriodについてやってしまったのでメモ。
AutoScalingGroupでHealthCheckTypeをELBにすると、HealthCheckGracePeriodという値を設定しないといけないのですが、このHealthCheckGracePeriodはAuto Scalingで起動したインスタンスに対してELBのヘルスチェックを実行するまでの時間、という設定になるそうです。

Auto Scalingの動作として、インスタンスを立ち上げて、サービスが全部立ち上がって準備ができるまでの間にELBのヘルスチェックが行われてしまうと、エラーと判定されて、その立ち上げたインスタンスを`Terminate`してきます。

HealthCheckGracePeriodの時間が短いとどういう事になるか。

```
①Auto Scalingがサーバー立ち上げる
↓
②HealthCheckGracePeriodの時間をまってからサーバーが正常に立ち上がったかチェック
↓
③(まだインスタンスやELBチェックのミドルウェアが立ち上がってなかった場合)
Auto Scaling「正常に起動してない！このインスタンスはダメだ！Terminate！」
↓
④Auto Scaling「DesiredCapacityが○○台だけど今は１台もない。立ち上げないと」
↓
①に戻る

⇒クラウド破産
```

HealthCheckGracePeriodの値は十分に大きな値にしておきましょう。

##被害を最小限に抑えるために

- Auto Scalingのテストをするときは常にAWS Managiment Consoleとにらめっこ
- DesiredCapacity等は最少の値にしておく
- PauseTime　Cooldown　の値を把握しておく。この値が長ければ立ち上げる間隔が長いので失敗に気付いて対処すれば被害が最小限に抑えられる。
- テスト中に問題が見つかったらMinSize、MaxSize、DesiredCapacityを0にしてとりあえずauto scalingの機能を止める


##最終的にこの設定でおちついた

今回、最終的に設定したAuto ScalingのCloudFormationテンプレートはこんな感じです

- VPC環境下
- ELBはすでに作られているものを使う
- 負荷に応じたスケールは行わない
- Update StackするとAutoScalingRollingUpdateにしたがってサーバーを入れ替えていく

```json
{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Rolling deployments template ",

  "Parameters" : {
    "InstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "t1.micro",
      "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","m3.xlarge","m3.2xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    },
    "KeyName" : {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
      "Type" : "String",
      "Default" : "KeyName"
    },
    "VpcId" : {
      "Type" : "String",
      "Description" : "VpcId of your existing Virtual Private Cloud (VPC)",
      "Default" : "vpc-xxxxx"
    },
    "Subnets" : {
      "Type" : "CommaDelimitedList",
      "Description" : "The list of SubnetIds in your Virtual Private Cloud (VPC)",
      "Default" : "subnet-xxxxxxx,subnet-xxxxxxx"
    },
    "ELB" : {
      "Description" : "ELB name",
      "Type" : "String",
      "Default" : "xxxxxxxxxxx"
    },
    "SecurityGroups" : {
      "Type" : "CommaDelimitedList",
      "Description" : "The list of SecurityGroups in your Virtual Private Cloud (VPC)",
      "Default" : "sg-xxxxxxx"
    },
    "AZs" : {
      "Type" : "CommaDelimitedList",
      "Description" : "The list of AvailabilityZones for your Virtual Private Cloud (VPC)",
      "Default" : "ap-northeast-1a,ap-northeast-1c"
    },
    "ASMinCount": {
        "Description": "AutoScaling Min Count",
        "Type": "Number",
        "Default": "1"
    },
    "ASMaxCount": {
        "Description": "AutoScaling Max Count",
        "Type": "Number",
        "Default": "2"
    },
    "ASDesiredCapacity": {
        "Description": "AutoScaling Desired Capacity",
        "Type": "Number",
        "Default": "1"
    },
    "ASUPMaxBatchSize": {
        "Description": "AutoScalingRollingUpdate MaxBatchSize",
        "Type": "Number",
        "Default": "1"
    },
    "ASUPMinInstancesInService": {
        "Description": "AutoScalingRollingUpdate MinInstancesInService",
        "Type": "Number",
        "Default": "1"
    },
    "ASAMI": {
        "Description": "The EC2 ImageId for the Web Server",
        "Type": "String",
        "Default": "ami-xxxxxxxx"
    }
  },

  "Resources" : {
    "WebServerGroup": {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        "UpdatePolicy" : {
            "AutoScalingRollingUpdate" : {
                "MaxBatchSize" : {
                    "Ref" : "ASUPMaxBatchSize"
                },
                "MinInstancesInService" : {
                    "Ref" : "ASUPMinInstancesInService"
                },
                "PauseTime" : "PT5M"
            }
        },
        "Properties": {
            "AvailabilityZones": {
                "Fn::GetAZs": ""
            },
            "VPCZoneIdentifier" : {
                "Ref" : "Subnets"
            },
            "HealthCheckType" : "ELB",
            "HealthCheckGracePeriod" : "600",
            "Cooldown" : "300",
            "LaunchConfigurationName": {
                "Ref": "LaunchConfig"
            },
            "MinSize": {
                "Ref": "ASMinCount"
            },
            "MaxSize": {
                "Ref": "ASMaxCount"
            },
            "DesiredCapacity": {
                "Ref": "ASDesiredCapacity"
            },
            "LoadBalancerNames": [
                {
                    "Ref": "ELB"
                }
            ]
        }
    },
    "LaunchConfig": {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "KeyName": {
                "Ref": "KeyName"
            },
            "ImageId": {
                "Ref": "ASAMI"
            },
            "InstanceMonitoring": "false",
            "AssociatePublicIpAddress": "false",
            "BlockDeviceMappings": [
                {
                   "DeviceName" : "/dev/sda1",
                   "Ebs" : { "VolumeSize" : "8", "VolumeType" : "standard", "DeleteOnTermination" : "true" }
                }
            ],
            "SecurityGroups" : {
                    "Ref" : "SecurityGroups"
            },
            "InstanceType": {
                "Ref": "InstanceType"
            }
        }
    }
  }
}
```

## 20170928追記

現在はEC2インスタンスは1秒あたりによる請求が可能になっている為、
最低ラインが1時間あたりの請求だった当時よりは問題にならないと思います！

https://aws.amazon.com/jp/blogs/news/new-per-second-billing-for-ec2-instances-and-ebs-volumes/