Skip to content

Commit

Permalink
feat(python): adds schedule scaling policy for ecs (#964)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Kaiser <[email protected]>
  • Loading branch information
revarawat and kaiz-io authored Dec 20, 2023
1 parent c013446 commit 2650d88
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 0 deletions.
18 changes: 18 additions & 0 deletions python/ecs-schedulescaling/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

# Schedule scaling for ecs

This project contains code to deploy ecs cluster with the ability to scale fargate tasks based on day-night schedule.

The `cdk.json` contains context variables for schedule along with minimum and maximum capacity count for day and night schedule.

Use below command to view application auto scaling scheduled actions:
```
aws application-autoscaling describe-scheduled-actions \
--service-namespace ecs
```

Use below command to view descriptive information about the scaling activities:
```
aws application-autoscaling describe-scaling-activities \
--service-namespace ecs
```
10 changes: 10 additions & 0 deletions python/ecs-schedulescaling/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3

import aws_cdk as cdk

from schedulescaling.schedulescaling_stack import SchedulescalingStack

app = cdk.App()
SchedulescalingStack(app, "SchedulescalingStack")

app.synth()
65 changes: 65 additions & 0 deletions python/ecs-schedulescaling/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"scaling":true,
"daytime": [{"cron":"cron(00 08 * * ? *)"},{"min":2},{"max":3}],
"nightime": [{"cron":"cron(00 18 * * ? *)"},{"min":1},{"max":2}]
}
}
2 changes: 2 additions & 0 deletions python/ecs-schedulescaling/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.111.0
constructs>=10.0.0,<11.0.0
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from aws_cdk import Duration, Stack
from aws_cdk import aws_applicationautoscaling as appscaling
from aws_cdk import aws_ec2 as ec2
from aws_cdk import aws_ecs as ecs
from aws_cdk import aws_ecs_patterns as ecs_patterns
from aws_cdk import aws_iam as iam
from aws_cdk import aws_sns_subscriptions as subs
from constructs import Construct


class SchedulescalingStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

scaling = self.node.try_get_context("scaling")
dayschedule = self.node.try_get_context("daytime")
nightschedule = self.node.try_get_context("nightime")

min_capacity = 1
max_capacity = 1

# daytime scaling schedule(UTC)
day_schedule = dayschedule[0]["cron"]
day_min = dayschedule[1]["min"]
day_max = dayschedule[2]["max"]

# nighttime scaling schedule(UTC)
night_schedule = nightschedule[0]["cron"]
night_min = nightschedule[1]["min"]
night_max = nightschedule[2]["max"]

vpc = ec2.Vpc(self, "ecsVpc", max_azs=2)

ecs_cluster = ecs.Cluster(
self,
id="ecscluster",
vpc=vpc,
container_insights=True,
enable_fargate_capacity_providers=True,
)

# create task definition
task_definition = ecs.FargateTaskDefinition(self, "taskdef", cpu=256)
image = ecs.ContainerImage.from_registry("amazon/amazon-ecs-sample")
container = task_definition.add_container(id="ecs-con-task", image=image)
container.add_port_mappings(ecs.PortMapping(container_port=8080))

# define service
service = ecs.FargateService(
self, "FargateService", cluster=ecs_cluster, task_definition=task_definition
)
# define autoscaling
if scaling:
target = appscaling.ScalableTarget(
self,
"ScalableTarget",
service_namespace=appscaling.ServiceNamespace.ECS,
min_capacity=min_capacity,
max_capacity=max_capacity,
resource_id=f"service/{ecs_cluster.cluster_name}/{service.service_name}",
scalable_dimension="ecs:service:DesiredCount",
)
target.scale_on_schedule(
"daytime",
schedule=appscaling.Schedule.expression(day_schedule),
min_capacity=day_min,
max_capacity=day_max,
)
target.scale_on_schedule(
"nighttime",
schedule=appscaling.Schedule.expression(night_schedule),
min_capacity=night_min,
max_capacity=night_max,
)

0 comments on commit 2650d88

Please sign in to comment.