Skip to content

Commit

Permalink
feat(ecs): add automated spot instance draining support (aws#4360)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
pahud authored and mergify[bot] committed Oct 9, 2019
1 parent 4284aa2 commit 9c208d0
Show file tree
Hide file tree
Showing 5 changed files with 1,444 additions and 40 deletions.
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,23 @@ cluster.addAutoScalingGroup(autoScalingGroup);

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

### Spot Instances

To add spot instances into the cluster, you must specify the `spotPrice` in the `ecs.AddCapacityOptions` and optionally enable the `spotInstanceDraining` property.

```ts
// Add an AutoScalingGroup with spot instances to the existing cluster
cluster.addCapacity('AsgSpot', {
maxCapacity: 2,
minCapacity: 2,
desiredCapacity: 2,
instanceType: new ec2.InstanceType('c5.xlarge'),
spotPrice: '0.0735',
// Enable the Automated Spot Draining support for Amazon ECS
spotInstanceDraining: true,
});
```

## Task definitions

A task Definition describes what a single copy of a **task** should look like.
Expand Down
52 changes: 32 additions & 20 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import autoscaling = require('@aws-cdk/aws-autoscaling');
import cloudwatch = require ('@aws-cdk/aws-cloudwatch');
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import iam = require('@aws-cdk/aws-iam');
import cloudmap = require('@aws-cdk/aws-servicediscovery');
import ssm = require('@aws-cdk/aws-ssm');
import {Construct, Duration, IResource, Resource, Stack} from '@aws-cdk/core';
import {InstanceDrainHook} from './drain-hook/instance-drain-hook';
import {CfnCluster} from './ecs.generated';
import { Construct, Duration, IResource, Resource, Stack } from '@aws-cdk/core';
import { InstanceDrainHook } from './drain-hook/instance-drain-hook';
import { CfnCluster } from './ecs.generated';

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

if (autoScalingGroup.spotPrice && options.spotInstanceDraining) {
autoScalingGroup.addUserData('echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config');
}

// ECS instances must be able to do these things
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
autoScalingGroup.addToRolePolicy(new iam.PolicyStatement({
Expand Down Expand Up @@ -252,7 +256,7 @@ export class Cluster extends Resource implements ICluster {
* @default average over 5 minutes
*/
public metricMemoryReservation(props?: cloudwatch.MetricOptions): cloudwatch.Metric {
return this.metric('MemoryReservation', props );
return this.metric('MemoryReservation', props);
}

/**
Expand Down Expand Up @@ -350,12 +354,12 @@ export class EcsOptimizedAmi implements ec2.IMachineImage {

// set the SSM parameter name
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
+ "recommended/image_id";
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "")
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "")
+ (this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "")
+ (this.hwType === AmiHardwareType.GPU ? "gpu/" : "")
+ (this.hwType === AmiHardwareType.ARM ? "arm64/" : "")
+ "recommended/image_id";
}

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

/**
* Construct an Amazon Linux AMI image from the latest ECS Optimized AMI published in SSM
*/
public static amazonLinux(): EcsOptimizedImage {
return new EcsOptimizedImage({generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX});
return new EcsOptimizedImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX });
}

/**
Expand All @@ -396,7 +400,7 @@ export class EcsOptimizedImage implements ec2.IMachineImage {
* @param windowsVersion Windows Version to use
*/
public static windows(windowsVersion: WindowsOptimizedVersion): EcsOptimizedImage {
return new EcsOptimizedImage({windowsVersion});
return new EcsOptimizedImage({ windowsVersion });
}

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

// set the SSM parameter name
this.amiParameterName = "/aws/service/ecs/optimized-ami/"
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "" )
+ ( this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "" )
+ ( this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "" )
+ ( this.hwType === AmiHardwareType.GPU ? "gpu/" : "" )
+ ( this.hwType === AmiHardwareType.ARM ? "arm64/" : "" )
+ "recommended/image_id";
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX ? "amazon-linux/" : "")
+ (this.generation === ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 ? "amazon-linux-2/" : "")
+ (this.windowsVersion ? `windows_server/${this.windowsVersion}/english/full/` : "")
+ (this.hwType === AmiHardwareType.GPU ? "gpu/" : "")
+ (this.hwType === AmiHardwareType.ARM ? "arm64/" : "")
+ "recommended/image_id";
}

/**
Expand Down Expand Up @@ -614,6 +618,14 @@ export interface AddAutoScalingGroupCapacityOptions {
* @default Duration.minutes(5)
*/
readonly taskDrainTime?: Duration;

/**
* Specify whether to enable Automated Draining for Spot Instances running Amazon ECS Services.
* For more information, see [Using Spot Instances](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-instance-spot.html).
*
* @default false
*/
readonly spotInstanceDraining?: boolean
}

/**
Expand Down
Loading

0 comments on commit 9c208d0

Please sign in to comment.