Skip to content

Commit 078d5a5

Browse files
committed
Issue-256 - Add demo-app ECS task
1 parent 3512841 commit 078d5a5

File tree

6 files changed

+289
-49
lines changed

6 files changed

+289
-49
lines changed

docker-compose-demo-app.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: '3.3'
2+
services:
3+
# This is just for testing the demo-app as a non-Lambda express app
4+
lambdademoapp:
5+
platform: linux/arm64
6+
build:
7+
context: .
8+
dockerfile: DockerfileLambdaDemoApp
9+
ports:
10+
- 3002:3001

src/cdk-construct/API.md

Lines changed: 45 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cdk-construct/src/ecs.ts

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,17 @@ export interface LambdaDispatchECSProps {
5858
readonly cpuArchitecture?: ecs.CpuArchitecture;
5959

6060
/**
61-
* Container image for the ECS task
61+
* Image for the lambda-dispatch Router
6262
* @default - latest image from public ECR repository
6363
*/
64-
readonly containerImage?: ecs.ContainerImage;
64+
readonly routerImage?: ecs.ContainerImage;
65+
66+
/**
67+
* Image for the demo app
68+
* This can be the same as the lambda image because it just won't start the lambda extension
69+
* @default - undefined
70+
*/
71+
readonly demoAppImage?: ecs.ContainerImage;
6572

6673
/**
6774
* The removal policy to apply to the log group
@@ -84,9 +91,13 @@ export class LambdaDispatchECS extends Construct {
8491
*/
8592
public readonly securityGroup: ec2.SecurityGroup;
8693
/**
87-
* Target group for the ECS service
94+
* Target group for the Router service
8895
*/
89-
public readonly targetGroup: elbv2.ApplicationTargetGroup;
96+
public readonly targetGroupRouter: elbv2.ApplicationTargetGroup;
97+
/**
98+
* Target group for the Demo App service
99+
*/
100+
public readonly targetGroupDemoApp: elbv2.ApplicationTargetGroup;
90101

91102
constructor(scope: Construct, id: string, props: LambdaDispatchECSProps) {
92103
super(scope, id);
@@ -133,13 +144,13 @@ export class LambdaDispatchECS extends Construct {
133144
// Give the task role permission to describe the log group, create log streams in that group, and write log events to those streams
134145
logGroup.grant(taskRole, 'logs:DescribeLogGroups', 'logs:CreateLogStream', 'logs:PutLogEvents');
135146

136-
const container = taskDefinition.addContainer('LambdaDispatchRouter', {
147+
const routerContainer = taskDefinition.addContainer('LambdaDispatchRouter', {
137148
image:
138-
props.containerImage ??
149+
props.routerImage ??
139150
ecs.ContainerImage.fromRegistry('public.ecr.aws/pwrdrvr/lambda-dispatch-router:latest'),
140151
logging: ecs.LogDriver.awsLogs({
141152
logGroup: logGroup,
142-
streamPrefix: 'ecs',
153+
streamPrefix: 'router',
143154
}),
144155
environment: {
145156
DOTNET_ThreadPool_UnfairSemaphoreSpinLimit: '0',
@@ -152,20 +163,30 @@ export class LambdaDispatchECS extends Construct {
152163
AWS_CLOUDWATCH_LOG_GROUP: logGroup.logGroupName,
153164
},
154165
});
155-
156-
container.addPortMappings(
166+
routerContainer.addPortMappings(
157167
{ containerPort: 5001, protocol: ecs.Protocol.TCP },
158168
{ containerPort: 5003, protocol: ecs.Protocol.TCP },
159169
{ containerPort: 5004, protocol: ecs.Protocol.TCP },
160170
);
161171

172+
const demoAppContainer = taskDefinition.addContainer('DemoApp', {
173+
image:
174+
props.demoAppImage ??
175+
ecs.ContainerImage.fromRegistry('public.ecr.aws/pwrdrvr/lambda-dispatch-demo-app:latest'),
176+
logging: ecs.LogDriver.awsLogs({
177+
logGroup: logGroup,
178+
streamPrefix: 'demo-app',
179+
}),
180+
});
181+
demoAppContainer.addPortMappings({ containerPort: 3001, protocol: ecs.Protocol.TCP });
182+
162183
this.securityGroup = new ec2.SecurityGroup(this, 'EcsSecurityGroup', {
163184
vpc: props.vpc,
164185
allowAllOutbound: true,
165186
description: 'Security Group for ECS Fargate tasks',
166187
});
167188

168-
// Add inbound rules for ports 5003 and 5004
189+
// Add inbound rules for ports 5003 and 5004 on the router from within the VPC
169190
this.securityGroup.addIngressRule(
170191
ec2.Peer.ipv4(props.vpc.vpcCidrBlock),
171192
ec2.Port.tcp(5003),
@@ -177,7 +198,8 @@ export class LambdaDispatchECS extends Construct {
177198
'Allow inbound traffic on port 5004 from within the VPC (HTTPS from Lambda)',
178199
);
179200

180-
this.targetGroup = new elbv2.ApplicationTargetGroup(this, 'FargateTargetGroup', {
201+
// Create target groups first
202+
this.targetGroupRouter = new elbv2.ApplicationTargetGroup(this, 'FargateTargetGroup', {
181203
vpc: props.vpc,
182204
port: 5001,
183205
protocol: elbv2.ApplicationProtocol.HTTP,
@@ -188,13 +210,29 @@ export class LambdaDispatchECS extends Construct {
188210
timeout: cdk.Duration.seconds(2),
189211
healthyThresholdCount: 2,
190212
},
213+
deregistrationDelay: cdk.Duration.seconds(60),
214+
});
215+
216+
this.targetGroupDemoApp = new elbv2.ApplicationTargetGroup(this, 'DemoAppTargetGroup', {
217+
vpc: props.vpc,
218+
port: 3001,
219+
protocol: elbv2.ApplicationProtocol.HTTP,
220+
targetType: elbv2.TargetType.IP,
221+
healthCheck: {
222+
path: '/health-quick',
223+
interval: cdk.Duration.seconds(5),
224+
timeout: cdk.Duration.seconds(2),
225+
healthyThresholdCount: 2,
226+
},
227+
deregistrationDelay: cdk.Duration.seconds(30),
191228
});
192229

193230
// Configure capacity provider strategy based on props
194231
const capacityProviderStrategies = props.useFargateSpot
195232
? [{ capacityProvider: 'FARGATE_SPOT', weight: 1 }]
196233
: [{ capacityProvider: 'FARGATE', weight: 1 }];
197234

235+
// Create service
198236
this.service = new ecs.FargateService(this, 'EcsService', {
199237
cluster: props.cluster,
200238
taskDefinition,
@@ -205,6 +243,21 @@ export class LambdaDispatchECS extends Construct {
205243
capacityProviderStrategies,
206244
});
207245

246+
// Add target group attachments with specific container names and ports
247+
this.targetGroupRouter.addTarget(
248+
this.service.loadBalancerTarget({
249+
containerName: 'LambdaDispatchRouter',
250+
containerPort: 5001,
251+
}),
252+
);
253+
254+
this.targetGroupDemoApp.addTarget(
255+
this.service.loadBalancerTarget({
256+
containerName: 'DemoApp',
257+
containerPort: 3001,
258+
}),
259+
);
260+
208261
// Add auto-scaling
209262
const scaling = this.service.autoScaleTaskCount({
210263
maxCapacity: props.maxCapacity ?? 10,
@@ -215,8 +268,5 @@ export class LambdaDispatchECS extends Construct {
215268
scaleInCooldown: cdk.Duration.seconds(60),
216269
scaleOutCooldown: cdk.Duration.seconds(60),
217270
});
218-
219-
// Attach the service to the target group
220-
this.service.attachToApplicationTargetGroup(this.targetGroup);
221271
}
222272
}

src/cdk-stack/src/lambda-dispatch-stack.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ export class LambdaDispatchStack extends cdk.Stack {
140140
dockerImage:
141141
!usePublicImages && (props.lambdaECRRepoName || props.lambdaImageTag)
142142
? lambda.DockerImageCode.fromEcr(
143-
ecr.Repository.fromRepositoryName(this, 'LambdaRepo', lambdaECRRepoName),
144-
{
145-
tagOrDigest: lambdaTag,
146-
},
147-
)
143+
ecr.Repository.fromRepositoryName(this, 'LambdaRepo', lambdaECRRepoName),
144+
{
145+
tagOrDigest: lambdaTag,
146+
},
147+
)
148148
: undefined,
149149
});
150150

@@ -153,12 +153,19 @@ export class LambdaDispatchStack extends cdk.Stack {
153153
vpc,
154154
lambdaFunction: lambdaConstruct.function,
155155
cluster,
156-
containerImage:
156+
routerImage:
157157
!usePublicImages && process.env.PR_NUMBER
158158
? ecs.ContainerImage.fromEcrRepository(
159-
ecr.Repository.fromRepositoryName(this, 'EcsRepo', 'lambda-dispatch-router'),
160-
`pr-${process.env.PR_NUMBER}-${process.env.GIT_SHA_SHORT}`,
161-
)
159+
ecr.Repository.fromRepositoryName(this, 'EcsRepo', 'lambda-dispatch-router'),
160+
`pr-${process.env.PR_NUMBER}-${process.env.GIT_SHA_SHORT}`,
161+
)
162+
: undefined,
163+
demoAppImage:
164+
!usePublicImages && process.env.PR_NUMBER
165+
? ecs.ContainerImage.fromEcrRepository(
166+
ecr.Repository.fromRepositoryName(this, 'DemoAppRepo', 'lambda-dispatch-demo-app'),
167+
`pr-${process.env.PR_NUMBER}-arm64-${process.env.GIT_SHA_SHORT}`,
168+
)
162169
: undefined,
163170
useFargateSpot: props.useFargateSpot ?? true,
164171
removalPolicy: props.removalPolicy,
@@ -167,15 +174,22 @@ export class LambdaDispatchStack extends cdk.Stack {
167174
// Allow ECS tasks to invoke Lambda
168175
lambdaConstruct.function.grantInvoke(ecsConstruct.service.taskDefinition.taskRole);
169176

170-
const hostname = `lambdadispatch${process.env.PR_NUMBER ? `-pr-${process.env.PR_NUMBER}` : ''}`;
177+
const hostnameRouter = `lambdadispatch${process.env.PR_NUMBER ? `-pr-${process.env.PR_NUMBER}` : ''}`;
178+
const hostnameDemoApp = `lambdadispatch-demoapp${process.env.PR_NUMBER ? `-pr-${process.env.PR_NUMBER}` : ''}`;
171179

172180
// Add the target group to the HTTPS listener
173181
httpsListener.addTargetGroups('EcsTargetGroup', {
174-
targetGroups: [ecsConstruct.targetGroup],
175-
conditions: [elbv2.ListenerCondition.hostHeaders([`${hostname}.ghpublic.pwrdrvr.com`])],
182+
targetGroups: [ecsConstruct.targetGroupRouter],
183+
conditions: [elbv2.ListenerCondition.hostHeaders([`${hostnameRouter}.ghpublic.pwrdrvr.com`])],
176184
// Set the priority to the PR number or 49999 if not a PR
177185
priority: process.env.PR_NUMBER ? parseInt(process.env.PR_NUMBER) : 49999,
178186
});
187+
httpsListener.addTargetGroups('EcsTargetGroupDemoApp', {
188+
targetGroups: [ecsConstruct.targetGroupDemoApp],
189+
conditions: [elbv2.ListenerCondition.hostHeaders([`${hostnameDemoApp}.ghpublic.pwrdrvr.com`])],
190+
// Set the priority to the PR number + 10000 or 49998 if not a PR
191+
priority: process.env.PR_NUMBER ? parseInt(process.env.PR_NUMBER) + 10000 : 49998,
192+
});
179193

180194
// Create Route53 records
181195
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
@@ -184,7 +198,12 @@ export class LambdaDispatchStack extends cdk.Stack {
184198
});
185199
new route53.ARecord(this, 'LambdaDispatchRecord', {
186200
zone: hostedZone,
187-
recordName: hostname,
201+
recordName: hostnameRouter,
202+
target: route53.RecordTarget.fromAlias(new route53targets.LoadBalancerTarget(loadBalancer)),
203+
});
204+
new route53.ARecord(this, 'LambdaDispatchDemoAppRecord', {
205+
zone: hostedZone,
206+
recordName: hostnameDemoApp,
188207
target: route53.RecordTarget.fromAlias(new route53targets.LoadBalancerTarget(loadBalancer)),
189208
});
190209

0 commit comments

Comments
 (0)