概要
TerraformでECS環境の構築 - Carpe Diemでは書いてなかったオートスケールについてです。
terraform 0.7からServiceでのオートスケールにも対応したので、それを使って構築します。
環境
- Ubuntu 14.04
- Terraform 0.7.3
実装
完成形はこちら github.com
Autoscale用のIAMの追加
Amazon ECS サービスの Auto Scaling IAM Role - Amazon EC2 Container Serviceを参考に用意します。
まずはJSONでPolicyの用意
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "application-autoscaling.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
次にRoleを用意
resource "aws_iam_role" "ecs_autoscale_role" { name = "ecs_autoscale_role" assume_role_policy = "${file("policies/autoscale-assume-role.json")}" }
最後に先ほどのPolicyをRoleへattachする設定をします。
resource "aws_iam_policy_attachment" "ecs_autoscale_role_attach" { name = "ecs-autoscale-role-attach" roles = ["${aws_iam_role.ecs_autoscale_role.name}"] policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole" }
ECS cluster用のpolicyの追加
resource "aws_autoscaling_policy" "scale_out" { name = "Instance-ScaleOut-CPU-High" scaling_adjustment = 1 adjustment_type = "ChangeInCapacity" cooldown = 300 autoscaling_group_name = "${aws_autoscaling_group.dev_api.name}" } resource "aws_autoscaling_policy" "scale_in" { name = "Instance-ScaleIn-CPU-Low" scaling_adjustment = -1 adjustment_type = "ChangeInCapacity" cooldown = 300 autoscaling_group_name = "${aws_autoscaling_group.dev_api.name}" }
ECS cluster用のalarmの追加
スケールアウトでは5分間で平均でCPU使用率が30%以上になったらアクションを起こすようにします。
スケールインでは5分間で平均でCPU使用率が5%以下になったらアクションを起こすようにします。
resource "aws_cloudwatch_metric_alarm" "dev_api_cluster_high" { alarm_name = "dev-api-cluster-CPU-Utilization-High-30" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "300" statistic = "Average" threshold = "30" dimensions { ClusterName = "${aws_ecs_cluster.api_cluster.name}" } alarm_actions = ["${aws_autoscaling_policy.scale_out.arn}"] } resource "aws_cloudwatch_metric_alarm" "dev_api_cluster_low" { alarm_name = "dev-api-cluster-CPU-Utilization-Low-5" comparison_operator = "LessThanThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "300" statistic = "Average" threshold = "5" dimensions { ClusterName = "${aws_ecs_cluster.api_cluster.name}" } alarm_actions = ["${aws_autoscaling_policy.scale_in.arn}"] }
注意点は以下。
dimensions
をClusterName
にしています。この場合EC2インスタンスの使用率ではなく、クラスターとしての使用率を元にしているので、stress
コマンドでCPUを上げても反応しないです。ab
でちゃんとcluster自体の使用率を上げる必要があります。
ECS service用のpolicyの追加
resource "aws_appautoscaling_target" "target" { service_namespace = "ecs" resource_id = "service/${aws_ecs_cluster.api_cluster.name}/${aws_ecs_service.api_service.name}" scalable_dimension = "ecs:service:DesiredCount" role_arn = "${aws_iam_role.ecs_autoscale_role.arn}" min_capacity = 1 max_capacity = 2 } resource "aws_appautoscaling_policy" "scale_out" { name = "scale-out" resource_id = "service/${aws_ecs_cluster.api_cluster.name}/${aws_ecs_service.api_service.name}" adjustment_type = "ChangeInCapacity" cooldown = 300 metric_aggregation_type = "Average" step_adjustment { metric_interval_lower_bound = 0 scaling_adjustment = 1 } depends_on = ["aws_appautoscaling_target.target"] } resource "aws_appautoscaling_policy" "scale_in" { name = "scale-in" resource_id = "service/${aws_ecs_cluster.api_cluster.name}/${aws_ecs_service.api_service.name}" adjustment_type = "ChangeInCapacity" cooldown = 300 metric_aggregation_type = "Average" step_adjustment { metric_interval_upper_bound = 0 scaling_adjustment = -1 } depends_on = ["aws_appautoscaling_target.target"] }
注意点は以下。
role_arn
にはautoscaleのIAMを設定aws_appautoscaling_policy
にはaws_appautoscaling_target
が必須metric_interval_lower_bound
とmetric_interval_upper_bound
の設定値。aws_cloudwatch_metric_alarm
のthresholdが50のとき、以下のように扱われます。
なのでスケールアウトの時にmetric_interval_upper_bound = 0
としちゃったり、逆にスケールインの時にmetric_interval_lower_bound = 0
としちゃうと、スケールポリシーが発火しなくなってしまいます。
ECS service用のalarmの追加
resource "aws_cloudwatch_metric_alarm" "dev_api_service_high" { alarm_name = "dev-api-service-CPU-Utilization-High-30" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "300" statistic = "Average" threshold = "30" dimensions { ClusterName = "${aws_ecs_cluster.api_cluster.name}" ServiceName = "${aws_ecs_service.api_service.name}" } alarm_actions = ["${aws_appautoscaling_policy.scale_out.arn}"] } resource "aws_cloudwatch_metric_alarm" "dev_api_service_low" { alarm_name = "dev-api-service-CPU-Utilization-Low-5" comparison_operator = "LessThanThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "180" statistic = "Average" threshold = "5" dimensions { ClusterName = "${aws_ecs_cluster.api_cluster.name}" ServiceName = "${aws_ecs_service.api_service.name}" } alarm_actions = ["${aws_appautoscaling_policy.scale_in.arn}"] }
注意点は以下。
- dimensionsには
ClusterName
、ServiceName
の両方が必要。ServiceName
だけだと検知されませんでした。 - Service側のスケールインの
period
はCluster側のスケールインの時間より短いほうがいい。
スケール検証
abを使って負荷を上げます。ただabだけだとCPU使用率30まで上げるのが大変なので、今回は手動でthresholdを10%に下げて検証しました。
$ sudo yum install -y httpd24-tools
負荷をかけます。
$ ab -n 100000 -c 1000 インスタンスのURL
問題なく設定できていればスケールアウト時は
- EC2のインスタンス増加
- ECSのserviceのdesired count増加
- task増加
スケールイン時は
- ECSのserviceのdesired count減少
- task減少
- EC2のインスタンス減少
となります。