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

fix(ecs): separate application and network load balanced services #3719

Merged
merged 21 commits into from
Sep 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
78d76a9
(aws-ecs-patterns): separate out logic for Application and Network Lo…
piradeepk Aug 19, 2019
efb205d
Update README
piradeepk Aug 20, 2019
d8f9132
Remove certificate from Network Load Balanced Service
piradeepk Aug 20, 2019
69350f2
Add TaskDef as a property on the construct
piradeepk Aug 20, 2019
c60535c
Only allow assign public ip on fargate services
piradeepk Aug 20, 2019
4049cc9
Update documentation
piradeepk Aug 21, 2019
5db8d56
Merge branch 'master' into separateLBServices
piradeepk Aug 22, 2019
3d9aabf
Merge branch 'master' into separateLBServices
piradeepk Aug 22, 2019
3d89e4c
Add to allowed-breaking-changes doc
piradeepk Aug 22, 2019
c0e5cce
Merge branch 'master' into separateLBServices
piradeepk Aug 22, 2019
eac611c
Merge branch 'master' into separateLBServices
piradeepk Aug 23, 2019
70407cb
Merge branch 'master' into separateLBServices
piradeepk Aug 23, 2019
fde8c0f
Merge branch 'master' into separateLBServices
piradeepk Aug 27, 2019
f9a1d8a
Merge branch 'master' into separateLBServices
piradeepk Aug 27, 2019
835331e
Merge branch 'master' into separateLBServices
piradeepk Aug 28, 2019
02ee853
Merge branch 'master' into separateLBServices
piradeepk Sep 3, 2019
b605145
Merge branch 'master' into separateLBServices
piradeepk Sep 4, 2019
6b016f2
Merge branch 'master' into separateLBServices
piradeepk Sep 5, 2019
d38e68c
Merge branch 'master' into separateLBServices
piradeepk Sep 5, 2019
543c2b6
Merge branch 'master' into separateLBServices
piradeepk Sep 5, 2019
358a789
Merge branch 'master' into separateLBServices
piradeepk Sep 6, 2019
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
51 changes: 43 additions & 8 deletions packages/@aws-cdk/aws-ecs-patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@

This library provides higher-level Amazon ECS constructs which follow common architectural patterns. It contains:

* Load Balanced Services
* Application Load Balanced Services
* Network Load Balanced Services
* Queue Processing Services
* Scheduled Tasks (cron jobs)

## Load Balanced Services
## Application Load Balanced Services

To define an Amazon ECS service that is behind a load balancer, instantiate one of the following:
To define an Amazon ECS service that is behind an application load balancer, instantiate one of the following:

* `LoadBalancedEc2Service`
* `ApplicationLoadBalancedEc2Service`

```ts
const loadBalancedEcsService = new ecsPatterns.LoadBalancedEc2Service(stack, 'Service', {
const loadBalancedEcsService = new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
image: ecs.ContainerImage.fromRegistry('test'),
Expand All @@ -40,10 +41,45 @@ const loadBalancedEcsService = new ecsPatterns.LoadBalancedEc2Service(stack, 'Se
});
```

* `LoadBalancedFargateService`
* `ApplicationLoadBalancedFargateService`

```ts
const loadBalancedFargateService = new ecsPatterns.LoadBalancedFargateService(stack, 'Service', {
const loadBalancedFargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
cpu: 512,
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
});
```

Instead of providing a cluster you can specify a VPC and CDK will create a new ECS cluster.
If you deploy multiple services CDK will only create on cluster per VPC.

You can omit `cluster` and `vpc` to let CDK create a new VPC with two AZs and create a cluster inside this VPC.

## Network Load Balanced Services

To define an Amazon ECS service that is behind a network load balancer, instantiate one of the following:

* `NetworkLoadBalancedEc2Service`

```ts
const loadBalancedEcsService = new ecsPatterns.NetworkLoadBalancedEc2Service(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
image: ecs.ContainerImage.fromRegistry('test'),
desiredCount: 2,
environment: {
TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value",
TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value"
}
});
```

* `NetworkLoadBalancedFargateService`

