Skip to content

Commit

Permalink
WIP Add Service Discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
SoManyHs committed Feb 5, 2019
1 parent 44d150f commit c764b63
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 9 deletions.
5 changes: 3 additions & 2 deletions examples/cdk-examples-typescript/hello-cdk-ecs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class BonjourECS extends cdk.Stack {
});

// Instantiate ECS Service with just cluster and image
const ecsService = new ecs.LoadBalancedEc2Service(this, "Ec2Service", {
// const ecsService = new ecs.LoadBalancedEc2Service(this, "Ec2Service", {
new ecs.LoadBalancedEc2Service(this, "Ec2Service", {
cluster,
memoryLimitMiB: 512,
image: ecs.ContainerImage.fromDockerHub("amazon/amazon-ecs-sample"),
Expand All @@ -28,7 +29,7 @@ class BonjourECS extends cdk.Stack {
// ecsService.addTracing

// Output the DNS where you can access your service
new cdk.Output(this, 'LoadBalancerDNS', { value: ecsService.loadBalancer.dnsName });
// new cdk.Output(this, 'LoadBalancerDNS', { value: ecsService.loadBalancer.dnsName });
}
}

Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-ecs/lib/base/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import iam = require('@aws-cdk/aws-iam');
// import cloudmap = require ('@aws-cdk/aws-servicediscovery');
import cdk = require('@aws-cdk/cdk');
import { NetworkMode, TaskDefinition } from '../base/task-definition';
import { CfnService } from '../ecs.generated';
Expand Down Expand Up @@ -63,6 +64,11 @@ export abstract class BaseService extends cdk.Construct
*/
public readonly connections: ec2.Connections = new ec2.Connections();

/**
* Cloudmap Service to enable service discovery
*/
// public readonly cloudmapService: cloudmap.Service = new cloudmap.Service();

/**
* ARN of this service
*/
Expand All @@ -85,6 +91,7 @@ export abstract class BaseService extends cdk.Construct

protected loadBalancers = new Array<CfnService.LoadBalancerProperty>();
protected networkConfiguration?: CfnService.NetworkConfigurationProperty;
protected serviceRegistries = new Array<CfnService.ServiceRegistryProperty>();
private readonly resource: CfnService;
private scalableTaskCount?: ScalableTaskCount;

Expand All @@ -102,6 +109,7 @@ export abstract class BaseService extends cdk.Construct
desiredCount: props.desiredCount,
serviceName: props.serviceName,
loadBalancers: new cdk.Token(() => this.loadBalancers),
serviceRegistries: new cdk.Token(() => this.serviceRegistries),
deploymentConfiguration: {
maximumPercent: props.maximumPercent || 200,
minimumHealthyPercent: props.minimumHealthyPercent || 50
Expand Down
48 changes: 48 additions & 0 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import autoscaling = require('@aws-cdk/aws-autoscaling');
import cloudwatch = require ('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import iam = require('@aws-cdk/aws-iam');
import cloudmap = require ('@aws-cdk/aws-servicediscovery');
import cdk = require('@aws-cdk/cdk');
import { InstanceDrainHook } from './drain-hook/instance-drain-hook';
import { CfnCluster } from './ecs.generated';
Expand Down Expand Up @@ -54,6 +55,16 @@ export class Cluster extends cdk.Construct implements ICluster {
*/
public readonly clusterName: string;

/**
* The name of the service discovery namespace created in this cluster
*/
public serviceDiscoveryNamespace: string;

/**
* The ID of the service discovery namespace created in this cluster
*/
public serviceDiscoveryNamespaceId: string;

/**
* Whether the cluster has EC2 capacity associated with it
*/
Expand All @@ -69,6 +80,22 @@ export class Cluster extends cdk.Construct implements ICluster {
this.clusterName = cluster.clusterName;
}

/**
* Add a CloudMap namespace for this cluster. Defaults to HTTP Namespace unless otherwise specified. FIXME: allow
* DNSNamespaces ?
*/
public addNamespace(name: string): cloudmap.PrivateDnsNamespace {
const sdNamespace = new cloudmap.PrivateDnsNamespace(this, 'DefaultServiceDiscoveryNamespace', {
name,
vpc: this.vpc
});

this.serviceDiscoveryNamespace = name;
this.serviceDiscoveryNamespaceId = sdNamespace.namespaceId;

return sdNamespace;
}

/**
* Add a default-configured AutoScalingGroup running the ECS-optimized AMI to this Cluster
*
Expand Down Expand Up @@ -403,3 +430,24 @@ export interface AddDefaultAutoScalingGroupOptions extends AddAutoScalingGroupCa
*/
minCapacity?: number;
}

// /**
// * Properties for Service Discovery Namespace
// */
// export interface NamespaceProps {

// /**
// * Service DiscoveryNamespace, e.g. foo.com
// */
// name: string;

// /**
// * HTTP or DNS Namespace
// *
// * @default HTTP
// */
// type?: string;
// }

// const HTTP_NAMESPACE = "HTTP";
// const DNS_NAMESPACE = "DNS";
58 changes: 54 additions & 4 deletions packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import cloudwatch = require ('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import elb = require('@aws-cdk/aws-elasticloadbalancing');
import cloudmap = require ('@aws-cdk/aws-servicediscovery');
import cdk = require('@aws-cdk/cdk');
import { BaseService, BaseServiceProps } from '../base/base-service';
import { NetworkMode, TaskDefinition } from '../base/task-definition';
Expand Down Expand Up @@ -198,6 +199,48 @@ export class Ec2Service extends BaseService implements elb.ILoadBalancerTarget {
});
}

/**
* Enable CloudMap service discovery for the service
*/
public enableServiceDiscovery(props: ServiceDiscoveryProps): cloudmap.Service {
// Determine DNS type based on network mode
const networkMode = this.taskDefinition.networkMode;

if (networkMode === NetworkMode.None) {
throw new Error("Cannot use a service discovery if NetworkMode is None. Use Host or AwsVpc instead.");
}
// Bridge or host network mode requires SRV records
let dnsType = props.dnsType;

if (networkMode === NetworkMode.Bridge || networkMode === NetworkMode.Host) {
dnsType = cloudmap.DnsType.Srv;
}

if (networkMode === NetworkMode.AwsVpc && props.dnsType === undefined ) {
// ECS does not currently support IPv6, so AAAA records not possible
dnsType = cloudmap.DnsType.A;
}

const cloudmapService = new cloudmap.Service(this, 'CloudmapService', {
namespaceId: props.namespaceId,
name: props.name,
dnsType: dnsType!,
healthCheckCustomConfig: { failureThreshold: props.failureThreshold
|| 1 }
});

const serviceArn = cloudmapService.serviceArn;

// add Cloudmap service to the ECS Service's serviceRegistry
this.serviceRegistries.push({
registryArn: serviceArn,
containerName: this.taskDefinition.defaultContainer!.node.id,
containerPort: this.taskDefinition.defaultContainer!.containerPort
});

return cloudmapService;
}

/**
* Return the given named metric for this Service
*/
Expand Down Expand Up @@ -290,8 +333,15 @@ export enum BinPackResource {
*/
Cpu = 'cpu',

/**
* Fill up hosts' memory allocations first
*/
Memory = 'memory',
/**
* Fill up hosts' memory allocations first
*/
Memory = 'memory',
}

export interface ServiceDiscoveryProps {
namespaceId: string,
name?: string,
dnsType?: string,
failureThreshold?: number,
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-ecs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"@aws-cdk/aws-lambda": "^0.23.0",
"@aws-cdk/aws-logs": "^0.23.0",
"@aws-cdk/aws-route53": "^0.23.0",
"@aws-cdk/aws-servicediscovery": "^0.23.0",
"@aws-cdk/aws-sns": "^0.23.0",
"@aws-cdk/cdk": "^0.23.0",
"@aws-cdk/cx-api": "^0.23.0"
Expand All @@ -97,6 +98,7 @@
"@aws-cdk/aws-iam": "^0.23.0",
"@aws-cdk/aws-logs": "^0.23.0",
"@aws-cdk/aws-route53": "^0.23.0",
"@aws-cdk/aws-servicediscovery": "^0.23.0",
"@aws-cdk/cdk": "^0.23.0"
},
"engines": {
Expand All @@ -110,4 +112,4 @@
"construct-ctor:@aws-cdk/aws-ecs.LoadBalancedFargateServiceApplet.<initializer>.params[0]"
]
}
}
}
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import ec2 = require('@aws-cdk/aws-ec2');
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import cdk = require('@aws-cdk/cdk');
Expand All @@ -24,6 +23,7 @@ const container = taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromDockerHub("amazon/amazon-ecs-sample"),
memoryLimitMiB: 256,
});

