codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

Follow publication

Heroku v.s. AWS Elastic Beanstalk

--

We deploy a web application to Heroku and to AWS Elastic Beanstalk in production configurations; contrasting their implementations and pricing.

note: This is not to say that AWS Elastic Beanstalk is the only comparable way to run workloads in AWS; it, however, is the one that most closely resembles Heroku.

TL;DR: The 30-thousand-foot comparison of these, Heroku and AWS Elastic Beanstalk, solutions (production configurations) is that they have comparable features and pricing. One big difference is that Heroku’s pricing takes exponential price jumps as one adds common additional features, e.g., auto-scaling, where-as AWS pricing is fairly linear. On the other hand, Heroku is generally simpler to get up and running as AWS has a fairly steep initial learning curve.

The Application

The web application is a minimal Node.js application that depends on a PostgreSQL database. Using two environment variables:

  • DATABASE_URL: PostgreSQL Connection URL
  • PORT: HTTP listener port

and exposing two endpoints:

  • /: responds with Hello World!
  • /db: Executes a PostgreSQL query and responds with result; Hello db!

Heroku Application

Following the instructions, Preparing a Codebase for Heroku Deployment and Deploying Node.js Apps on Heroku, we start by adding the engines property to package.json; specifying the Node.js version to use:

We also create a file, Procfile, to specify how to start the Dynos:

Finally, we push the application to a Heroku App configured with a Heroku Postgres resource.

The Heroku components and pricing is:

  • Heroku Standard Support (business hours, 1 day response): No Cost
  • 2 x Professional Standard 1X Dynos (512 MB memory): $25 / Dyno / month
  • Premium-0 Heroku Postgres (4 GB memory, 64 GB storage): $200 / month

Thus the total monthly cost is $250.

A network diagram illustrating the components and a representative communication flow:

note: Blue components are associated with the Heroku App. Dashed components in the diagram are those that are managed by Heroku behind the scene.

  1. Browser (Br.) makes request to the Load Balancer (LB) in an Availability Zone (AZ). Availability Zones are isolated logical data centers
  2. Load Balancer (LB) routes the request to one of the Dynos managed by a horizontal scaling group (H. Scaling). Dynos are randomly distributed across Availability Zones
  3. Dyno executes a query against a database on the PostgreSQL primary instance (PG P.). The PostgreSQL cluster (PG) has a standby instance (PG S.) in a different Availability Zone

Observations:

  • Heroku automatically manages, e.g, performs upgrades for, the Dynos and Heroku Postgres resources
  • Heroku automatically manages the DATABASE_URL and PORT environment variables
  • Professional level Dynos are required to enable horizontal scaling and thus high availability (or something approximating it)

The Heroku Platform is spread across multiple AWS availability zones and application dynos are provisioned automatically so there is no guarantee that your dynos will be evenly balanced across availability zones (it is effectively random allocation).

Basically, the dynos themselves can bounce between AZ’s but if an AZ becomes unavailable, the affected dynos would be started another AZ.

— Heroku — How do multi-availability zones work on Heroku?

  • Premium level Heroku Postgres is required for high availability

For premium tier database plans, the failover database is in a separate availability zone automatically.

— Heroku — How do multi-availability zones work on Heroku?

  • The final and important observation is that the PostgreSQL instances are publicly available; secured using SSL and an username / password pair

note: The application adapted to Heroku is available in the heroku branch of the larkintuckerllc / heroku-aws GitHub repository.

AWS Elastic Beanstalk Application: Create Application

Because we are deploying a production configuration, we first need to follow the instructions in Using Elastic Beanstalk with Amazon VPC to create a Public / Private VPC. Because the instructions provide an AWS CloudFormation template, this is close to a one-click experience.

Next we need to create the PostgreSQL database using AWS Relational Database Service (RDS). We start by creating a RDS Subnet Group consisting of the two private Subnets created in the previous step. We create the Database using the following (non-default) parameters:

  • Standard Create
  • Amazon Aurora
  • Amazon Aurora with PostgreSQL compatibility
  • Templates: Production
  • DB instance size: db.t3.medium
  • VPC: The VPC from the first step
  • Subnet group: The Subnet Group from the previous step
  • VPC security group: Create new

note: We also setup an Aurora replica instance in a separate availability zone as the primary instance.

Next we create the AWS Elastic Beanstalk Application using the following (non-default) parameters:

  • Platform: Node.js
  • Application code: Leave the Sample application for now
  • Capacity > Environment type: Load Balanced (Min and Max: 2)
  • Network > VPC: The VPC from the first step
  • Network > Load Balancer subnets: The two public Subnets from the first step
  • Network > Instance subnets: The two private Subnets from the first step

Before deploying our code, let us first review the AWS components and pricing:

  • AWS Developer Support Plan (business hours, 1 day response): $29 / month
  • VPC with its Subnets, Security Groups, Internet Gateway: No Cost
  • VPC NAT Gateway: $0.045 / hour + Data (~$30 / month)
  • Aurora Database Instances: 2 x db.t3.medum (4 GB memory): $0.082 / hour (2 x $60 = $120 / month)
  • Aurora Database Storage and IOs: $0.10 / GB / month + IO (~$6 / month)
  • Load Balancer: Application Load Balancer: $0.0225 / hour + Data (~$17 / month)
  • EC2 Auto Scaling Group (ASG): No Cost
  • EC2: 2 x t2-micro (1 CPU, 1 GB memory): $0.0116 / hour ( 2 x $9 = $18 / month)
  • EC2 Storage: 2 x EBS gp2 8 GB: $0.10 / GB / month (2 x $1 = $2 / month)

