|
| 1 | +# AWS ECS - L3 Construct for Autoscaling ECS/Fargate Service that Processes Items in a SQS Queue |
| 2 | + |
| 3 | +To address issue [#2396](https://github.com/awslabs/aws-cdk/issues/2396), the AWS ECS CDK construct library should provide a way for customers to create a queue worker service (an AWS ECS/Fargate service that processes items from an sqs queue). This would mean adding new ECS CDK constructs `Ec2QueueWorkerService` and `FargateQueryWorkerService`, that would take in the necessary properties required to create a task definition, an SQS queue as well as an ECS/Fargate service and enable autoscaling for the service based on cpu usage and the SQS queue's approximateNumberOfMessagesVisible metric. |
| 4 | + |
| 5 | +## General approach |
| 6 | + |
| 7 | +The new `ecs.QueueWorkerServiceBase`, `ecs.Ec2QueueWorkerService` and `ecs.FargateQueueWorkerService` classes will create L3 constructs for: |
| 8 | + |
| 9 | +* Ec2QueueWorkerService |
| 10 | +* FargateQueueWorkerService |
| 11 | + |
| 12 | +A `QueueWorkerService` will create a task definition with the specified container (on both EC2 and Fargate). An AWS SQS `Queue` will be created and autoscaling of the ECS Service will be dependent on both CPU as well as the SQS queue's `ApproximateNumberOfMessagesVisible` metric. |
| 13 | + |
| 14 | +The `QueueWorkerService` constructs (for EC2 and Fargate) will use the following existing constructs: |
| 15 | + |
| 16 | +* Ec2TaskDefinition/FargateTaskDefinition - To create a Task Definition for the container to start |
| 17 | +* SQSQueue - The queue that the worker is processing from |
| 18 | +* Ec2Service/FargateService - The Service running the container |
| 19 | + |
| 20 | +## Code changes |
| 21 | + |
| 22 | +Given the above, we should make the following changes to support queue workers on ECS (for both EC2 and Fargate): |
| 23 | +1. Create `QueueWorkerServiceBaseProps` interface and `QueueWorkerServiceBase` construct |
| 24 | +2. Create `Ec2QueueWorkerServiceProps` interface and `Ec2QueueWorkerService` construct |
| 25 | +3. Create `FargateQueueWorkerServiceProps` interface and `FargateQueueWorkerService` construct |
| 26 | + |
| 27 | +### Part 1: Create `QueueWorkerServiceBaseProps` interface and `QueueWorkerServiceBase` construct |
| 28 | + |
| 29 | +The `QueueWorkerServiceBaseProps` interface will contain common properties used to construct both the Ec2QueueWorkerService and the FargateQueueWorkerService: |
| 30 | + |
| 31 | +```ts |
| 32 | +/** |
| 33 | + * Properties to define a Query Worker service |
| 34 | + */ |
| 35 | +export interface QueueWorkerServiceBaseProps { |
| 36 | + /** |
| 37 | + * Cluster where service will be deployed |
| 38 | + */ |
| 39 | + readonly cluster: ICluster; |
| 40 | + |
| 41 | + /** |
| 42 | + * The image to start. |
| 43 | + */ |
| 44 | + readonly image: ContainerImage; |
| 45 | + |
| 46 | + /** |
| 47 | + * The CMD value to pass to the container as a string array. |
| 48 | + * |
| 49 | + * @default none |
| 50 | + */ |
| 51 | + readonly command?: string[]; |
| 52 | + |
| 53 | + /** |
| 54 | + * Number of desired copies of running tasks |
| 55 | + * |
| 56 | + * @default 1 |
| 57 | + */ |
| 58 | + readonly desiredTaskCount?: number; |
| 59 | + |
| 60 | + /** |
| 61 | + * Flag to indicate whether to enable logging |
| 62 | + * |
| 63 | + * @default true |
| 64 | + */ |
| 65 | + readonly enableLogging?: boolean; |
| 66 | + |
| 67 | + /** |
| 68 | + * The environment variables to pass to the container. |
| 69 | + * |
| 70 | + * @default 'QUEUE_NAME: queue.queueName' |
| 71 | + */ |
| 72 | + readonly environment?: { [key: string]: string }; |
| 73 | + |
| 74 | + /** |
| 75 | + * A queue for which to process items from. |
| 76 | + * |
| 77 | + * If specified and this is a FIFO queue, the queue name must end in the string '.fifo'. |
| 78 | + * @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html |
| 79 | + * |
| 80 | + * @default 'SQSQueue with CloudFormation-generated name' |
| 81 | + */ |
| 82 | + readonly queue?: IQueue; |
| 83 | + |
| 84 | + /** |
| 85 | + * Maximum capacity to scale to. |
| 86 | + * |
| 87 | + * @default (desiredTaskCount * 2) |
| 88 | + */ |
| 89 | + readonly maxScalingCapacity?: number |
| 90 | + |
| 91 | + /** |
| 92 | + * The intervals for scaling based on the SQS queue's ApproximateNumberOfMessagesVisible metric. |
| 93 | + * |
| 94 | + * Maps a range of metric values to a particular scaling behavior. |
| 95 | + * https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html |
| 96 | + * |
| 97 | + * @default [{ upper: 0, change: -1 },{ lower: 100, change: +1 },{ lower: 500, change: +5 }] |
| 98 | + */ |
| 99 | + readonly scalingSteps: autoScaling.ScalingInterval[]; |
| 100 | +} |
| 101 | +``` |
| 102 | + |
| 103 | +### Part 2: Create `Ec2QueueWorkerServiceProps` interface and `Ec2QueueWorkerService` construct |
| 104 | + |
| 105 | +The `Ec2QueueWorkerServiceProps` interface will contain properties to construct the Ec2TaskDefinition, SQSQueue and Ec2Service: |
| 106 | + |
| 107 | +```ts |
| 108 | +/** |
| 109 | + * Properties to define an ECS service |
| 110 | + */ |
| 111 | +export interface Ec2QueueWorkerServiceProps { |
| 112 | + /** |
| 113 | + * The minimum number of CPU units to reserve for the container. |
| 114 | + * |
| 115 | + * @default none |
| 116 | + */ |
| 117 | + readonly cpu?: number; |
| 118 | + |
| 119 | + /** |
| 120 | + * The hard limit (in MiB) of memory to present to the container. |
| 121 | + * |
| 122 | + * If your container attempts to exceed the allocated memory, the container |
| 123 | + * is terminated. |
| 124 | + * |
| 125 | + * At least one of memoryLimitMiB and memoryReservationMiB is required for non-Fargate services. |
| 126 | + */ |
| 127 | + readonly memoryLimitMiB?: number; |
| 128 | + |
| 129 | + /** |
| 130 | + * The soft limit (in MiB) of memory to reserve for the container. |
| 131 | + * |
| 132 | + * When system memory is under contention, Docker attempts to keep the |
| 133 | + * container memory within the limit. If the container requires more memory, |
| 134 | + * it can consume up to the value specified by the Memory property or all of |
| 135 | + * the available memory on the container instance—whichever comes first. |
| 136 | + * |
| 137 | + * At least one of memoryLimitMiB and memoryReservationMiB is required for non-Fargate services. |
| 138 | + */ |
| 139 | + readonly memoryReservationMiB?: number; |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +An example use case: |
| 144 | +```ts |
| 145 | +// Create the vpc and cluster used by the Queue Worker task |
| 146 | +const vpc = new ec2.VpcNetwork(stack, 'Vpc', { maxAZs: 1 }); |
| 147 | +const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); |
| 148 | +cluster.addCapacity('DefaultAutoScalingGroup', { |
| 149 | + instanceType: new ec2.InstanceType('t2.micro') |
| 150 | +}); |
| 151 | +const queue = new sqs.Queue(stack, 'WorkerQueue', { |
| 152 | + QueueName: 'EcsWorkerQueue' |
| 153 | +}); |
| 154 | + |
| 155 | +// Create the Queue Worker task |
| 156 | +new Ec2QueueWorkerService(stack, 'EcsQueueWorkerService', { |
| 157 | + cluster, |
| 158 | + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), |
| 159 | + desiredTaskCount: 2, |
| 160 | + maxScalingCapacity: 5, |
| 161 | + memoryReservationMiB: 512, |
| 162 | + cpu: 256, |
| 163 | + queue |
| 164 | +}); |
| 165 | +``` |
| 166 | + |
| 167 | +### Part 3: Create `FargateQueueWorkerServiceProps` interface and `FargateQueueWorkerService` construct |
| 168 | + |
| 169 | +The `FargateQueueWorkerServiceProps` interface will contain properties to construct the FargateTaskDefinition, SQSQueue and FargateService: |
| 170 | + |
| 171 | +```ts |
| 172 | +/** |
| 173 | + * Properties to define an Fargate service |
| 174 | + */ |
| 175 | +export interface FargateQueueWorkerServiceProps { |
| 176 | + /** |
| 177 | + * The number of cpu units used by the task. |
| 178 | + * Valid values, which determines your range of valid values for the memory parameter: |
| 179 | + * 256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB |
| 180 | + * 512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB |
| 181 | + * 1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB |
| 182 | + * 2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments |
| 183 | + * 4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments |
| 184 | + * |
| 185 | + * This default is set in the underlying FargateTaskDefinition construct. |
| 186 | + * |
| 187 | + * @default 256 |
| 188 | + */ |
| 189 | + readonly cpu?: string; |
| 190 | + |
| 191 | + /** |
| 192 | + * The amount (in MiB) of memory used by the task. |
| 193 | + * |
| 194 | + * This field is required and you must use one of the following values, which determines your range of valid values |
| 195 | + * for the cpu parameter: |
| 196 | + * |
| 197 | + * 0.5GB, 1GB, 2GB - Available cpu values: 256 (.25 vCPU) |
| 198 | + * |
| 199 | + * 1GB, 2GB, 3GB, 4GB - Available cpu values: 512 (.5 vCPU) |
| 200 | + * |
| 201 | + * 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB - Available cpu values: 1024 (1 vCPU) |
| 202 | + * |
| 203 | + * Between 4GB and 16GB in 1GB increments - Available cpu values: 2048 (2 vCPU) |
| 204 | + * |
| 205 | + * Between 8GB and 30GB in 1GB increments - Available cpu values: 4096 (4 vCPU) |
| 206 | + * |
| 207 | + * This default is set in the underlying FargateTaskDefinition construct. |
| 208 | + * |
| 209 | + * @default 512 |
| 210 | + */ |
| 211 | + readonly memoryMiB?: string; |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +An example use case: |
| 216 | +```ts |
| 217 | +// Create the vpc and cluster used by the Queue Worker task |
| 218 | +const vpc = new ec2.VpcNetwork(stack, 'Vpc', { maxAZs: 2 }); |
| 219 | +const cluster = new ecs.Cluster(stack, 'FargateCluster', { vpc }); |
| 220 | +const queue = new sqs.Queue(stack, 'WorkerQueue', { |
| 221 | + QueueName: 'FargateWorkerQueue' |
| 222 | +}); |
| 223 | + |
| 224 | +// Create the Queue Worker task |
| 225 | +new FargateQueueWorkerService(stack, 'FargateQueueWorkerService', { |
| 226 | + cluster, |
| 227 | + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), |
| 228 | + desiredTaskCount: 2, |
| 229 | + maxScalingCapacity: 5, |
| 230 | + queue |
| 231 | +}); |
| 232 | +``` |
0 commit comments