Skip to content

Commit 9c208d0

Browse files
pahudmergify[bot]
authored andcommitted
feat(ecs): add automated spot instance draining support (#4360)
* feat(aws-ecs): add automated spot instance draining support * use @see in comments * 2 spaces indentation and simplify the integ content * update aws-ecs/README.md about Spot Instances. * fix typo * * update README * add unit test * remove redundant descriptions * fix trailing whitespace * - verify ASG has spot capacity only
1 parent 4284aa2 commit 9c208d0

File tree

5 files changed

+1444
-40
lines changed

5 files changed

+1444
-40
lines changed

packages/@aws-cdk/aws-ecs/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,23 @@ cluster.addAutoScalingGroup(autoScalingGroup);
123123

124124
If you omit the property `vpc`, the construct will create a new VPC with two AZs.
125125

126+
### Spot Instances
127+
128+
To add spot instances into the cluster, you must specify the `spotPrice` in the `ecs.AddCapacityOptions` and optionally enable the `spotInstanceDraining` property.
129+
130+
```ts
131+
// Add an AutoScalingGroup with spot instances to the existing cluster
132+
cluster.addCapacity('AsgSpot', {
133+
maxCapacity: 2,
134+
minCapacity: 2,
135+
desiredCapacity: 2,
136+
instanceType: new ec2.InstanceType('c5.xlarge'),
137+
spotPrice: '0.0735',
138+
// Enable the Automated Spot Draining support for Amazon ECS
139+
spotInstanceDraining: true,
140+
});
141+
```
142+
126143
## Task definitions
127144

128145
A task Definition describes what a single copy of a **task** should look like.

packages/@aws-cdk/aws-ecs/lib/cluster.ts

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import autoscaling = require('@aws-cdk/aws-autoscaling');
2-
import cloudwatch = require ('@aws-cdk/aws-cloudwatch');
2+
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
33
import ec2 = require('@aws-cdk/aws-ec2');
44
import iam = require('@aws-cdk/aws-iam');
55
import cloudmap = require('@aws-cdk/aws-servicediscovery');
66
import ssm = require('@aws-cdk/aws-ssm');
7-
import {Construct, Duration, IResource, Resource, Stack} from '@aws-cdk/core';
8-
import {InstanceDrainHook} from './drain-hook/instance-drain-hook';
9-
import {CfnCluster} from './ecs.generated';
7+
import { Construct, Duration, IResource, Resource, Stack } from '@aws-cdk/core';
8+
import { InstanceDrainHook } from './drain-hook/instance-drain-hook';
9+
import { CfnCluster } from './ecs.generated';
1010

1111
/**
1212
* The properties used to define an ECS cluster.
@@ -195,6 +195,10 @@ export class Cluster extends Resource implements ICluster {
195195
autoScalingGroup.addUserData('echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config');
196196
}
197197

198+
if (autoScalingGroup.spotPrice && options.spotInstanceDraining) {
199+
autoScalingGroup.addUserData('echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config');
200+
}
201+
198202
// ECS instances must be able to do these things
199203
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
200204
autoScalingGroup.addToRolePolicy(new iam.PolicyStatement({
@@ -252,7 +256,7 @@ export class Cluster extends Resource implements ICluster {
252256
* @default average over 5 minutes
253257
*/
254258
public metricMemoryReservation(props?: cloudwatch.MetricOptions): cloudwatch.Metric {
255-
return this.metric('MemoryReservation', props );
259+
return this.metric('MemoryReservation', props);
256260
}
257261

258262
/**
@@ -350,12 +354,12 @@ export class EcsOptimizedAmi implements ec2.IMachineImage {
350354

351355
// set the SSM parameter name
352356
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
353-
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
354-
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
355-
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
356-
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
357-
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
358-
+ "recommended/image_id";
357+
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "")
358+
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "")
359+
+ (this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "")
360+
+ (this.hwType === AmiHardwareType.GPU ? "gpu/" : "")
361+
+ (this.hwType === AmiHardwareType.ARM ? "arm64/" : "")
362+
+ "recommended/image_id";
359363
}
360364

361365
/**
@@ -380,14 +384,14 @@ export class EcsOptimizedImage implements ec2.IMachineImage {
380384
* @param hardwareType ECS-optimized AMI variant to use
381385
*/
382386
public static amazonLinux2(hardwareType = AmiHardwareType.STANDARD): EcsOptimizedImage {
383-
return new EcsOptimizedImage({generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, hardwareType});
387+
return new EcsOptimizedImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, hardwareType });
384388
}
385389

386390
/**
387391
* Construct an Amazon Linux AMI image from the latest ECS Optimized AMI published in SSM
388392
*/
389393
public static amazonLinux(): EcsOptimizedImage {
390-
return new EcsOptimizedImage({generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX});
394+
return new EcsOptimizedImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX });
391395
}
392396

393397
/**
@@ -396,7 +400,7 @@ export class EcsOptimizedImage implements ec2.IMachineImage {
396400
* @param windowsVersion Windows Version to use
397401
*/
398402
public static windows(windowsVersion: WindowsOptimizedVersion): EcsOptimizedImage {
399-
return new EcsOptimizedImage({windowsVersion});
403+
return new EcsOptimizedImage({ windowsVersion });
400404
}
401405

402406
private readonly generation?: ec2.AmazonLinuxGeneration;
@@ -421,12 +425,12 @@ export class EcsOptimizedImage implements ec2.IMachineImage {
421425

422426
// set the SSM parameter name
423427
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
424-
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
425-
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
426-
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
427-
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
428-
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
429-
+ "recommended/image_id";
428+
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "")
429+
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "")
430+
+ (this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "")
431+
+ (this.hwType === AmiHardwareType.GPU ? "gpu/" : "")
432+
+ (this.hwType === AmiHardwareType.ARM ? "arm64/" : "")
433+
+ "recommended/image_id";
430434
}
431435

432436
/**
@@ -614,6 +618,14 @@ export interface AddAutoScalingGroupCapacityOptions {
614618
* @default Duration.minutes(5)
615619
*/
616620
readonly taskDrainTime?: Duration;
621+
622+
/**
623+
* Specify whether to enable Automated Draining for Spot Instances running Amazon ECS Services.
624+
* For more information, see [Using Spot Instances](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-instance-spot.html).
625+
*
626+
* @default false
627+
*/
628+
readonly spotInstanceDraining?: boolean
617629
}
618630

619631
/**

0 commit comments

Comments
 (0)