Skip to content

Commit 4d43243

Browse files
committed
add cost breakdown table; allow using nat instance
1 parent ae718df commit 4d43243

4 files changed

Lines changed: 143 additions & 32 deletions

File tree

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,27 @@ cp .env.local.example .env.local
106106
```
107107

108108
## Cost
109-
Lambda, SQS, CloudWatch, CloudFront, and S3 offer free tier plans, which allows you to use those services almost freely for small businesses.
110-
Up to one million requests per month, most of the costs related to those services are free. See [this page for more details](https://aws.amazon.com/free/).
111109

112-
Aurora PostgreSQL Serverless v2 is billed based on compute and storage usage. The database automatically scales up and down based on workload, and you only pay for the resources you use. See [this page for the current prices](https://aws.amazon.com/rds/aurora/pricing/). Aurora Serverless v2 can scale down to almost zero during periods of inactivity, helping to minimize costs (see [database.ts](cdk/lib/constructs/database.ts) for configuration details).
113-
114-
Other costs will be derived from data transfer and Elastic Container Repository (used for Docker Lambda). Although it usually does not cost much compared to other services, you may want to continuously monitor the billing metrics. Please refer to [the document to set CloudWatch alarm for AWS charges](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html).
110+
The following table provides a sample cost breakdown for deploying this system in the us-east-1 (N. Virginia) region for one month (when deployed using less expensive configuration).
111+
112+
| Service | Usage Details | Monthly Cost [USD] |
113+
|---------|--------------|-------------------|
114+
| Aurora Serverless v2 | 0.5 ACU × 2 hour/day, 1GB storage | 3.6 |
115+
| Cognito | 100 MAU (Monthly Active Users) | 1.5 |
116+
| AppSync Events | 100 events/month, 10 hours connection/user | 0.02 |
117+
| Lambda | 1000 requests/month, 512MB×200ms/request | 0.15 |
118+
| Lambda@Edge | 100 requests/month, 128MB×50ms/request | 0.09 |
119+
| VPC | NAT Instance (t4g.nano) x1 | 3.02 |
120+
| EventBridge | Scheduler 100 jobs/month | 0.0001 |
121+
| CloudFront | Data transfer 1kB/request | 0.01 |
122+
| Total | | 8.49 |
123+
124+
Notes:
125+
- Assumes 100 users per month and 1000 requests per user
126+
- Aurora usage assumed at 1 hour per day
127+
- Actual costs may vary depending on usage patterns
128+
129+
Costs could be further reduced by leveraging Free Tier benefits where applicable. Lambda, SQS, CloudWatch, CloudFront, EC2, and Cognito offer free tier plans, which allows you to use those services almost freely for small businesses. See [this page for more details](https://aws.amazon.com/free/).
115130

116131
## Clean up
117132
To avoid incurring future charges, clean up the resources you created.

cdk/bin/cdk.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ const app = new cdk.App();
99
interface EnvironmentProps {
1010
account: string;
1111
domainName: string;
12+
useNatInstance?: boolean;
1213
}
1314

1415
const props: EnvironmentProps = {
1516
account: process.env.CDK_DEFAULT_ACCOUNT!,
16-
domainName: 'mtomooka.people.aws.dev',
17+
domainName: 'FIXME.example.com',
18+
useNatInstance: true,
1719
};
1820

1921
const virginia = new UsEast1Stack(app, 'ServerlessWebappStarterKitUsEast1Stack', {

cdk/lib/main-stack.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Construct } from 'constructs';
44
import { AsyncJob } from './constructs/async-job';
55
import { Auth } from './constructs/auth';
66
import { Database } from './constructs/database';
7-
import { Vpc } from 'aws-cdk-lib/aws-ec2';
7+
import { InstanceClass, InstanceSize, InstanceType, NatProvider, Vpc } from 'aws-cdk-lib/aws-ec2';
88
import { HostedZone } from 'aws-cdk-lib/aws-route53';
99
import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
1010
import { Webapp } from './constructs/webapp';
@@ -15,12 +15,19 @@ interface MainStackProps extends StackProps {
1515
readonly sharedCertificate: ICertificate;
1616
readonly signPayloadHandler: EdgeFunction;
1717
readonly domainName: string;
18+
19+
/**
20+
* @default true
21+
*/
22+
readonly useNatInstance?: boolean;
1823
}
1924

2025
export class MainStack extends Stack {
2126
constructor(scope: Construct, id: string, props: MainStackProps) {
2227
super(scope, id, { description: 'Serverless fullstack webapp stack (uksb-1tupboc47)', ...props });
2328

29+
const { useNatInstance = true } = props;
30+
2431
const hostedZone = HostedZone.fromLookup(this, 'HostedZone', {
2532
domainName: props.domainName,
2633
});
@@ -35,7 +42,15 @@ export class MainStack extends Stack {
3542
});
3643

3744
const vpc = new Vpc(this, `Vpc`, {
38-
natGateways: 1,
45+
...(useNatInstance
46+
? {
47+
natGatewayProvider: NatProvider.instanceV2({
48+
instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.NANO),
49+
associatePublicIpAddress: true,
50+
}),
51+
natGateways: 1,
52+
}
53+
: {}),
3954
});
4055

4156
const database = new Database(this, 'Database', { vpc });

cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,11 +2200,40 @@ Object {
22002200
},
22012201
"Type": "AWS::EC2::InternetGateway",
22022202
},
2203+
"VpcNatSecurityGroup8DA26EDC": Object {
2204+
"Properties": Object {
2205+
"GroupDescription": "Security Group for NAT instances",
2206+
"SecurityGroupEgress": Array [
2207+
Object {
2208+
"CidrIp": "0.0.0.0/0",
2209+
"Description": "Allow all outbound traffic by default",
2210+
"IpProtocol": "-1",
2211+
},
2212+
],
2213+
"SecurityGroupIngress": Array [
2214+
Object {
2215+
"CidrIp": "0.0.0.0/0",
2216+
"Description": "from 0.0.0.0/0:ALL TRAFFIC",
2217+
"IpProtocol": "-1",
2218+
},
2219+
],
2220+
"Tags": Array [
2221+
Object {
2222+
"Key": "Name",
2223+
"Value": "ServerlessWebappStarterKitStack/Vpc",
2224+
},
2225+
],
2226+
"VpcId": Object {
2227+
"Ref": "Vpc8378EB38",
2228+
},
2229+
},
2230+
"Type": "AWS::EC2::SecurityGroup",
2231+
},
22032232
"VpcPrivateSubnet1DefaultRouteBE02A9ED": Object {
22042233
"Properties": Object {
22052234
"DestinationCidrBlock": "0.0.0.0/0",
2206-
"NatGatewayId": Object {
2207-
"Ref": "VpcPublicSubnet1NATGateway4D7517AA",
2235+
"InstanceId": Object {
2236+
"Ref": "VpcPublicSubnet1NatInstance57B636B8",
22082237
},
22092238
"RouteTableId": Object {
22102239
"Ref": "VpcPrivateSubnet1RouteTableB2C5B500",
@@ -2265,8 +2294,8 @@ Object {
22652294
"VpcPrivateSubnet2DefaultRoute060D2087": Object {
22662295
"Properties": Object {
22672296
"DestinationCidrBlock": "0.0.0.0/0",
2268-
"NatGatewayId": Object {
2269-
"Ref": "VpcPublicSubnet1NATGateway4D7517AA",
2297+
"InstanceId": Object {
2298+
"Ref": "VpcPublicSubnet1NatInstance57B636B8",
22702299
},
22712300
"RouteTableId": Object {
22722301
"Ref": "VpcPrivateSubnet2RouteTableA678073B",
@@ -2327,8 +2356,8 @@ Object {
23272356
"VpcPrivateSubnet3DefaultRoute94B74F0D": Object {
23282357
"Properties": Object {
23292358
"DestinationCidrBlock": "0.0.0.0/0",
2330-
"NatGatewayId": Object {
2331-
"Ref": "VpcPublicSubnet1NATGateway4D7517AA",
2359+
"InstanceId": Object {
2360+
"Ref": "VpcPublicSubnet1NatInstance57B636B8",
23322361
},
23332362
"RouteTableId": Object {
23342363
"Ref": "VpcPrivateSubnet3RouteTableD98824C7",
@@ -2401,41 +2430,91 @@ Object {
24012430
},
24022431
"Type": "AWS::EC2::Route",
24032432
},
2404-
"VpcPublicSubnet1EIPD7E02669": Object {
2433+
"VpcPublicSubnet1NatInstance57B636B8": Object {
2434+
"DependsOn": Array [
2435+
"VpcPublicSubnet1DefaultRoute3DA9E72A",
2436+
"VpcPublicSubnet1NatInstanceInstanceRole9D835E32",
2437+
"VpcPublicSubnet1RouteTableAssociation97140677",
2438+
],
24052439
"Properties": Object {
2406-
"Domain": "vpc",
2440+
"AvailabilityZone": "dummy1a",
2441+
"IamInstanceProfile": Object {
2442+
"Ref": "VpcPublicSubnet1NatInstanceInstanceProfileEE10C485",
2443+
},
2444+
"ImageId": Object {
2445+
"Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61arm64C96584B6F00A464EAD1953AFF4B05118Parameter",
2446+
},
2447+
"InstanceType": "t4g.nano",
2448+
"NetworkInterfaces": Array [
2449+
Object {
2450+
"AssociatePublicIpAddress": true,
2451+
"DeviceIndex": "0",
2452+
"GroupSet": Array [
2453+
Object {
2454+
"Fn::GetAtt": Array [
2455+
"VpcNatSecurityGroup8DA26EDC",
2456+
"GroupId",
2457+
],
2458+
},
2459+
],
2460+
"SubnetId": Object {
2461+
"Ref": "VpcPublicSubnet1Subnet5C2D37C4",
2462+
},
2463+
},
2464+
],
2465+
"SourceDestCheck": false,
24072466
"Tags": Array [
24082467
Object {
24092468
"Key": "Name",
2410-
"Value": "ServerlessWebappStarterKitStack/Vpc/PublicSubnet1",
2469+
"Value": "ServerlessWebappStarterKitStack/Vpc/PublicSubnet1/NatInstance",
24112470
},
24122471
],
2472+
"UserData": Object {
2473+
"Fn::Base64": "#!/bin/bash
2474+
yum install iptables-services -y
2475+
systemctl enable iptables
2476+
systemctl start iptables
2477+
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf
2478+
sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
2479+
sudo /sbin/iptables -t nat -A POSTROUTING -o $(route | awk '/^default/{print $NF}') -j MASQUERADE
2480+
sudo /sbin/iptables -F FORWARD
2481+
sudo service iptables save",
2482+
},
24132483
},
2414-
"Type": "AWS::EC2::EIP",
2484+
"Type": "AWS::EC2::Instance",
24152485
},
2416-
"VpcPublicSubnet1NATGateway4D7517AA": Object {
2417-
"DependsOn": Array [
2418-
"VpcPublicSubnet1DefaultRoute3DA9E72A",
2419-
"VpcPublicSubnet1RouteTableAssociation97140677",
2420-
],
2486+
"VpcPublicSubnet1NatInstanceInstanceProfileEE10C485": Object {
24212487
"Properties": Object {
2422-
"AllocationId": Object {
2423-
"Fn::GetAtt": Array [
2424-
"VpcPublicSubnet1EIPD7E02669",
2425-
"AllocationId",
2488+
"Roles": Array [
2489+
Object {
2490+
"Ref": "VpcPublicSubnet1NatInstanceInstanceRole9D835E32",
2491+
},
2492+
],
2493+
},
2494+
"Type": "AWS::IAM::InstanceProfile",
2495+
},
2496+
"VpcPublicSubnet1NatInstanceInstanceRole9D835E32": Object {
2497+
"Properties": Object {
2498+
"AssumeRolePolicyDocument": Object {
2499+
"Statement": Array [
2500+
Object {
2501+
"Action": "sts:AssumeRole",
2502+
"Effect": "Allow",
2503+
"Principal": Object {
2504+
"Service": "ec2.amazonaws.com",
2505+
},
2506+
},
24262507
],
2427-
},
2428-
"SubnetId": Object {
2429-
"Ref": "VpcPublicSubnet1Subnet5C2D37C4",
2508+
"Version": "2012-10-17",
24302509
},
24312510
"Tags": Array [
24322511
Object {
24332512
"Key": "Name",
2434-
"Value": "ServerlessWebappStarterKitStack/Vpc/PublicSubnet1",
2513+
"Value": "ServerlessWebappStarterKitStack/Vpc/PublicSubnet1/NatInstance",
24352514
},
24362515
],
24372516
},
2438-
"Type": "AWS::EC2::NatGateway",
2517+
"Type": "AWS::IAM::Role",
24392518
},
24402519
"VpcPublicSubnet1RouteTable6C95E38E": Object {
24412520
"Properties": Object {

0 commit comments

Comments
 (0)