note: Generally the Data and IO charges are minimal under most circumstances.

Thus the total monthly cost is approximately $222.

A network diagram illustrating the components and a representative communication flow:

note: Orange components are associated with the Aurora Database. Blue components are associated with the Elastic Beanstalk Application. Dashed components in the diagram are those that are managed by AWS behind the scene.

  1. Browser (Br.) makes a request to the Load Balancer (LB) in a public Subnet (SN)
  2. Load Balancer (LB) routes the request to one of the EC2 Instances (EC2) in a private Subnet (SN) managed by an Auto Scaling Group (ASG). EC2 Instances are evenly distributed across Availability Zones (in their respective private Subnets)
  3. EC2 Instance executes a query against a database on the PostgreSQL Writer Instance (PG W.) in a private Subnet (SN). There is also a PostgreSQL Reader Instance (PG R.) in a private Subnet in a different Availability Zone

Observations:

  • AWS automatically manages, e.g, performs upgrades for, the PostgreSQL and EC2 Instances
  • AWS Elastic Beanstalk automatically manages the PORT environment variable
  • The final and important observation is that the EC2 and PostgreSQL Instances are in private Subnets and further secured using Security Groups. The PostgreSQL Instances are also secured using SSL and a username / password pair

AWS Elastic Beanstalk Application: Deployment

We roughly follow the instruction, Launching and connecting to an external Amazon RDS instance in a default VPC, and first update the RDS Instances’ Security Group to allow inbound PostgreSQL traffic from itself.

We then add the RDS Instances’ Security Group to the Elastic Beanstalk’s EC2 Instance configuration.

The last step in the instructions is to add database connection information to the Elastic Beanstalk’s software configuration environment variables. For our application, we will need to add a DATABASE_URL environment variable with the PostgreSQL Connection URL, e.g,:

postgres://USERNAME:PASSWORD@INSTANCE/DATABASE

Parameters:

  • USERNAME: The username provided when creating database
  • PASSWORD: The password provided when creating database
  • INSTANCE: The Database Writer endpoint
  • DATABASE: The same as USERNAME

Roughly following the instructions, Deploying Node.js applications to Elastic Beanstalk, we add a start script to package.json.

note: It is important to observe, that we do not do the Heroku steps here; i.e., engines in package.json and creating Procfile.

We then zip the application files, excluding the GIT and Node.js modules files, with:

zip -r application.zip . -x *.git* node_modules/\*

Finally, we upload it to the Elastic Beanstalk application (technically its single Environment).

note: The application adapted to AWS Elastic Beanstalk is available in the aws branch of the larkintuckerllc / heroku-aws GitHub repository.

Contrasting Solutions

The first observation is that these solutions are roughly comparable with the same cost; $250 (Heroku) v.s. $222 (AWS Elastic Beanstalk) per month.

The next observation is that the AWS Elastic Beanstalk solution’s PostgreSQL Instances, being in private Subnets with restricted Security Groups, are more secure than the publicly available PostgreSQL Instances in the Heroku solution.

Heroku’s Private Spaces feature can be used to address this issue; the problem though is that this feature is only available with custom pricing (assuming going to be expensive).

Another observation is that Heroku’s Dyno high availability, by randomly distributing across Availability Zones, is less robust than the AWS Elastic Beanstalk’s (actually powered by an EC2 Auto Scaling Group) active distribution across Availability Zones.

While we did not explore AWS Elastic Beanstalk’s Auto Scaling Group feature, it is available at no cost. For Heroku, however:

Autoscaling is currently available only for Performance-tier dynos and dynos running in Private Spaces.

— Heroku — Scaling Your Dyno Formation

The price of a Performance-tier Dyno is $250 / Dyno / month and as we saw earlier Private Spaces is only available with custom pricing. This is exponentially more than the $9 / Instance / month for AWS Elastic Beanstalk.

With Heroku’s simple pricing model one often has to over-provision features for a particular result.

For example, we used Premium-0 Heroku Postgres (4 GB memory, 64 GB storage) in the Heroku solution for $200 / month. In order to get more storage, we have to jump to Premium-2 Heroku Postgres (8 GB memory, 256 GB) for $350 / month (an extra $150 / month). Because AWS decouples the Aurora Database Instance from the Storage, we can scale them independently; at $0.10 / GB / month, getting to 256 GB (from 64 GB) only costs an additional $19 / month.

The last, obvious, observation is that working with Heroku is generally simpler to get up and running with as compared to AWS. At the same time, once one already tackled the fairly steep initial AWS learning curve, incremental learning AWS features is relatively easy (features build off the same core functionality).

Wrap Up

The 30-thousand-foot comparison of these, Heroku and AWS Elastic Beanstalk, solutions (production configurations) is that they have comparable features and pricing. One big difference is that Heroku’s pricing takes exponential price jumps as one adds common additional features, e.g., auto-scaling, where-as AWS pricing is fairly linear. On the other hand, Heroku is generally simpler to get up and running as AWS has a fairly steep initial learning curve.

--

--

Published in codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

Written by John Tucker

Broad infrastructure, development, and soft-skill background

Responses (1)