たかこです。
弊社のインフラは、今まで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