-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathecs-fargatespot-service-restapi-stack.ts
140 lines (127 loc) · 6.34 KB
/
ecs-fargatespot-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
132
133
134
135
136
137
138
139
140
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 with Spot CapacityProvider, Auto Scaling, ALB, and Log Group.
* Set the ALB logs for the production-level.
*/
export class FargateSpotRestAPIServiceStack 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 = 'fargatespot-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 container = taskDefinition.addContainer('container-restapi', {
containerName,
image: ecs.ContainerImage.fromRegistry(`${props.env?.account}.dkr.ecr.${props.env?.region}.amazonaws.com/fargate-restapi-${props.stage}:latest`),
// or build with /app folder
// 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
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE_SPOT',
weight: 1,
},
{
capacityProvider: 'FARGATE',
weight: 1,
}
]
});
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 });
}
}