diff --git a/ecs-service.cfhighlander.rb b/ecs-service.cfhighlander.rb index 399028b..3e2fedb 100644 --- a/ecs-service.cfhighlander.rb +++ b/ecs-service.cfhighlander.rb @@ -20,6 +20,12 @@ ComponentParam 'DnsDomain' end + ComponentParam 'DesiredCount', 1 + ComponentParam 'MinimumHealthyPercent', 100 + ComponentParam 'MaximumPercent', 200 + + ComponentParam 'EnableScaling', 'false', allowedValues: ['true','false'] + if ((defined? network_mode) && (network_mode == "awsvpc")) maximum_availability_zones.times do |az| ComponentParam "SubnetCompute#{az}" diff --git a/ecs-service.cfndsl.rb b/ecs-service.cfndsl.rb index 790601f..b796ba0 100644 --- a/ecs-service.cfndsl.rb +++ b/ecs-service.cfndsl.rb @@ -9,6 +9,8 @@ az_conditions_resources('SubnetCompute', maximum_availability_zones) end + Condition('IsScalingEnabled', FnEquals(Ref('EnableScaling'), 'true')) + log_retention = 7 unless defined?(log_retention) Resource('LogGroup') { Type 'AWS::Logs::LogGroup' @@ -89,6 +91,13 @@ task_def.merge!({MountPoints: mount_points }) end + # volumes from + if task.key?('volumes_from') + task['volumes_from'].each do |source_container| + task_def.merge!({ VolumesFrom: [ SourceContainer: source_container ] }) + end + end + # add port if task.key?('ports') port_mapppings = [] @@ -277,17 +286,9 @@ end IAM_Role('Role') do - AssumeRolePolicyDocument ({ - Statement: [ - Effect: 'Allow', - Principal: { Service: [ 'ecs.amazonaws.com' ] }, - Action: [ 'sts:AssumeRole' ] - ] - }) + AssumeRolePolicyDocument service_role_assume_policy('application-autoscaling') Path '/' - Policies Policies(IAMPolicies.new.create_policies([ - 'ecs-service-role' - ])) + ManagedPolicyArns ["arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"] end has_security_group = false @@ -307,12 +308,20 @@ end end + desired_count = 1 + if (defined? scaling_policy) && (scaling_policy.has_key?('min')) + desired_count = scaling_policy['min'] + elsif defined? desired + desired_count = desired + end + ECS_Service('Service') do Cluster Ref("EcsCluster") - DesiredCount 1 + Property("HealthCheckGracePeriodSeconds", health_check_grace_period || 0) + DesiredCount Ref('DesiredCount') DeploymentConfiguration ({ - MinimumHealthyPercent: 100, - MaximumPercent: 200 + MinimumHealthyPercent: Ref('MinimumHealthyPercent'), + MaximumPercent: Ref('MaximumPercent') }) TaskDefinition Ref('Task') @@ -332,4 +341,104 @@ end end if defined? task_definition + if defined?(scaling_policy) + + IAM_Role(:ServiceECSAutoScaleRole) { + Condition 'IsScalingEnabled' + AssumeRolePolicyDocument service_role_assume_policy('application-autoscaling') + Path '/' + Policies ([ + PolicyName: 'ecs-scaling', + PolicyDocument: { + Statement: [ + { + Effect: "Allow", + Action: ['cloudwatch:DescribeAlarms','cloudwatch:PutMetricAlarm','cloudwatch:DeleteAlarms'], + Resource: "*" + }, + { + Effect: "Allow", + Action: ['ecs:UpdateService','ecs:DescribeServices'], + Resource: Ref('Service') + } + ] + }]) + } + + ApplicationAutoScaling_ScalableTarget(:ServiceScalingTarget) { + Condition 'IsScalingEnabled' + MaxCapacity scaling_policy['max'] + MinCapacity scaling_policy['min'] + ResourceId FnJoin( '', [ "service/", Ref('EcsCluster'), "/", FnGetAtt(:Service,:Name) ] ) + RoleARN FnGetAtt(:ServiceECSAutoScaleRole,:Arn) + ScalableDimension "ecs:service:DesiredCount" + ServiceNamespace "ecs" + } + + ApplicationAutoScaling_ScalingPolicy(:ServiceScalingUpPolicy) { + PolicyName FnJoin('-', [ Ref('EnvironmentName'), component_name, "scale-up-policy" ]) + PolicyType "StepScaling" + ScalingTargetId Ref(:ServiceScalingTarget) + StepScalingPolicyConfiguration({ + AdjustmentType: "ChangeInCapacity", + Cooldown: scaling_policy['up']['cooldown'] || 300, + MetricAggregationType: "Average", + StepAdjustments: [{ ScalingAdjustment: scaling_policy['up']['adjustment'].to_s, MetricIntervalLowerBound: 0 }] + }) + } + + ApplicationAutoScaling_ScalingPolicy(:ServiceScalingDownPolicy) { + Condition 'IsScalingEnabled' + PolicyName FnJoin('-', [ Ref('EnvironmentName'), component_name, "scale-down-policy" ]) + PolicyType 'StepScaling' + ScalingTargetId Ref(:ServiceScalingTarget) + StepScalingPolicyConfiguration({ + AdjustmentType: "ChangeInCapacity", + Cooldown: scaling_policy['up']['cooldown'] || 900, + MetricAggregationType: "Average", + StepAdjustments: [{ ScalingAdjustment: scaling_policy['down']['adjustment'].to_s, MetricIntervalLowerBound: 0 }] + }) + } + + default_alarm = {} + default_alarm['metric_name'] = 'CPUUtilization' + default_alarm['namespace'] = 'AWS/ECS' + default_alarm['statistic'] = 'Average' + default_alarm['period'] = '60' + default_alarm['evaluation_periods'] = '5' + default_alarm['dimentions'] = [ + { Name: 'ServiceName', Value: FnGetAtt(:Service,:Name)}, + { Name: 'ClusterName', Value: Ref('EcsCluster')} + ] + + CloudWatch_Alarm(:ServiceScaleUpAlarm) { + Condition 'IsScalingEnabled' + AlarmDescription FnJoin(' ', [Ref('EnvironmentName'), "#{component_name} ecs scale down alarm"]) + MetricName scaling_policy['up']['metric_name'] || default_alarm['metric_name'] + Namespace scaling_policy['up']['namespace'] || default_alarm['namespace'] + Statistic scaling_policy['up']['statistic'] || default_alarm['statistic'] + Period (scaling_policy['up']['period'] || default_alarm['period']).to_s + EvaluationPeriods scaling_policy['up']['evaluation_periods'].to_s + Threshold scaling_policy['up']['threshold'].to_s + AlarmActions [Ref(:ServiceScalingUpPolicy)] + ComparisonOperator 'GreaterThanThreshold' + Dimensions scaling_policy['up']['dimentions'] || default_alarm['dimentions'] + } + + CloudWatch_Alarm(:ServiceScaleDownAlarm) { + Condition 'IsScalingEnabled' + AlarmDescription FnJoin(' ', [Ref('EnvironmentName'), "#{component_name} ecs scale down alarm"]) + MetricName scaling_policy['down']['metric_name'] || default_alarm['metric_name'] + Namespace scaling_policy['down']['namespace'] || default_alarm['namespace'] + Statistic scaling_policy['down']['statistic'] || default_alarm['statistic'] + Period (scaling_policy['down']['period'] || default_alarm['period']).to_s + EvaluationPeriods scaling_policy['down']['evaluation_periods'].to_s + Threshold scaling_policy['down']['threshold'].to_s + AlarmActions [Ref(:ServiceScalingDownPolicy)] + ComparisonOperator 'LessThanThreshold' + Dimensions scaling_policy['down']['dimentions'] || default_alarm['dimentions'] + } + + end + end diff --git a/ecs-service.config.yaml b/ecs-service.config.yaml index 44def4a..96cec3e 100644 --- a/ecs-service.config.yaml +++ b/ecs-service.config.yaml @@ -5,6 +5,8 @@ log_retention: 7 # network_mode: awsvpc +# health_check_grace_period: 60 +# # cpu: 256 # memory: 256 # @@ -48,3 +50,17 @@ log_retention: 7 # priority: 20 # tags: # Name: api + +# scaling_policy: +# min: 2 +# max: 4 +# up: +# cooldown: 150 +# threshold: 70 +# evaluation_periods: 5 +# adjustment: 2 +# down: +# cooldown: 600 +# threshold: 70 +# evaluation_periods: 5 +# adjustment: -1