A CDK construct that provides an easy and low-cost ECS on EC2 server setup without a load balancer.
ECS may often seem expensive when used for personal development purposes, due to the cost of the load balancer. The application load balancer is a great service that is easy to set up managed ACM certificates, easy scaling, and has dynamic port mappings..., but it is over-featured for running 1 ECS task.
However, to run an ECS server without a load balancer, you need to associate an Elastic IP to the host instance and install your certificate to your service every time you start up the server. This construct aims to automate these works and make it easy to deploy resources to run a low-cost ECS server.
The easiest way to try the construct is to clone this repository and deploy the sample Nginx server.
Edit settings in examples/minimum.ts
and deploy the cdk construct. Public hosted zone is required.
- Clone and install packages
git clone https://github.com/rajyan/low-cost-ecs.git
yarn install
-
Edit email and domain in example.ts
low-cost-ecs/examples/minimum.ts
Lines 1 to 15 in 3d1bbf7
-
Deploy!
cdk deploy
Access the configured hostedZoneDomain
and see that the sample Nginx server has been deployed.
To use this construct in your cdk stack as a library,
npm install low-cost-ecs
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { LowCostECS } from 'low-cost-ecs';
class SampleStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const vpc = { /** Your VPC */ };
const securityGroup = { /** Your security group */ };
const serverTaskDefinition = { /** Your task definition */ };
new LowCostECS(this, 'LowCostECS', {
hostedZoneDomain: "example.com",
email: "test@example.com",
vpc: vpc,
securityGroup: securityGroup,
serverTaskDefinition: serverTaskDefinition
});
}
}
The required fields are hostedZoneDomain
and email
.
You can configure your server task definition and other props. Read LowCostECSProps
documentation for details.
Resources generated in this stack
- Route53 A record
- Forwarding to host instance Elastic IP
- Certificate State Machine
- Install and renew certificates to EFS using certbot-dns-route53
- Scheduled automated renewal every 60 days
- Email notification on certbot task failure
- ECS on EC2 host instance
- ECS-optimized Amazon Linux 2 AMI instance auto-scaling group
- Automatically associated with Elastic IP on instance initialization
- ECS Service
- TLS/SSL certificate installation before default container startup
- Certificate EFS mounted on default container as
/etc/letsencrypt
- Others
- VPC with only public subnets (no NAT Gateways to decrease cost)
- Security groups with minimum inbounds
- IAM roles with minimum privileges
All resources except Route53 HostedZone should be included in AWS Free Tier
if you are in the 12 Months Free period.
After your 12 Months Free period, setting hostInstanceSpotPrice
to use spot instances is recommended.
- EC2
- t2.micro 750 instance hours (12 Months Free Tier)
- 30GB EBS volume (12 Months Free Tier)
- ECS
- No additional charge because using ECS on EC2
- EFS
- Usage is very small, it should be free
- Cloud Watch
- Usage is very small, and it should be included in the free tier
- Enabling
containerInsights
will charge for custom metrics
- SSM Session Manager
SSM manager is pre-installed in the host instance (by ECS-optimized Amazon Linux 2 AMI) and AmazonSSMManagedInstanceCore
is added to the host instance role to access and debug in your host instance.
aws ssm start-session --target $INSTANCE_ID
- ECS Exec
Service ECS Exec is enabled, so execute commands can be used to debug your server task container.
aws ecs execute-command \
--cluster $CLUSTER_ID \
--task $TASK_ID \
--container nginx \
--command bash \
--interactive
Because the ECS service occupies a host port, only one task can be executed at a time. The old task must be terminated before the new task launches, and this causes downtime on release.
Also, if you make changes that require recreating the service, you may need to manually terminate the task of the old service.
import { LowCostECS } from 'low-cost-ecs'
new LowCostECS(scope: Construct, id: string, props: LowCostECSProps)
Name | Type | Description |
---|---|---|
scope |
constructs.Construct |
No description. |
id |
string |
No description. |
props |
LowCostECSProps |
No description. |
- Type: constructs.Construct
- Type: string
- Type: LowCostECSProps
Name | Description |
---|---|
toString |
Returns a string representation of this construct. |
public toString(): string
Returns a string representation of this construct.
Name | Description |
---|---|
isConstruct |
Checks if x is a construct. |
import { LowCostECS } from 'low-cost-ecs'
LowCostECS.isConstruct(x: any)
Checks if x
is a construct.
- Type: any
Any object.
Name | Type | Description |
---|---|---|
node |
constructs.Node |
The tree node. |
certFileSystem |
aws-cdk-lib.aws_efs.FileSystem |
EFS file system that the SSL/TLS certificates are installed. |
cluster |
aws-cdk-lib.aws_ecs.Cluster |
ECS cluster created in configured VPC. |
hostAutoScalingGroup |
aws-cdk-lib.aws_autoscaling.AutoScalingGroup |
ECS on EC2 service host instance autoscaling group. |
serverTaskDefinition |
aws-cdk-lib.aws_ecs.Ec2TaskDefinition |
Server task definition generated from LowCostECSTaskDefinitionOptions. |
service |
aws-cdk-lib.aws_ecs.Ec2Service |
ECS service of the server with desiredCount: 1, minHealthyPercent: 0, maxHealthyPercent: 100. |
topic |
aws-cdk-lib.aws_sns.Topic |
SNS topic used to notify certbot renewal failure. |
public readonly node: Node;
- Type: constructs.Node
The tree node.
public readonly certFileSystem: FileSystem;
- Type: aws-cdk-lib.aws_efs.FileSystem
EFS file system that the SSL/TLS certificates are installed.
public readonly cluster: Cluster;
- Type: aws-cdk-lib.aws_ecs.Cluster
ECS cluster created in configured VPC.
public readonly hostAutoScalingGroup: AutoScalingGroup;
- Type: aws-cdk-lib.aws_autoscaling.AutoScalingGroup
ECS on EC2 service host instance autoscaling group.
public readonly serverTaskDefinition: Ec2TaskDefinition;
- Type: aws-cdk-lib.aws_ecs.Ec2TaskDefinition
Server task definition generated from LowCostECSTaskDefinitionOptions.
public readonly service: Ec2Service;
- Type: aws-cdk-lib.aws_ecs.Ec2Service
ECS service of the server with desiredCount: 1, minHealthyPercent: 0, maxHealthyPercent: 100.
public readonly topic: Topic;
- Type: aws-cdk-lib.aws_sns.Topic
SNS topic used to notify certbot renewal failure.
import { LowCostECSProps } from 'low-cost-ecs'
const lowCostECSProps: LowCostECSProps = { ... }
Name | Type | Description |
---|---|---|
email |
string |
Email for expiration emails to register to your let's encrypt account. |
hostedZoneDomain |
string |
Domain name of the hosted zone. |
awsCliDockerTag |
string |
Docker image tag of amazon/aws-cli. |
certbotDockerTag |
string |
Docker image tag of certbot/dns-route53 to create certificates. |
certbotScheduleInterval |
number |
Certbot task schedule interval in days to renew the certificate. |
containerInsights |
boolean |
Enable container insights or not. |
hostInstanceSpotPrice |
string |
The maximum hourly price (in USD) to be paid for any Spot Instance launched to fulfill the request. |
hostInstanceType |
string |
Instance type of the ECS host instance. |
logGroup |
aws-cdk-lib.aws_logs.ILogGroup |
Log group of the certbot task and the aws-cli task. |
recordDomainNames |
string[] |
Domain names for A records to elastic ip of ECS host instance. |
removalPolicy |
aws-cdk-lib.RemovalPolicy |
Removal policy for the file system and log group (if using default). |
securityGroups |
aws-cdk-lib.aws_ec2.ISecurityGroup[] |
Security group of the ECS host instance. |
serverTaskDefinition |
LowCostECSTaskDefinitionOptions |
Task definition for the server ecs task. |
vpc |
aws-cdk-lib.aws_ec2.IVpc |
VPC of the ECS cluster and EFS file system. |
public readonly email: string;
- Type: string
Email for expiration emails to register to your let's encrypt account.
https://docs.aws.amazon.com/sns/latest/dg/sns-email-notifications.html
public readonly hostedZoneDomain: string;
- Type: string
Domain name of the hosted zone.
public readonly awsCliDockerTag: string;
- Type: string
- Default: latest
Docker image tag of amazon/aws-cli.
This image is used to associate elastic ip on host instance startup, and run certbot cfn on ecs container startup.
public readonly certbotDockerTag: string;
- Type: string
- Default: v1.29.0
Docker image tag of certbot/dns-route53 to create certificates.
public readonly certbotScheduleInterval: number;
- Type: number
- Default: 60
Certbot task schedule interval in days to renew the certificate.
public readonly containerInsights: boolean;
- Type: boolean
- Default: undefined (container insights disabled)
Enable container insights or not.
public readonly hostInstanceSpotPrice: string;
- Type: string
- Default: undefined
The maximum hourly price (in USD) to be paid for any Spot Instance launched to fulfill the request.
Host instance asg would use spot instances if hostInstanceSpotPrice is set.
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs.AddCapacityOptions.html#spotprice
public readonly hostInstanceType: string;
- Type: string
- Default: t2.micro
Instance type of the ECS host instance.
public readonly logGroup: ILogGroup;
- Type: aws-cdk-lib.aws_logs.ILogGroup
- Default: Creates default cdk log group
Log group of the certbot task and the aws-cli task.
public readonly recordDomainNames: string[];
- Type: string[]
- Default: [ props.hostedZone.zoneName ]
Domain names for A records to elastic ip of ECS host instance.
public readonly removalPolicy: RemovalPolicy;
- Type: aws-cdk-lib.RemovalPolicy
- Default: RemovalPolicy.DESTROY
Removal policy for the file system and log group (if using default).
public readonly securityGroups: ISecurityGroup[];
- Type: aws-cdk-lib.aws_ec2.ISecurityGroup[]
- Default: Creates security group with allowAllOutbound and ingress rule (ipv4, ipv6) => (tcp 80, 443).
Security group of the ECS host instance.
public readonly serverTaskDefinition: LowCostECSTaskDefinitionOptions;
- Type: LowCostECSTaskDefinitionOptions
- Default: Nginx server task definition defined in sampleTaskDefinition()
Task definition for the server ecs task.
public readonly vpc: IVpc;
- Type: aws-cdk-lib.aws_ec2.IVpc
- Default: Creates vpc with only public subnets and no NAT gateways.
VPC of the ECS cluster and EFS file system.
import { LowCostECSTaskDefinitionOptions } from 'low-cost-ecs'
const lowCostECSTaskDefinitionOptions: LowCostECSTaskDefinitionOptions = { ... }
Name | Type | Description |
---|---|---|
containers |
aws-cdk-lib.aws_ecs.ContainerDefinitionOptions[] |
No description. |
taskDefinition |
aws-cdk-lib.aws_ecs.Ec2TaskDefinitionProps |
No description. |
volumes |
aws-cdk-lib.aws_ecs.Volume[] |
No description. |
public readonly containers: ContainerDefinitionOptions[];
- Type: aws-cdk-lib.aws_ecs.ContainerDefinitionOptions[]
public readonly taskDefinition: Ec2TaskDefinitionProps;
- Type: aws-cdk-lib.aws_ecs.Ec2TaskDefinitionProps
public readonly volumes: Volume[];
- Type: aws-cdk-lib.aws_ecs.Volume[]