okochangの馬鹿でありがとう

ふらふら適当に世間を生きる日々でございます

Termination Protection 対 Auto Scaling

こんにちは@okoc_changです。
Auto Scalingを使っていると、必要なタイミングでインスタンスが起動することが出来ますし、必要がなくなったタイミングでインスタンスを削除することが出来ます。
また、EC2にはTermination Protection(disable-api-termination)というインスタンスの削除を防止する機能があります。
今回はこれを同時に使った場合の挙動を確認します。
以下のリンクには ”Auto Scaling overrides the instance termination protection attribute, if enabled”と書かれてあります。

また、以下のリンクには”Instances that are part of an Auto Scaling group are not covered by termination protection”と書かれています。

どうやらTeminate ProtectionはAuto Scalingによって上書きされてしまうようですが、実際にどんな挙動になるかを確認します。(2013年12月時点の結果です)

テスト環境構築構成

今回は構成をシンプルにするために、Auto Scalingで起動するインスタンスは一つのAZに固定しています。

f:id:okochang:20131205095329p:plain

テスト環境構築

作業前の状態確認

当然ですが、最初はELBもAuto Scalingも存在しません。

$ aws elb describe-load-balancers --region ap-northeast-1
{
    "LoadBalancerDescriptions": []
}
$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1
{
    "AutoScalingGroups": []
}

ELBの作成

最初にELBの作成します。

$ aws elb create-load-balancer \
--load-balancer-name okochang-lb \
--listeners Protocol=http,LoadBalancerPort=80,InstanceProtocol=http,InstancePort=80 \
--subnets "subnet-zzzzzzzz" \
--security-groups "sg-yyyyyyyy" \
--region ap-northeast-1
{
    "DNSName": "okochang-lb-1512932827.ap-northeast-1.elb.amazonaws.com"
}

先ほど作成したELBにヘルスチェックの設定をします。

$ aws elb configure-health-check \
--load-balancer-name okochang-lb \
--health-check Target=TCP:80,Interval=30,Timeout=10,UnhealthyThreshold=2,HealthyThreshold=5 \
--region ap-northeast-1
{
    "HealthCheck": {
        "HealthyThreshold": 5, 
        "Interval": 30, 
        "Target": "TCP:80", 
        "Timeout": 10, 
        "UnhealthyThreshold": 2
    }
}

Auto Scalingの作成

Launch Configurationを作成します

$ aws autoscaling create-launch-configuration \
--launch-configuration-name okochang-lc \
--image-id ami-xxxxxxxx \
--key-name okochang-key \
--security-groups sg-b72d31db \
--instance-type t1.micro \
--associate-public-ip-address \
--region ap-northeast-1

Auto Scaling Groupを作成します

$ aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name okochang-asg \
--launch-configuration-name okochang-lc \
--min-size 2 \
--max-size 4 \
--load-balancer-names okochang-lb \
--health-check-type ELB \
--health-check-grace-period 300 \
--vpc-zone-identifier subnet-zzzzzzzz \
--region ap-northeast-1

Scale outのポリシーを作成します

$ aws autoscaling put-scaling-policy \
--auto-scaling-group-name okochang-asg \
--policy-name okochang-scale-out \
--scaling-adjustment 1 \
--adjustment-type ChangeInCapacity \
--cooldown 300 \
--region ap-northeast-1
{
    "PolicyARN": "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:36df9388-8dce-4c18-afcd-14f79ff88636:autoScalingGroupName/okochang-asg:policyName/okochang-scale-out"
}

Scale Inのポリシーを作成します

$ aws autoscaling put-scaling-policy \
--auto-scaling-group-name okochang-asg \
--policy-name okochang-scale-in \
--scaling-adjustment -1 \
--adjustment-type ChangeInCapacity \
--cooldown 300 \
--region ap-northeast-1
{
    "PolicyARN": "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:856b384d-495d-4d54-8df2-d03b25e86301:autoScalingGroupName/okochang-asg:policyName/okochang-scale-in"
}

Scale Outのアラームを作成します

$ aws cloudwatch put-metric-alarm \
--alarm-name okochang-scale-out-alarm \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--statistic Average \
--period 180 \
--alarm-actions "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:36df9388-8dce-4c18-afcd-14f79ff88636:autoScalingGroupName/okochang-asg:policyName/okochang-scale-out" \
--evaluation-periods 1 \
--threshold 70 \
--comparison-operator GreaterThanThreshold \
--region ap-northeast-1