container.addPortMappings({
containerPort: 80,
hostPort: 8080,
Expand All @@ -44,4 +44,4 @@ listener.addTargets('ECS', {

new cdk.Output(stack, 'LoadBalancerDNS', { value: lb.dnsName, });

app.run();
app.run();
77 changes: 77 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import ec2 = require('@aws-cdk/aws-ec2');
// import ecr = require('@aws-cdk/aws-ecr');
// import cloudmap = require('@aws-cdk/aws-servicediscovery');
import cdk = require('@aws-cdk/cdk');
import ecs = require('../../lib');

const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-ecs-integ-ecs');

const vpc = new ec2.VpcNetwork(stack, 'Vpc', { maxAZs: 2 });

const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });

cluster.addDefaultAutoScalingGroupCapacity({
instanceType: new ec2.InstanceType('t2.micro')
});

const domainName = "scorekeep.com";
cluster.addNamespace(domainName);

// Create frontend service
const frontendTD = new ecs.Ec2TaskDefinition(stack, 'frontendTD');

// const frontendImage = new ecr.Repository(stack, 'scorekeep-frontend');
const frontend = frontendTD.addContainer('frontend', {
// image: ecs.ContainerImage.fromEcrRepository("arn:aws:ecr:us-west-2:794715269151:repository/scorekeep-frontend"),
// image: ecs.ContainerImage.fromEcrRepository(frontendImage),
image: ecs.ContainerImage.fromDockerHub("amazon/amazon-ecs-sample"),
memoryLimitMiB: 256,
});

frontend.addPortMappings({
containerPort: 80,
hostPort: 8080,
protocol: ecs.Protocol.Tcp
});

const frontendService = new ecs.Ec2Service(stack, "FrontendService", {
cluster,
taskDefinition: frontendTD,
});

// Create backend service
// const apiTD = new ecs.Ec2TaskDefinition(stack, 'TaskDef');

// const apiImage = new ecr.Repository(stack, 'scorekeep-api');
// const api = apiTD.addContainer('api', {
// image: ecs.ContainerImage.fromEcrRepository(apiImage),
// memoryLimitMiB: 256,
// });

// api.addPortMappings({
// containerPort: 80,
// hostPort: 5000,
// protocol: ecs.Protocol.Tcp
// });

// const apiService = new ecs.Ec2Service(stack, "APIService", {
// cluster,
// taskDefinition: apiTD,
// });

// NOTE: Passing in the cluster's serviceDiscoveryNamespaceId is a little clunky, would have liked to have
// enableServiceDiscovery not take any argument and look it up on the cluster, but that would have involved adding
// cluster as a member on service which may have called for a breaking change. This is a also little more explicit
const namespaceId = cluster.serviceDiscoveryNamespaceId;

frontendService.enableServiceDiscovery({
name: "frontend",
namespaceId,
});

// apiService.enableServiceDiscovery({
// namespaceId,
// });

app.run();
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import cdk = require('@aws-cdk/cdk');
import { CfnPublicDnsNamespace} from './servicediscovery.generated';

// TODO: refactor to use factory?
export interface PublicDnsNamespaceProps {
/**
* A name for the HttpNamespace.
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export class Service extends cdk.Construct {
*/
public readonly name: string;

public readonly namespaceId: string;

/**
* The ID of the namespace that you want to use for DNS configuration.
*/
Expand Down

0 comments on commit c764b63

Please sign in to comment.