Skip to content

Commit a109984

Browse files
author
Abdul Kader Maliyakkal
committed
Add ECS deployment circuit breaker support to higher-level constructs
1 parent cbed348 commit a109984

14 files changed

+191
-4
lines changed

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

+23-1
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,28 @@ const loadBalancedFargateService = new ApplicationLoadBalancedFargateService(sta
394394
});
395395
```
396396

397+
### Deployment circuit breaker and rollback
398+
399+
Amazon ECS [deployment circuit breaker](https://aws.amazon.com/tw/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/)
400+
automatically rolls back unhealthy service deployments without the need for manual intervention. Use `circuitBreaker` to enable
401+
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)
402+
for more details.
403+
404+
```ts
405+
const service = new ApplicationLoadBalancedFargateService(stack, 'Service', {
406+
cluster,
407+
memoryLimitMiB: 1024,
408+
desiredCount: 1,
409+
cpu: 512,
410+
taskImageOptions: {
411+
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
412+
},
413+
deploymentController: {
414+
type: ecs.DeploymentControllerType.ECS,
415+
},
416+
circuitBreaker: { rollback: true },
417+
});
418+
```
397419

398420
### Set deployment configuration on QueueProcessingService
399421

@@ -469,7 +491,7 @@ const scheduledFargateTask = new ScheduledFargateTask(stack, 'ScheduledFargateTa
469491

470492
### Use the REMOVE_DEFAULT_DESIRED_COUNT feature flag
471493

472-
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.
494+
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.
473495

474496
* ApplicationLoadBalancedEc2Service
475497
* ApplicationLoadBalancedFargateService

packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Certificate, CertificateValidation, ICertificate } from '@aws-cdk/aws-certificatemanager';
22
import { IVpc } from '@aws-cdk/aws-ec2';
3-
import { AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
3+
import {
4+
AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
5+
ICluster, LogDriver, PropagatedTagSource, Secret,
6+
} from '@aws-cdk/aws-ecs';
47
import {
58
ApplicationListener, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup,
69
IApplicationLoadBalancer, ListenerCertificate, ListenerAction, AddApplicationTargetsProps,
@@ -226,6 +229,14 @@ export interface ApplicationLoadBalancedServiceBaseProps {
226229
* @default - Rolling update (ECS)
227230
*/
228231
readonly deploymentController?: DeploymentController;
232+
233+
/**
234+
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
235+
* enabled.
236+
* @default - disabled
237+
*/
238+
readonly circuitBreaker?: DeploymentCircuitBreaker;
239+
229240
}
230241

231242
export interface ApplicationLoadBalancedTaskImageOptions {

packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { IVpc } from '@aws-cdk/aws-ec2';
2-
import { AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
2+
import {
3+
AwsLogDriver, BaseService, CloudMapOptions, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
4+
ICluster, LogDriver, PropagatedTagSource, Secret,
5+
} from '@aws-cdk/aws-ecs';
36
import { INetworkLoadBalancer, NetworkListener, NetworkLoadBalancer, NetworkTargetGroup } from '@aws-cdk/aws-elasticloadbalancingv2';
47
import { IRole } from '@aws-cdk/aws-iam';
58
import { ARecord, CnameRecord, IHostedZone, RecordTarget } from '@aws-cdk/aws-route53';
@@ -176,6 +179,13 @@ export interface NetworkLoadBalancedServiceBaseProps {
176179
* @default - Rolling update (ECS)
177180
*/
178181
readonly deploymentController?: DeploymentController;
182+
183+
/**
184+
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
185+
* enabled.
186+
* @default - disabled
187+
*/
188+
readonly circuitBreaker?: DeploymentCircuitBreaker;
179189
}
180190

181191
export interface NetworkLoadBalancedTaskImageOptions {

packages/@aws-cdk/aws-ecs-patterns/lib/base/queue-processing-service-base.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { ScalingInterval } from '@aws-cdk/aws-applicationautoscaling';
22
import { IVpc } from '@aws-cdk/aws-ec2';
3-
import { AwsLogDriver, BaseService, Cluster, ContainerImage, DeploymentController, ICluster, LogDriver, PropagatedTagSource, Secret } from '@aws-cdk/aws-ecs';
3+
import {
4+
AwsLogDriver, BaseService, Cluster, ContainerImage, DeploymentController, DeploymentCircuitBreaker,
5+
ICluster, LogDriver, PropagatedTagSource, Secret,
6+
} from '@aws-cdk/aws-ecs';
47
import { IQueue, Queue } from '@aws-cdk/aws-sqs';
58
import { CfnOutput, Duration, Stack } from '@aws-cdk/core';
69
import * as cxapi from '@aws-cdk/cx-api';
@@ -189,6 +192,13 @@ export interface QueueProcessingServiceBaseProps {
189192
* @default - Rolling update (ECS)
190193
*/
191194
readonly deploymentController?: DeploymentController;
195+
196+
/**
197+
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
198+
* enabled.
199+
* @default - disabled
200+
*/
201+
readonly circuitBreaker?: DeploymentCircuitBreaker;
192202
}
193203

194204
/**

packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export class ApplicationLoadBalancedEc2Service extends ApplicationLoadBalancedSe
133133
enableECSManagedTags: props.enableECSManagedTags,
134134
cloudMapOptions: props.cloudMapOptions,
135135
deploymentController: props.deploymentController,
136+
circuitBreaker: props.circuitBreaker,
136137
});
137138
this.addServiceAsTarget(this.service);
138139
}

packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export class NetworkLoadBalancedEc2Service extends NetworkLoadBalancedServiceBas
131131
enableECSManagedTags: props.enableECSManagedTags,
132132
cloudMapOptions: props.cloudMapOptions,
133133
deploymentController: props.deploymentController,
134+
circuitBreaker: props.circuitBreaker,
134135
});
135136
this.addServiceAsTarget(this.service);
136137
}

packages/@aws-cdk/aws-ecs-patterns/lib/ecs/queue-processing-ecs-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export class QueueProcessingEc2Service extends QueueProcessingServiceBase {
114114
propagateTags: props.propagateTags,
115115
enableECSManagedTags: props.enableECSManagedTags,
116116
deploymentController: props.deploymentController,
117+
circuitBreaker: props.circuitBreaker,
117118
});
118119

119120
this.configureAutoscalingForService(this.service);

packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc
170170
cloudMapOptions: props.cloudMapOptions,
171171
platformVersion: props.platformVersion,
172172
deploymentController: props.deploymentController,
173+
circuitBreaker: props.circuitBreaker,
173174
securityGroups: props.securityGroups,
174175
vpcSubnets: props.taskSubnets,
175176
});

packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic
157157
cloudMapOptions: props.cloudMapOptions,
158158
platformVersion: props.platformVersion,
159159
deploymentController: props.deploymentController,
160+
circuitBreaker: props.circuitBreaker,
160161
vpcSubnets: props.taskSubnets,
161162
});
162163
this.addServiceAsTarget(this.service);

packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export class QueueProcessingFargateService extends QueueProcessingServiceBase {
148148
securityGroups: props.securityGroups,
149149
vpcSubnets: props.taskSubnets,
150150
assignPublicIp: props.assignPublicIp,
151+
circuitBreaker: props.circuitBreaker,
151152
});
152153

153154
this.configureAutoscalingForService(this.service);

packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts

+66
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,72 @@ export = {
10981098
test.done();
10991099
},
11001100

1101+
'ALB with circuit breaker'(test: Test) {
1102+
// GIVEN
1103+
const stack = new cdk.Stack();
1104+
const vpc = new ec2.Vpc(stack, 'VPC');
1105+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
1106+
cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') });
1107+
1108+
// WHEN
1109+
new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', {
1110+
cluster,
1111+
memoryLimitMiB: 1024,
1112+
taskImageOptions: {
1113+
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
1114+
},
1115+
deploymentController: {
1116+
type: ecs.DeploymentControllerType.ECS,
1117+
},
1118+
circuitBreaker: { rollback: true },
1119+
});
1120+
1121+
// THEN
1122+
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
1123+
DeploymentConfiguration: {
1124+
DeploymentCircuitBreaker: {
1125+
Enable: true,
1126+
Rollback: true,
1127+
},
1128+
},
1129+
}));
1130+
1131+
test.done();
1132+
},
1133+
1134+
'NLB with circuit breaker'(test: Test) {
1135+
// GIVEN
1136+
const stack = new cdk.Stack();
1137+
const vpc = new ec2.Vpc(stack, 'VPC');
1138+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
1139+
cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') });
1140+
1141+
// WHEN
1142+
new ecsPatterns.NetworkLoadBalancedEc2Service(stack, 'Service', {
1143+
cluster,
1144+
memoryLimitMiB: 1024,
1145+
taskImageOptions: {
1146+
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
1147+
},
1148+
deploymentController: {
1149+
type: ecs.DeploymentControllerType.ECS,
1150+
},
1151+
circuitBreaker: { rollback: true },
1152+
});
1153+
1154+
// THEN
1155+
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
1156+
DeploymentConfiguration: {
1157+
DeploymentCircuitBreaker: {
1158+
Enable: true,
1159+
Rollback: true,
1160+
},
1161+
},
1162+
}));
1163+
1164+
test.done();
1165+
},
1166+
11011167
'NetworkLoadbalancedEC2Service accepts previously created load balancer'(test: Test) {
11021168
// GIVEN
11031169
const stack = new cdk.Stack();

packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.queue-processing-ecs-service.ts

+5
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export = {
212212
deploymentController: {
213213
type: ecs.DeploymentControllerType.CODE_DEPLOY,
214214
},
215+
circuitBreaker: { rollback: true },
215216
});
216217

217218
// THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all optional properties are set.
@@ -220,6 +221,10 @@ export = {
220221
DeploymentConfiguration: {
221222
MinimumHealthyPercent: 60,
222223
MaximumPercent: 150,
224+
DeploymentCircuitBreaker: {
225+
Enable: true,
226+
Rollback: true,
227+
},
223228
},
224229
LaunchType: 'EC2',
225230
ServiceName: 'ecs-test-service',

packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts

+52
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,58 @@ export = {
427427
test.done();
428428
},
429429

430+
'setting ALB circuitBreaker works'(test: Test) {
431+
// GIVEN
432+
const stack = new cdk.Stack();
433+
434+
// WHEN
435+
new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', {
436+
taskImageOptions: {
437+
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
438+
},
439+
deploymentController: {
440+
type: ecs.DeploymentControllerType.ECS,
441+
},
442+
circuitBreaker: { rollback: true },
443+
});
444+
// THEN
445+
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
446+
DeploymentConfiguration: {
447+
DeploymentCircuitBreaker: {
448+
Enable: true,
449+
Rollback: true,
450+
},
451+
},
452+
}));
453+
test.done();
454+
},
455+
456+
'setting NLB circuitBreaker works'(test: Test) {
457+
// GIVEN
458+
const stack = new cdk.Stack();
459+
460+
// WHEN
461+
new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', {
462+
taskImageOptions: {
463+
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
464+
},
465+
deploymentController: {
466+
type: ecs.DeploymentControllerType.ECS,
467+
},
468+
circuitBreaker: { rollback: true },
469+
});
470+
// THEN
471+
expect(stack).to(haveResourceLike('AWS::ECS::Service', {
472+
DeploymentConfiguration: {
473+
DeploymentCircuitBreaker: {
474+
Enable: true,
475+
Rollback: true,
476+
},
477+
},
478+
}));
479+
test.done();
480+
},
481+
430482
'setting NLB special listener port to create the listener'(test: Test) {
431483
// GIVEN
432484
const stack = new cdk.Stack();

packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts

+5
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ export = {
347347
deploymentController: {
348348
type: ecs.DeploymentControllerType.CODE_DEPLOY,
349349
},
350+
circuitBreaker: { rollback: true },
350351
});
351352

352353
// THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all optional properties are set.
@@ -355,6 +356,10 @@ export = {
355356
DeploymentConfiguration: {
356357
MinimumHealthyPercent: 60,
357358
MaximumPercent: 150,
359+
DeploymentCircuitBreaker: {
360+
Enable: true,
361+
Rollback: true,
362+
},
358363
},
359364
LaunchType: 'FARGATE',
360365
ServiceName: 'fargate-test-service',

0 commit comments

Comments
 (0)