```ts
const loadBalancedFargateService = new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', {
cluster,
memoryLimitMiB: 1024,
cpu: 512,
Expand Down Expand Up @@ -101,7 +137,6 @@ const queueProcessingFargateService = new QueueProcessingFargateService(stack, '

To define a task that runs periodically, instantiate an `ScheduledEc2Task`:


```ts
// Instantiate an Amazon EC2 Task to run at a scheduled interval
const ecsScheduledTask = new ScheduledEc2Task(this, 'ScheduledTask', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { ICertificate } from '@aws-cdk/aws-certificatemanager';
import { IVpc } from '@aws-cdk/aws-ec2';
import { AwsLogDriver, BaseService, Cluster, ContainerImage, ICluster, LogDriver, Secret } from '@aws-cdk/aws-ecs';
import { ApplicationListener, ApplicationLoadBalancer, ApplicationTargetGroup, BaseLoadBalancer, NetworkListener,
NetworkLoadBalancer, NetworkTargetGroup } from '@aws-cdk/aws-elasticloadbalancingv2';
import { ApplicationListener, ApplicationLoadBalancer, ApplicationTargetGroup } from '@aws-cdk/aws-elasticloadbalancingv2';
import { IRole } from '@aws-cdk/aws-iam';
import { AddressRecordTarget, ARecord, IHostedZone } from '@aws-cdk/aws-route53';
import { LoadBalancerTarget } from '@aws-cdk/aws-route53-targets';
import cdk = require('@aws-cdk/core');

export enum LoadBalancerType {
APPLICATION,
NETWORK
}

/**
* The properties for the base LoadBalancedEc2Service or LoadBalancedFargateService service.
* The properties for the base ApplicationLoadBalancedEc2Service or ApplicationLoadBalancedFargateService service.
*/
export interface LoadBalancedServiceBaseProps {
export interface ApplicationLoadBalancedServiceBaseProps {
/**
* The name of the cluster that hosts the service.
*
Expand Down Expand Up @@ -66,13 +60,6 @@ export interface LoadBalancedServiceBaseProps {
*/
readonly desiredCount?: number;

/**
* The type of the load balancer to be used.
*
* @default application
*/
readonly loadBalancerType?: LoadBalancerType

/**
* Certificate Manager certificate to associate with the load balancer.
* Setting this option will set the load balancer port to 443.
Expand Down Expand Up @@ -102,13 +89,6 @@ export interface LoadBalancedServiceBaseProps {
*/
readonly enableLogging?: boolean;

/**
* Determines whether the service will be assigned a public IP address.
*
* @default false
*/
readonly publicTasks?: boolean;

/**
* The domain name for the service, e.g. "api.example.com."
*
Expand Down Expand Up @@ -169,22 +149,19 @@ export interface LoadBalancedServiceBaseProps {
}

/**
* The base class for LoadBalancedEc2Service and LoadBalancedFargateService services.
* The base class for ApplicationLoadBalancedEc2Service and ApplicationLoadBalancedFargateService services.
*/
export abstract class LoadBalancedServiceBase extends cdk.Construct {
public readonly assignPublicIp: boolean;
export abstract class ApplicationLoadBalancedServiceBase extends cdk.Construct {
/**
* The desired number of instantiations of the task definition to keep running on the service.
*/
public readonly desiredCount: number;

public readonly loadBalancerType: LoadBalancerType;
public readonly loadBalancer: ApplicationLoadBalancer;

public readonly loadBalancer: BaseLoadBalancer;
public readonly listener: ApplicationListener;

public readonly listener: ApplicationListener | NetworkListener;

public readonly targetGroup: ApplicationTargetGroup | NetworkTargetGroup;
public readonly targetGroup: ApplicationTargetGroup;
/**
* The cluster that hosts the service.
*/
Expand All @@ -193,9 +170,9 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {
public readonly logDriver?: LogDriver;

/**
* Constructs a new instance of the LoadBalancedServiceBase class.
* Constructs a new instance of the ApplicationLoadBalancedServiceBase class.
*/
constructor(scope: cdk.Construct, id: string, props: LoadBalancedServiceBaseProps) {
constructor(scope: cdk.Construct, id: string, props: ApplicationLoadBalancedServiceBaseProps) {
super(scope, id);

if (props.cluster && props.vpc) {
Expand All @@ -207,51 +184,29 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {
const enableLogging = props.enableLogging !== undefined ? props.enableLogging : true;
this.logDriver = props.logDriver !== undefined ? props.logDriver : enableLogging ? this.createAWSLogDriver(this.node.id) : undefined;

this.assignPublicIp = props.publicTasks !== undefined ? props.publicTasks : false;
this.desiredCount = props.desiredCount || 1;

// Load balancer
this.loadBalancerType = props.loadBalancerType !== undefined ? props.loadBalancerType : LoadBalancerType.APPLICATION;

if (this.loadBalancerType !== LoadBalancerType.APPLICATION && this.loadBalancerType !== LoadBalancerType.NETWORK) {
throw new Error(`invalid loadBalancerType`);
}

const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true;

const lbProps = {
vpc: this.cluster.vpc,
internetFacing
};

if (this.loadBalancerType === LoadBalancerType.APPLICATION) {
this.loadBalancer = new ApplicationLoadBalancer(this, 'LB', lbProps);
} else {
this.loadBalancer = new NetworkLoadBalancer(this, 'LB', lbProps);
}
this.loadBalancer = new ApplicationLoadBalancer(this, 'LB', lbProps);

const targetProps = {
port: 80
piradeepk marked this conversation as resolved.
Show resolved Hide resolved
};

const hasCertificate = props.certificate !== undefined;
if (hasCertificate && this.loadBalancerType !== LoadBalancerType.APPLICATION) {
throw new Error("Cannot add certificate to an NLB");
}

if (this.loadBalancerType === LoadBalancerType.APPLICATION) {
this.listener = (this.loadBalancer as ApplicationLoadBalancer).addListener('PublicListener', {
port: hasCertificate ? 443 : 80,
open: true
});
this.targetGroup = this.listener.addTargets('ECS', targetProps);
this.listener = this.loadBalancer.addListener('PublicListener', {
port: props.certificate !== undefined ? 443 : 80,
piradeepk marked this conversation as resolved.
Show resolved Hide resolved
open: true
});
this.targetGroup = this.listener.addTargets('ECS', targetProps);

if (props.certificate !== undefined) {
this.listener.addCertificateArns('Arns', [props.certificate.certificateArn]);
}
} else {
this.listener = (this.loadBalancer as NetworkLoadBalancer).addListener('PublicListener', { port: 80 });
this.targetGroup = this.listener.addTargets('ECS', targetProps);
if (props.certificate !== undefined) {
this.listener.addCertificateArns('Arns', [props.certificate.certificateArn]);
}

if (typeof props.domainName !== 'undefined') {
Expand All @@ -277,11 +232,7 @@ export abstract class LoadBalancedServiceBase extends cdk.Construct {
}

protected addServiceAsTarget(service: BaseService) {
if (this.loadBalancerType === LoadBalancerType.APPLICATION) {
(this.targetGroup as ApplicationTargetGroup).addTarget(service);
} else {
(this.targetGroup as NetworkTargetGroup).addTarget(service);
}
this.targetGroup.addTarget(service);
}

private createAWSLogDriver(prefix: string): AwsLogDriver {
Expand Down
Loading