DEV Community

Marcos Henrique for AWS Community Builders

Posted on • Updated on

Autoscaling using Spot Instances with AWS CDK + TS

Suppose you’re a developer (like me xD). In that case, you know the value of running applications in the cloud—the ability to scale up and down easily, test your app anywhere, and pay for resources as needed.

AWS EC2 Spot Instances are the best of both worlds—they're just like regular EC2 instances, except you don't need to pay for them upfront, and they're cheaper than regular instances.

The main advantage to spot instances is that you only pay when you use them (rather than paying an upfront fee). But with spot instances, you also get more flexibility—you can move them around pretty easily, which means they work out as a great option for short-term projects or testing. In general, if your project isn't going to last longer than a few weeks or months, and you're not concerned with having access to all the features of your regular instance type (such as the ability to scale it up), then this is probably the best way to go!

If you want to know more about spot instances please take a look at this awesome blog post.

Let's get your hands dirty!

Our Architecture:

a vpc with ALB and autoscaling with spot instances

VPC

Let's start creating our vpc, piece of cake public and private subnets.

import { IVpc, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
import { Construct } from "constructs";

export default class DefaultVpc extends Construct {
  public readonly vpc: IVpc;
  constructor(scope: Construct, id: string) {
    super(scope, id);
    this.vpc = new Vpc(this, "my-vpc", {
      cidr: "10.0.0.1/24",
      subnetConfiguration: [
        {
          cidrMask: 28,
          name: "public subnet",
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 28,
          name: "private subnet",
          subnetType: SubnetType.PRIVATE_WITH_NAT,
        },
      ],
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Application Load Balancer

In this step we need to create an internet facing load balancer

import { AutoScalingGroup } from "aws-cdk-lib/aws-autoscaling";
import { IVpc } from "aws-cdk-lib/aws-ec2";
import { ApplicationLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancingv2";
import { Construct } from "constructs";

export default class InternetFacingApplicationLoadBalancer extends Construct {
  constructor(scope: Construct, id: string, resources: { vpc: IVpc, ec2AutoScalingGroup: AutoScalingGroup }) {
    super(scope, id);

    const loadBalancer = new ApplicationLoadBalancer(this, "appLoadBalancer", {
      vpc: resources.vpc,
      internetFacing: true,
    });

    const httpListener = loadBalancer.addListener("httpListener", {
      port: 80,
      open: true,
    });

    httpListener.addTargets('ApplicationSpotFleet', {
        port: 8080,
        targets: [resources.ec2AutoScalingGroup],
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

AutoScaling Group

Now we create the autoscaling, ec2 instances and the wonderful decision to use spot instances.
We are using t4g.micro instances with latest Amazon Linux images. We are allowing all outbound traffic; the desired number of instances is one, while the maximum is 2.
Maximum spot price is set to 0.007, if the sport price goes up, for example to 0.008, then we have no instances. There are ways to address this, but for this example I'll only use it in this way.

import { Duration } from "aws-cdk-lib";
import { AutoScalingGroup, HealthCheck } from "aws-cdk-lib/aws-autoscaling";
import {
  AmazonLinuxGeneration,
  AmazonLinuxImage,
  InstanceClass,
  InstanceSize,
  InstanceType,
  IVpc,
} from "aws-cdk-lib/aws-ec2";
import { Construct } from "constructs";

export default class ApplicationAutoScalingGroup extends Construct {
  public readonly autoScalingGroup: AutoScalingGroup;
  constructor(
    scope: Construct,
    id: string,
    resources: { vpc: IVpc }
  ) {
    super(scope, id);

    const applicationAutoScalingGroup = new AutoScalingGroup(this, "AutoScalingGroup", {
      vpc: resources.vpc,
      instanceType: InstanceType.of(
        InstanceClass.BURSTABLE4_GRAVITON,
        InstanceSize.MICRO
      ),
      machineImage: new AmazonLinuxImage({
        generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
      }),
      allowAllOutbound: true,
      maxCapacity: 2,
      minCapacity: 1,
      desiredCapacity: 1,
      spotPrice: "0.007", // $0.0032 per Hour when writing, $0.0084 per Hour on-demand
      healthCheck: HealthCheck.ec2(),
    });

    applicationAutoScalingGroup.scaleOnCpuUtilization("CpuScaling", {
        targetUtilizationPercent: 50,
        cooldown: Duration.minutes(1),
        estimatedInstanceWarmup: Duration.minutes(1),
    });

    this.autoScalingGroup = applicationAutoScalingGroup;
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally our stack will be something like that:

Stack

import { StackProps, Environment, Stack } from 'aws-cdk-lib'
import { Construct } from 'constructs'
import InternetFacingApplicationLoadBalancer from '../lib/application-load-balancer/internet-facing-application-load-balancer'
import ApplicationAutoScalingGroup from '../lib/ec2/auto-scaling-group'
import DefaultVpc from '../lib/vpc/default-vpc'

export interface IStackProps extends StackProps {
  variables?: any
  env: Environment
}

export class ApplicationIntegrationStack extends Stack {
  constructor(scope: Construct, id: string, props: IStackProps) {
    super(scope, id, props)

    const { vpc } = new DefaultVpc(this, 'DefaultVpc')
    const { autoScalingGroup } = new ApplicationAutoScalingGroup(this, 'ApplicationAutoScalingGroup', { vpc })
    new InternetFacingApplicationLoadBalancer(this, 'InternetFacingApplicationLoadBalancer', { vpc, ec2AutoScalingGroup: autoScalingGroup })
  }
}
Enter fullscreen mode Exit fullscreen mode

If you want to take a deep dive into this topic I strongly recommend this re:invent video about spot instances.

That's all folks!

Top comments (4)

Collapse
 
didof profile image
Francesco Di Donato

Just found your posts man. Amazing work, I will definitely take a look in the upcoming days! Cheers

Collapse
 
wakeupmh profile image
Marcos Henrique

thanks for your words mate, I really appreciate this!
your blog posts about caching helped me a lot

Collapse
 
gabrielterriaga profile image
Gabriel Terriaga

Amazing bro!!

Collapse
 
wakeupmh profile image
Marcos Henrique

thaaaaanks my bro