From AWS EC2 to Severless :How I divided my bills by 12!
Tags: aws, architecture, programming, devops
Published: 2023-12-28 15:42:14

When it comes to Cloud Computing, we can hear a lot of fantasies here and there about costs. If you only lift and shift to the Cloud, the chances to save money are unlikely. On the contrary, it might be more expansive because you would probably improve your SLA by design.

So, the real power of the cloud, cost-wise, lies more in replatforming and refactoring.

In this article, we will see together how I redesigned and refactored a 3-tier blog application into a serverless one to see how I divided my cloud bills by 12 !

## Initial stack

### Architecture

The initial architecture of our blog is a common 3-tier with the web tier, application tier and database tier.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axr8a5hpr8etolzadhzb.png)

- **Web server :** host the frontend
- **App server :** host the backend handling the REST requests to execute business logic code and interacting with the database
- **Database :** host the RDBMS (PotgreSql here) to read/write data

### Features

The application is not that complex and only do few actions :
- GET    /personalblog/api/articles
- POST   /personalblog/api/articles
- GET    /personalblog/api/articles/{articleId}
- PUT    /personalblog/api/articles/{articleId}
- DELETE /personalblog/api/articles/{articleId}

The OAS contract is defined below :

```yaml
openapi: 3.0.0
info:
  title: Blog API
  version: 1.0.0
  description: API contract for the core features of a blog.

servers:
  - url: https://api.example.com/v1

paths:
  /articles:
    get:
      summary: Retrieve the list of articles
      responses:
        '200':
          description: List of articles retrieved successfully
          content:
            application/json:
              example:
                articles:
                  - id: 1
                    title: "First article"
                    content: "Content of the article."
                  - id: 2
                    title: "Second article"
                    content: "Content of the article."

    post:
      summary: Create a new article
      requestBody:
        required: true
        content:
          application/json:
            example:
              title: "New article"
              content: "Content of the new article."
      responses:
        '201':
          description: Article created successfully
          content:
            application/json:
              example:
                id: 3
                title: "New article"
                content: "Content of the new article."

  /articles/{articleId}:
    parameters:
      - name: articleId
        in: path
        required: true
        description: ID of the article
        schema:
          type: integer
    get:
      summary: Retrieve details of an article
      responses:
        '200':
          description: Article details retrieved successfully
          content:
            application/json:
              example:
                id: 1
                title: "First article"
                content: "Content of the article."

    put:
      summary: Update an existing article
      requestBody:
        required: true
        content:
          application/json:
            example:
              title: "Updated article"
              content: "New content of the article."
      responses:
        '200':
          description: Article updated successfully
          content:
            application/json:
              example:
                id: 1
                title: "Updated article"
                content: "New content of the article."

    delete:
      summary: Delete an article
      responses:
        '204':
          description: Article deleted successfully
```
### Technologies

The application is mainly coded with Typescript with :
- Angular for the frontend
- NestJS for the Backend
- PostgreSQL for the database

### Infrastructure

The application is hosted with :
- 1 EC2 t3.small (2vCPU/2GB RAM) for the frontend.
- 1 EC2 t3.large (2vCPU/8GB RAM) for the backend : I could have used a t3.small but I wanted to add observability agents and some other stuff on the server.
- 1 Aurora for PostgreSql Serverless instance : this is the lowest price I could get with PostgreSql. EC2, RDS or Aurora provisioned would have been very much expansive.

### Infrastructure cost

So, with this configuration, I had around 100$ of monthly cost for the infrastructure.

![lift and shift cost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhi65jg6oyn91r2p1u73.png)

Let's see now how we can replatform all of this with serverless and what needs to be refactored.

## Serverless stack

### Architecture

Here is our new serverless architecture for the exact same needs :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwickojr11pe5qzvfzag.png)

- S3 Webhosting : to host the Angular frontend
- API Gateway : to handle REST requests from the frontend and trigger lambda functions
- Lambda functions : to execute the application logic with Typescript
- DynamoDB : to read/write data

### Refactoring

To be able to deploy our application to this new architecture, here is what we have to do :
- Infrastucture as code : I used Terraform because the old architecture was made with it. But I could have done it with AWS SAM too.
- Frontend : nothing changed. I was still delivering the `dist` folder of Angular.
- Backend : I have split our code in functions and have rewrite the SQL transactions into DynamoDB API calls with AWS SDK.
- IAM : I created the right AWS roles and permissions to be able to make the AWS services to communicate properly.
- CI/CD : this part was time consuming since lambdas do not detect code changes when delivering an unchanged configuration. I had to upload lambda's code into s3 with event triggering to update the code inside the lambda.

All these changes took about 5 days of work to get the things done right.

### Infrastructure cost

The final infrastructure cost after all of this replatforming and refactoring was around 7.65$ per month !

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm60qyo2c3fm0gj5nopy.png)

So I divided my bill by around 12 !

### Other benefits

The whole stack is now scalable and highly available by design. With the old stack, I would have need Load Balancing and multiple EC2 in multiple AZ. It would have doubled my monthly cost to achieve such a goal.

### Drawbacks

There are few drawbacks but they can be blockers or not considering your own context :
- We had to invest some time in code refactoring
- Lambda's are considered as vendor lock in. There are some alternatives like K-Native or OpenFaaS but I must admit that it implies changes.
- Cold start : if your lambdas are not invoked at least every hour, they may have a cold start with lower performances. There are many articles talking about this topic and how to avoid it. So you just have to be aware of how to avoid it.

## Conclusion

Finally, we've seen that the major savings to be made in the Cloud come after the migration with replatforming and refactoring. It comes with an invest but it can bring you high rewards.