Scale Inのアラームを作成します

$ aws cloudwatch put-metric-alarm \
--alarm-name okochang-scale-in-alarm \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--statistic Average \
--period 180 \
--alarm-actions "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:856b384d-495d-4d54-8df2-d03b25e86301:autoScalingGroupName/okochang-asg:policyName/okochang-scale-in" \
--evaluation-periods 1 \
--threshold 30 \
--comparison-operator LessThanThreshold \
--region ap-northeast-1

検証スタート

現時点でのAuto Scaling Groupの状態とELBからのヘルスチェックは以下の通りです。
※Auto Scaling Group内のEC2インスタンスはどちらも正常な状態

$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}"
{
  "Instances": [
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "InService",
      "HealthStatus": "Healthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-f59493f0"
    },
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "InService",
      "HealthStatus": "Healthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-0270d307"
    }
  ]
}

$ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates"
[
  {
    "Description": "N/A",
    "State": "InService",
    "ReasonCode": "N/A",
    "InstanceId": "i-0270d307"
  },
  {
    "Description": "N/A",
    "State": "InService",
    "ReasonCode": "N/A",
    "InstanceId": "i-f59493f0"
  }
]

ここでi-0270d307のTermination Protectionを有効にしてみます

$ aws ec2 modify-instance-attribute --instance-id i-0270d307 --disable-api-termination "{\"Value\": true}" --region ap-northeast-1
{
    "return": "true"
}

$ aws ec2 describe-instance-attribute --instance-id i-0270d307 --attribute disableApiTermination --region ap-northeast-1 | jq ".DisableApiTermination"
{
  "Value": true
}

次にTermination Protectionを有効にしたインスタンスApacheを停止してから、Auto Scaling GroupとELBの分散対象となるインスタンスを確認します。
ELBからの監視が失敗してOutOfServiceとなり、HealthStatusもUnhealthyになります。

$ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates"
[
  {
    "Description": "Instance has failed at least the UnhealthyThreshold number of health checks consecutively.",
    "State": "OutOfService",
    "ReasonCode": "Instance",
    "InstanceId": "i-0270d307"
  },
  {
    "Description": "N/A",
    "State": "InService",
    "ReasonCode": "N/A",
    "InstanceId": "i-f59493f0"
  }
]

$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}"
{
  "Instances": [
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "InService",
      "HealthStatus": "Healthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-f59493f0"
    },
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "InService",
      "HealthStatus": "Unhealthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-0270d307"
    }
  ]
}

しばらくするとAuto Scaling Groupには新しいインスタンス(i-23f99926)が起動し、先ほどのインスタンスがAuto Scaling Groupから外れています。
また、ELBにも新しいインスタンスが分散対象に登録されている事が分かります。

$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}"
{
  "Instances": [
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "InService",
      "HealthStatus": "Healthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-f59493f0"
    },
    {
      "LaunchConfigurationName": "okochang-lc",
      "LifecycleState": "Pending",
      "HealthStatus": "Healthy",
      "AvailabilityZone": "ap-northeast-1c",
      "InstanceId": "i-23f99926"
    }
  ]
}

$ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates"
[
  {
    "Description": "Instance has failed at least the UnhealthyThreshold number of health checks consecutively.",
    "State": "OutOfService",
    "ReasonCode": "Instance",
    "InstanceId": "i-23f99926"
  },
  {
    "Description": "N/A",
    "State": "InService",
    "ReasonCode": "N/A",
    "InstanceId": "i-f59493f0"
  }
]

しかし以下のように先ほどのEC2インスタンスはrunningのままでTerminateはされていません。
さすがはTermination Protectionといったところでしょうか。

$ aws ec2 describe-instances --instance-id i-0270d307 --region ap-northeast-1 | jq ".Reservations[] | .Instances[] | {State}"
{
  "State": {
    "Name": "running",
    "Code": 16
  }
}

と、思いきやしばらく待っているとAuto ScalingによってTerminateされてしまいました。

$ aws ec2 describe-instances --instance-id i-0270d307 --region ap-northeast-1 | jq ".Reservations[] | .Instances[] | {State}"
{
  "State": {
    "Name": "terminated",
    "Code": 48
  }
}

まとめ

一瞬、Termination Protectionの勝利か!と思いましたが、ドキュメントに記載されているように、Auto Scalingを使っている場合はTermination Protectionすらも上書きされてしまいます。
最強の盾(Termination Protection)よりも最強の矛(Auto Scaling)が強いという結果となりました。