Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FargatePlatformVersion, FargateTaskDefinition, RuntimePlatform } from '../../../aws-ecs';
import type { DeploymentCircuitBreaker, FargatePlatformVersion, FargateTaskDefinition, RuntimePlatform } from '../../../aws-ecs';

export interface FargateServiceBaseProps {
/**
Expand Down Expand Up @@ -89,4 +89,11 @@ export interface FargateServiceBaseProps {
* @default - If the property is undefined, `operatingSystemFamily` is LINUX and `cpuArchitecture` is X86_64
*/
readonly runtimePlatform?: RuntimePlatform;

/**
* Whether to enable the deployment circuit breaker. If this property is defined, circuit breaker will be implicitly
* enabled.
* @default - disabled
*/
readonly circuitBreaker?: DeploymentCircuitBreaker;
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export class NetworkMultipleTargetGroupsFargateService extends NetworkMultipleTa
enableExecuteCommand: props.enableExecuteCommand,
minHealthyPercent: props.minHealthyPercent,
maxHealthyPercent: props.maxHealthyPercent,
circuitBreaker: props.circuitBreaker,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('NetworkMultipleTargetGroupsFargateService', () => {
image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
},
minHealthyPercent: 100,
circuitBreaker: {}, // Set to silence an unrelated warning
});

// THEN - no warning about minHealthyPercent
Expand Down
18 changes: 11 additions & 7 deletions packages/aws-cdk-lib/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
// Add capacity to it
cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
instanceType: new ec2.InstanceType("t2.xlarge"),
desiredCapacity: 3,
});

const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
Expand All @@ -36,6 +35,9 @@ const ecsService = new ecs.Ec2Service(this, 'Service', {
cluster,
taskDefinition,
minHealthyPercent: 100,
circuitBreaker: {
enable: true,
},
});
```

Expand Down Expand Up @@ -127,7 +129,6 @@ const cluster = new ecs.Cluster(this, 'Cluster', {
// Either add default capacity
cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
instanceType: new ec2.InstanceType("t2.xlarge"),
desiredCapacity: 3,
});

// Or add customized capacity. Be sure to start the Amazon ECS-optimized AMI.
Expand Down Expand Up @@ -360,7 +361,7 @@ cluster.addCapacity('ASGEncryptedSNS', {

### Container Insights

On a cluster, CloudWatch Container Insights can be enabled by setting the `containerInsightsV2` property. [Container Insights](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-container-insights.html)
On a cluster, CloudWatch Container Insights can be enabled by setting the `containerInsightsV2` property. [Container Insights](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-container-insights.html)
can be disabled, enabled, or enhanced.

```ts
Expand Down Expand Up @@ -807,6 +808,9 @@ const service = new ecs.FargateService(this, 'Service', {
taskDefinition,
desiredCount: 5,
minHealthyPercent: 100,
circuitBreaker: {
enable: true,
},
});
```

Expand Down Expand Up @@ -1683,7 +1687,7 @@ Capacity Option Type provides the purchasing option for the EC2 instances used i
See [ECS documentation for Managed Instances Capacity Provider](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/managed-instances-capacity-providers-concept.html) for more documentation.

#### IAM Roles Setup
Managed instances require an infrastructure and an EC2 instance profile. You can either provide your own infrastructure role and/or instance profile, or let the construct create them automatically.
Managed instances require an infrastructure and an EC2 instance profile. You can either provide your own infrastructure role and/or instance profile, or let the construct create them automatically.

Option 1: Let CDK create the role and instance profile automatically
```ts
Expand Down Expand Up @@ -1850,16 +1854,16 @@ const miCapacityProvider = new ecs.ManagedInstancesCapacityProvider(this, 'MICap
acceleratorManufacturers: [ec2.AcceleratorManufacturer.NVIDIA],
acceleratorNames: [ec2.AcceleratorName.T4, ec2.AcceleratorName.V100],
acceleratorCountMin: 1,

// Storage requirements
localStorage: ec2.LocalStorage.REQUIRED,
localStorageTypes: [ec2.LocalStorageType.SSD],
totalLocalStorageGBMin: 100,

// Network requirements
networkInterfaceCountMin: 2,
networkBandwidthGbpsMin: 10,

// Cost optimization
onDemandMaxPricePercentageOverLowestPrice: 10,
},
Expand Down
5 changes: 5 additions & 0 deletions packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,11 @@ export abstract class BaseService extends Resource
Annotations.of(this)._addTrackableError(lit`CircuitBreakerRequiresEcsController`, 'Deployment circuit breaker requires the ECS deployment controller.');
}

if (!props.circuitBreaker && this.isEcsDeploymentController) {
// If we *could* use a circuit breaker, then let's recommend users to do so. It makes detecting errors sooo much faster.
Annotations.of(this).addWarningV2('@aws-cdk/aws-ecs:shouldUseCircuitBreaker', 'Enable the \'circuitBreaker\' property to trigger a quicker deployment failure if tasks are failing to come start (without this setting deployments may take up to 3 hours to fail).');
}

if (props.deploymentAlarms && !this.isEcsDeploymentController) {
throw new ValidationError(lit`RequiresDeploymentAlarmsRequires`, 'Deployment alarms requires the ECS deployment controller.', this);
}
Expand Down
31 changes: 31 additions & 0 deletions packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as s3 from '../../../aws-s3';
import * as cloudmap from '../../../aws-servicediscovery';
import * as cdk from '../../../core';
import { App } from '../../../core';
import { flattenMeta } from '../../../core/test/util';
import { ECS_ARN_FORMAT_INCLUDES_CLUSTER_NAME } from '../../../cx-api';
import * as ecs from '../../lib';
import {
Expand Down Expand Up @@ -63,6 +64,36 @@ describe('ec2 service', () => {
expect(service.node.defaultChild).toBeDefined();
});

test.each([false, true])('suggests using circuitBreaker if %p set', (circuitBreakerSet) => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Stack');
const vpc = new ec2.Vpc(stack, 'MyVpc', {});
const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
addDefaultCapacityProvider(cluster, stack, vpc);
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef');

taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
memoryLimitMiB: 512,
});

new ecs.Ec2Service(stack, 'Ec2Service', {
cluster,
taskDefinition,
circuitBreaker: circuitBreakerSet ? { } : undefined,
});

// THEN
const warnings = flattenMeta(app.synth().getStackByName('Stack').metadata)['/Stack/Ec2Service']['aws:cdk:warning'];

if (circuitBreakerSet) {
expect(warnings).not.toContainEqual(expect.stringContaining('Enable the \'circuitBreaker\' property'));
} else {
expect(warnings).toContainEqual(expect.stringContaining('Enable the \'circuitBreaker\' property'));
}
});

[false, undefined].forEach((value) => {
test('set cloudwatch permissions based on falsy feature flag when no cloudwatch log configured', () => {
// GIVEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3646,6 +3646,7 @@ describe('fargate service', () => {
cluster,
taskDefinition,
minHealthyPercent: 50, // must be set to avoid warning causing test failure
circuitBreaker: {}, // Set to silence an unrelated warning
});

// WHEN
Expand Down
Loading