-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathecs-fargate-service-restapi-stack.ts
131 lines (118 loc) · 6.06 KB
/
ecs-fargate-service-restapi-stack.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import { Stack, CfnOutput, Duration, Tags } from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import { CLUSTER_NAME } from '../../ecs-fargate-cluster/lib/cluster-config';
import { StackCommonProps, SSM_PREFIX } from '../../config';
/**
* Crearte Fargate Service, Auto Scaling, ALB, and Log Group.
* Set the ALB options for the production-level.
*/
export class FargateRestAPIServiceStack extends Stack {
constructor(scope: Construct, id: string, props: StackCommonProps) {
super(scope, id, props);
const vpcId = this.node.tryGetContext('vpcId') || ssm.StringParameter.valueFromLookup(this, `${SSM_PREFIX}/vpc-id`);
const vpc = ec2.Vpc.fromLookup(this, 'vpc', { vpcId });
const clusterSgId = ssm.StringParameter.valueFromLookup(this, `${SSM_PREFIX}/cluster-securitygroup-id`);
const ecsSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, 'ecs-security-group', clusterSgId);
const cluster = ecs.Cluster.fromClusterAttributes(this, 'ecs-fargate-cluster', {
clusterName: `${CLUSTER_NAME}-${props.stage}`,
vpc,
securityGroups: [ecsSecurityGroup]
});
const serviceName = 'fargate-restapi'
const containerName = `${serviceName}-container`
const applicationPort = 8080;
const executionRoleArn = ssm.StringParameter.valueFromLookup(this, `${SSM_PREFIX}/task-execution-role-arn`);
const taskRoleArn = ssm.StringParameter.valueFromLookup(this, `${SSM_PREFIX}/default-task-role-arn`);
const taskDefinition = new ecs.TaskDefinition(this, 'fargate-task-definition', {
cpu: '1024',
memoryMiB: '2048',
compatibility: ecs.Compatibility.FARGATE,
family: `${serviceName}-task`,
executionRole: iam.Role.fromRoleArn(this, 'task-execution-role', cdk.Lazy.string({ produce: () => executionRoleArn })),
taskRole: iam.Role.fromRoleArn(this, 'task-role', cdk.Lazy.string({ produce: () => taskRoleArn }))
});
const regUrl = `${props.env?.account}.dkr.ecr.${props.env?.region}.amazonaws.com/fargate-restapi-${props.stage}:latest`;
const container = taskDefinition.addContainer('container-restapi', {
containerName,
image: ecs.ContainerImage.fromRegistry(regUrl),
// or build with /app folder asset
// import * as path from 'path';
// image: ecs.ContainerImage.fromAsset(path.join(__dirname, "../../", "app")),
cpu: 1024,
memoryReservationMiB: 2048
});
container.addPortMappings({ containerPort: applicationPort, hostPort: applicationPort });
const fargateservice = new ecs.FargateService(this, 'ecs-fargate-service', {
cluster,
serviceName: `${serviceName}-${props.stage}`,
taskDefinition,
enableExecuteCommand: true,
minHealthyPercent: 100,
maxHealthyPercent: 200,
healthCheckGracePeriod: Duration.seconds(0) // set the value as your application initialize time
});
fargateservice.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 100,
}).scaleOnCpuUtilization('cpuscaling', {
targetUtilizationPercent: 50,
scaleOutCooldown: Duration.seconds(60),
scaleInCooldown: Duration.seconds(120)
});
const logGroup = new logs.LogGroup(this, 'loggroup', {
logGroupName: serviceName,
removalPolicy: cdk.RemovalPolicy.DESTROY,
retention: logs.RetentionDays.TWO_WEEKS,
});
const albSecurityGroupName = `albsg-${serviceName}`
const albSecurityGroup = new ec2.SecurityGroup(this, albSecurityGroupName, {
securityGroupName: albSecurityGroupName,
vpc,
allowAllOutbound: true,
description: `ALB security group for ${serviceName} Service`
});
ecsSecurityGroup.addIngressRule(albSecurityGroup, ec2.Port.tcp(applicationPort), 'Allow from ALB');
albSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow any');
const alb = new elbv2.ApplicationLoadBalancer(this, 'alb', {
securityGroup: albSecurityGroup,
vpc,
loadBalancerName: `alb-${serviceName}`,
internetFacing: true,
deletionProtection: false,
idleTimeout: cdk.Duration.seconds(30),
});
alb.addListener('https-listener', {
protocol: elbv2.ApplicationProtocol.HTTP,
open: false,
}).addTargets('ec2-service-target', {
targetGroupName: `tg-${serviceName}`,
port: applicationPort,
protocol: elbv2.ApplicationProtocol.HTTP,
targets: [fargateservice.loadBalancerTarget({
containerName: containerName,
containerPort: applicationPort,
})],
healthCheck: {
healthyThresholdCount: 2,
unhealthyThresholdCount: 5,
interval: Duration.seconds(31),
path: '/ping',
timeout: Duration.seconds(30),
},
deregistrationDelay: Duration.seconds(15)
});
Tags.of(albSecurityGroup).add('Stage', props.stage);
Tags.of(albSecurityGroup).add('Name', albSecurityGroupName);
new CfnOutput(this, 'Service', { value: fargateservice.serviceArn });
new CfnOutput(this, 'TaskDefinition', { value: taskDefinition.family });
new CfnOutput(this, 'LogGroup', { value: logGroup.logGroupName });
new CfnOutput(this, 'ALB', { value: alb.loadBalancerDnsName });
}
}