Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ecs-patterns): Add ECS deployment circuit breaker support to higher-level constructs #12719

Merged
merged 4 commits into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion packages/@aws-cdk/aws-ecs-patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,25 @@ const loadBalancedFargateService = new ApplicationLoadBalancedFargateService(sta
});
```

### Deployment circuit breaker and rollback

Amazon ECS [deployment circuit breaker](https://aws.amazon.com/tw/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/)
automatically rolls back unhealthy service deployments without the need for manual intervention. Use `circuitBreaker` to enable
deployment circuit breaker and optionally enable `rollback` for automatic rollback. See [Using the deployment circuit breaker](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-type-ecs.html)
for more details.

```ts
const service = new ApplicationLoadBalancedFargateService(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
desiredCount: 1,
cpu: 512,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
circuitBreaker: { rollback: true },
});
```

### Set deployment configuration on QueueProcessingService

Expand Down Expand Up @@ -469,7 +488,7 @@ const scheduledFargateTask = new ScheduledFargateTask(stack, 'ScheduledFargateTa

### Use the REMOVE_DEFAULT_DESIRED_COUNT feature flag

The REMOVE_DEFAULT_DESIRED_COUNT feature flag is used to override the default desiredCount that is autogenerated by the CDK. This will set the desiredCount of any service created by any of the following constructs to be undefined.
The REMOVE_DEFAULT_DESIRED_COUNT feature flag is used to override the default desiredCount that is autogenerated by the CDK. This will set the desiredCount of any service created by any of the following constructs to be undefined.

* ApplicationLoadBalancedEc2Service
* ApplicationLoadBalancedFargateService
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Certificate, CertificateValidation, ICertificate } from '@aws-cdk/aws-certificatemanager';
import { IVpc } from '@aws-cdk/aws-ec2';
import { AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
import {
AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
ICluster, LogDriver, PropagatedTagSource, Secret,
} from '@aws-cdk/aws-ecs';
import {
ApplicationListener, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup,
IApplicationLoadBalancer, ListenerCertificate, ListenerAction, AddApplicationTargetsProps,
Expand Down Expand Up @@ -226,6 +229,14 @@ export interface ApplicationLoadBalancedServiceBaseProps {
* @default - Rolling update (ECS)
*/
readonly deploymentController?: DeploymentController;

/**
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
* enabled.
* @default - disabled
*/
readonly circuitBreaker?: DeploymentCircuitBreaker;

}

export interface ApplicationLoadBalancedTaskImageOptions {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { IVpc } from '@aws-cdk/aws-ec2';
import { AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
import {
AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
ICluster, LogDriver, PropagatedTagSource, Secret,
} from '@aws-cdk/aws-ecs';
import { INetworkLoadBalancer, NetworkListener, NetworkLoadBalancer, NetworkTargetGroup } from '@aws-cdk/aws-elasticloadbalancingv2';
import { IRole } from '@aws-cdk/aws-iam';
import { ARecord, CnameRecord, IHostedZone, RecordTarget } from '@aws-cdk/aws-route53';
Expand Down Expand Up @@ -176,6 +179,13 @@ export interface NetworkLoadBalancedServiceBaseProps {
* @default - Rolling update (ECS)
*/
readonly deploymentController?: DeploymentController;

/**
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
* enabled.
* @default - disabled
*/
readonly circuitBreaker?: DeploymentCircuitBreaker;
}

export interface NetworkLoadBalancedTaskImageOptions {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ScalingInterval } from '@aws-cdk/aws-applicationautoscaling';
import { IVpc } from '@aws-cdk/aws-ec2';
import { AwsLogDriver, BaseService, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
import {
AwsLogDriver, BaseService, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
ICluster, LogDriver, PropagatedTagSource, Secret,
} from '@aws-cdk/aws-ecs';
import { IQueue, Queue } from '@aws-cdk/aws-sqs';
import { CfnOutput, Duration, Stack } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
Expand Down Expand Up @@ -189,6 +192,13 @@ export interface QueueProcessingServiceBaseProps {
* @default - Rolling update (ECS)
*/
readonly deploymentController?: DeploymentController;

/**
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
* enabled.
* @default - disabled
*/
readonly circuitBreaker?: DeploymentCircuitBreaker;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export class ApplicationLoadBalancedEc2Service extends ApplicationLoadBalancedSe
enableECSManagedTags: props.enableECSManagedTags,
cloudMapOptions: props.cloudMapOptions,
deploymentController: props.deploymentController,
circuitBreaker: props.circuitBreaker,
});
this.addServiceAsTarget(this.service);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class NetworkLoadBalancedEc2Service extends NetworkLoadBalancedServiceBas
enableECSManagedTags: props.enableECSManagedTags,
cloudMapOptions: props.cloudMapOptions,
deploymentController: props.deploymentController,
circuitBreaker: props.circuitBreaker,
});
this.addServiceAsTarget(this.service);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export class QueueProcessingEc2Service extends QueueProcessingServiceBase {
propagateTags: props.propagateTags,
enableECSManagedTags: props.enableECSManagedTags,
deploymentController: props.deploymentController,
circuitBreaker: props.circuitBreaker,
});

this.configureAutoscalingForService(this.service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc
cloudMapOptions: props.cloudMapOptions,
platformVersion: props.platformVersion,
deploymentController: props.deploymentController,
circuitBreaker: props.circuitBreaker,
securityGroups: props.securityGroups,
vpcSubnets: props.taskSubnets,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic
cloudMapOptions: props.cloudMapOptions,
platformVersion: props.platformVersion,
deploymentController: props.deploymentController,
circuitBreaker: props.circuitBreaker,
vpcSubnets: props.taskSubnets,
});
this.addServiceAsTarget(this.service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export class QueueProcessingFargateService extends QueueProcessingServiceBase {
securityGroups: props.securityGroups,
vpcSubnets: props.taskSubnets,
assignPublicIp: props.assignPublicIp,
circuitBreaker: props.circuitBreaker,
});

this.configureAutoscalingForService(this.service);
Expand Down
66 changes: 66 additions & 0 deletions packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,72 @@ export = {
test.done();
},

'ALB with circuit breaker'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') });

// WHEN
new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
},
circuitBreaker: { rollback: true },
});

// THEN
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
DeploymentConfiguration: {
DeploymentCircuitBreaker: {
Enable: true,
Rollback: true,
},
},
DeploymentController: {
Type: 'ECS',
},
}));

test.done();
},

'NLB with circuit breaker'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') });

// WHEN
new ecsPatterns.NetworkLoadBalancedEc2Service(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
},
circuitBreaker: { rollback: true },
});

// THEN
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
DeploymentConfiguration: {
DeploymentCircuitBreaker: {
Enable: true,
Rollback: true,
},
},
DeploymentController: {
Type: 'ECS',
},
}));

test.done();
},

'NetworkLoadbalancedEC2Service accepts previously created load balancer'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,7 @@ export = {
maxHealthyPercent: 150,
serviceName: 'ecs-test-service',
family: 'ecs-task-family',
deploymentController: {
type: ecs.DeploymentControllerType.CODE_DEPLOY,
},
circuitBreaker: { rollback: true },
});

// THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all optional properties are set.
Expand All @@ -220,11 +218,15 @@ export = {
DeploymentConfiguration: {
MinimumHealthyPercent: 60,
MaximumPercent: 150,
DeploymentCircuitBreaker: {
Enable: true,
Rollback: true,
},
},
LaunchType: 'EC2',
ServiceName: 'ecs-test-service',
DeploymentController: {
Type: 'CODE_DEPLOY',
Type: 'ECS',
},
}));

Expand Down
Loading