Skip to content

Commit

Permalink
fix(ecs): separate application and network load balanced services (#3719
Browse files Browse the repository at this point in the history
)

Separate out the logic for fronting a service behind either an application or network load balancer from the LoadBalancedServiceBase to either ApplicationLoadBalancedServiceBase or NetworkLoadBalancedServiceBase.

**Changes:**
* Created the ApplicationLoadBalanced*Service and NetworkLoadBalanced*Service constructs to separate out the logic between using either LoadBalancer type.
* Removed unused properties.
* Deleted the LoadBalanced*Service constructs.

BREAKING CHANGE: The LoadBalancedServiceBase, LoadBalancedEc2Service and LoadBalancedFargateService constructs have been separated out into Application and Network LoadBalancedService constructs for both Ec2 and Fargate Services.
  • Loading branch information
piradeepk authored and rix0rrr committed Sep 6, 2019
1 parent 479663b commit 21eb835
Show file tree
Hide file tree
Showing 21 changed files with 612 additions and 189 deletions.
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
};

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,
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

0 comments on commit 21eb835

Please sign in to comment.