たかこです。
弊社のインフラは、今までAWSのserviceは全てCloudFormationで管理・運用していて、
各サーバー(Gateway(nat兼任)・Frontend・Backend・Batch)の構築はChefを使っていました。
そこからDockerを利用して使い捨ての環境を作っていこう、DevOpsをスムーズにしよう!と変化し、
Chef(Gateway(nat兼任))+ECS(Frontend・Backend・Batch)の環境を構築することにしました。
今回はTaskスケールアウトのCloudFormationのTemplateを書きます。
テンプレート作成
まず、ロールを作成します。
EC2のAutoScalingのタイプとは別で、ECSのスケーリングはApplicationAutoScalingを利用します。
なので、application-autoscaling.amazonaws.comをエンティティとして、
Serviceの数の参照と更新、cloudwatchのalarmの参照を許可します。
"ECSAutoScaleRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "application-autoscaling.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "ecs-autoscale", "PolicyDocument": { "Statement": [ { "Effect": "Allow", "Action": [ "ecs:DescribeServices", "ecs:UpdateService", "cloudwatch:DescribeAlarms" ], "Resource": "*" } ] } } ] } },
次に、スケーリングのトリガーを設定します。
この設定だと、最低1・最大2のタスクが実行されます。
ResourceIdは「service/cluster名/サービス名」で、ScalableDimensionとServiceNamespaceはいまのところ固定です。
"ServiceAutoScalingTarget": { "Type" : "AWS::ApplicationAutoScaling::ScalableTarget", "Properties" : { "MaxCapacity" : 2, "MinCapacity" : 1, "ResourceId" : { "Fn::Join": [ "", [ "service/", { "Ref": "Cluster" }, "/", { "Ref": "Service" } ] ] }, "RoleARN" : { "Ref" : "ServiceRoleARN" }, "ScalableDimension" : "ecs:service:DesiredCount", "ServiceNamespace" : "ecs" } },
ポリシーの設定をします。
ポリシーのResourceId・ScalableDimension・ServiceNamespaceはトリガーと同じものを設定します。
MinAdjustmentMagnitudeはリソースの最低限の個数で、最低1つは動いて欲しいので1にします。
ScalingAdjustmentは追加するタスクの数で、1つ追加したいので100%にしました。
ポリシーのテンプレートの作成はトリガーがなければ失敗するので、"DependsOn" : "ServiceAutoScalingTarget" をつけています。
"ServiceScaleUpPolicy": { "Type" : "AWS::ApplicationAutoScaling::ScalingPolicy", "DependsOn" : "ServiceAutoScalingTarget", "Properties" : { "PolicyName" : "scale-up-policy", "PolicyType" : "StepScaling", "ResourceId" : { "Fn::Join": [ "", [ "service/", { "Ref": "Cluster" }, "/", { "Ref": "Service" } ] ] }, "ScalableDimension" : "ecs:service:DesiredCount", "ServiceNamespace" : "ecs", "StepScalingPolicyConfiguration" : { "AdjustmentType" : "PercentChangeInCapacity", "Cooldown" : 60, "MetricAggregationType" : "Average", "MinAdjustmentMagnitude": 1, "StepAdjustments" : [ { "MetricIntervalLowerBound" : 0, "ScalingAdjustment" : 100 } ] } } },
cloudwatchの設定をします。
これはEC2のAutoScalingでも同じです。
スケールを試したいので閾値を20にしています。
あとは負荷をかければserviceの希望数が変更されて、Taskが2つ起動されます。
※1つのサーバーに同じportのコンテナは立てれないので、私はEC2のAutoScalingと組み合わせて設定しました。
"ServiceCPUAlarmHigh": { "Type": "AWS::CloudWatch::Alarm", "Properties": { "MetricName": "CPUUtilization", "Namespace": "AWS/ECS", "Statistic": "Maximum", "Period": "300", "EvaluationPeriods": "1", "Threshold": "20", "AlarmActions": [ { "Ref": "ServiceScaleUpPolicy" } ], "Dimensions": [ { "Name": "ClusterName", "Value": { "Ref": "Cluster" } }, { "Name": "ServiceName", "Value": { "Ref": "Service" } } ], "ComparisonOperator": "GreaterThanOrEqualToThreshold" } }
全部まとめると
つなげるとこんな感じです。
{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "service cloudformation stack", "Parameters" : { "Cluster" : { "Type" : "String", "Default": "【Cluster名】" }, "Service" : { "Type" : "String", "Default": "【Service名】" } }, "Resources" : { "ECSAutoScaleRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "application-autoscaling.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "ecs-autoscale", "PolicyDocument": { "Statement": [ { "Effect": "Allow", "Action": [ "ecs:DescribeServices", "ecs:UpdateService", "cloudwatch:DescribeAlarms" ], "Resource": "*" } ] } } ] } }, "ServiceAutoScalingTarget": { "Type" : "AWS::ApplicationAutoScaling::ScalableTarget", "Properties" : { "MaxCapacity" : 2, "MinCapacity" : 1, "ResourceId" : { "Fn::Join": [ "", [ "service/", { "Ref": "Cluster" }, "/", { "Ref": "Service" } ] ] }, "RoleARN" : {"Fn::GetAtt" : ["ECSAutoScaleRole", "Arn"] }, "ScalableDimension" : "ecs:service:DesiredCount", "ServiceNamespace" : "ecs" } }, "ServiceScaleUpPolicy": { "Type" : "AWS::ApplicationAutoScaling::ScalingPolicy", "DependsOn" : "ServiceAutoScalingTarget", "Properties" : { "PolicyName" : "scale-up-policy", "PolicyType" : "StepScaling", "ResourceId" : { "Fn::Join": [ "", [ "service/", { "Ref": "Cluster" }, "/", { "Ref": "Service" } ] ] }, "ScalableDimension" : "ecs:service:DesiredCount", "ServiceNamespace" : "ecs", "StepScalingPolicyConfiguration" : { "AdjustmentType" : "PercentChangeInCapacity", "Cooldown" : 60, "MetricAggregationType" : "Average", "MinAdjustmentMagnitude": 1, "StepAdjustments" : [ { "MetricIntervalLowerBound" : 0, "ScalingAdjustment" : 100 } ] } } }, "ServiceCPUAlarmHigh": { "Type": "AWS::CloudWatch::Alarm", "Properties": { "MetricName": "CPUUtilization", "Namespace": "AWS/ECS", "Statistic": "Maximum", "Period": "300", "EvaluationPeriods": "1", "Threshold": "18", "AlarmActions": [ { "Ref": "ServiceScaleUpPolicy" } ], "Dimensions": [ { "Name": "ClusterName", "Value": { "Ref": "Cluster" } }, { "Name": "ServiceName", "Value": { "Ref": "Service" } } ], "ComparisonOperator": "GreaterThanOrEqualToThreshold" } } }, "Outputs": { "ECSAutoScaleRole": { "Value": { "Ref" : "ECSAutoScaleRole" } }, "ServiceAutoScalingTarget": { "Value": { "Ref" : "ServiceAutoScalingTarget" } }, "ServiceScaleUpPolicy": { "Value": { "Ref" : "ServiceScaleUpPolicy" } }, "ServiceCPUAlarmHigh": { "Value": { "Ref" : "ServiceCPUAlarmHigh" } } } }
参考
・Amazon ECSでAuto Scaling
https://aws.amazon.com/jp/blogs/news/automatic-scaling-with-amazon-ecs
・CloudFormation - Template Reference
AWS::ApplicationAutoScaling::ScalableTarget
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalabletarget.html
・AWS::ApplicationAutoScaling::ScalingPolicy
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalingpolicy.html