How to Autoscale Node.js Image Transformations Using Sharp and Express
Tags: cloud, node, devops
Published: 2022-10-13 09:42:57

Automation of application scaling is one of the key benefits of PaaS. The following example shows how you can use this ability in [Zerops](https://contabo.com/en/zerops/), a developer-first app platform – specifically, when transforming images by scaling or rotating them using [Node.js](https://nodejs.org/).

_Note: Please be aware that the example code is not meant to have production code quality. The purpose is to demonstrate the scaling feature of [Zerops](https://contabo.com/en/zerops/). In the following, we assume that you have a [Zerops](https://contabo.com/en/zerops/) account and an installed `zcli` (Zerops command line tool) available._

## Writing your code

The code is fairly simple. It uses [Express](https://expressjs.com/) and [Sharp](https://sharp.pixelplumbing.com/) to perform the actual work. Thus please execute the following in a folder of your choice:

```sh
npm i express sharp
```

This will install all dependencies in a folder `node_modules` and more importantly create files `package.json` and `package-lock.json`.

Then please create the file `index.js` and insert the following code:

```js
const sharp = require("sharp")
const express = require('express')
const expressServer = express()

expressServer.get('/image', (request, response) => {

  const requestedWidth = request.query.width ? parseInt(request.query.width) : 300;
  const requestedHeight = request.query.height ? parseInt(request.query.height) : 300;
  const requestedAngle = request.query.angle ? parseInt(request.query.angle) : 0;
  const format = 'jpg';

  console.log(`Handling image: size to ${requestedWidth}x${requestedHeight} and rotation to ${requestedAngle}`)

  sharp('example.jpg')
    .resize(requestedWidth, requestedHeight, {
      fit: sharp.fit.inside,
    })
    .rotate(requestedAngle)
    .toFormat(format)
    .toBuffer()
    .then(function (outputBuffer) {
      response.type(format);
      response.end(outputBuffer)
    });
})

expressServer.listen(3000, () => {
  console.log(`Listening on port 3000.`)
})
```

You can use any jpg image as `example.jpg`. Please put one in that folder.
That's it. Just regular code (and an image).

## Setting up the project in Zerops

Once you have the code ready, you need to create a project in [Zerops](https://contabo.com/en/zerops/). For that purpose, please:

1. login to [Zerops](https://contabo.com/en/zerops/)
2. create a project by clicking on `Add new project`
3. specify a project name e.g. `image`
4. choose `Node.js` as service to be added
5. click `Set to high availability?`
6. click add

This sets your Node.js runtime to high availability meaning it will perform vertical and horizontal scaling automatically. Vertical scaling means simply adjusting the amount of resources in your container whereas horizontal scaling adjusts the number of containers. You can read more about it [here](https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di).

## Build and deploy on Zerops

The following steps describe how you can build and deploy the example as a [Zerops](https://contabo.com/en/zerops/) application.

* For [Zerops](https://contabo.com/en/zerops/) to know which files to use please also create a `zerops.yml` file next the other files:

```yaml
nodejs0:
  build:
    base: [ nodejs@16 ]
    build:
      - npm ci
    cache: [ node_modules ]
    deploy: [ index.js, example.jpg, node_modules ]
  run:
    start: node index.js
```

* Simply deploy your file

```sh
zcli push "image" nodejs0
```

* Enable Zerops subdomain access via `Public access & internal ports` on the `nodejs0` service and invoke the pre-generated URL in your browser.

## Autoscaling in action

So far we deployed the code only without seeing the autoscaling in action. For that purpose we will use [k6](https://k6.io/) to generate some load.

No worries, [Zerops](https://contabo.com/en/zerops/) won't scale to infinity without keeping your expenses under control. You can adjust the maximum and minimum resources for both vertical and horizontal scaling easily every time you see fit. [Zerops](https://contabo.com/en/zerops/) will do the rest.

Here the script to generate some load `k6.js`:

```js
import http from 'k6/http';
import { check } from 'k6';

export default function () {
  const baseUrl = "https://<your-generated-subdomain>.usc1.contabozerops.com"
  const width = 300 + Math.random() * 1000
  const height = 300 + Math.random() * 1000
  const angle = Math.random() * 360
  const res = http.get(`${baseUrl}/image?width=${width}&height=${height}&angle=${angle}`)
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
}
```

And then run it by invoking

```sh
k6 run --vus 100 --duration 120s k6.js
```

This will simulate a 100 users requesting the image service as fast as possible during 120 seconds.

Our example is mainly a CPU-intensive task so you will see the number of vCPUs (virtual CPUs) rise -- vertical scaling -- and after a short time also see more containers being created -- horizontal scaling. Please refer to the following screenshot where you see many vCPUs and 4 containers.
![Zerops Scaling up -- vertically and horizontally](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptwj2se59l0pytylpyag.png)

After the k6 script finished generating the load, [Zerops](https://contabo.com/en/zerops/) will wait for a moment to make sure the demand has really gone down. Then you'll first see the vertical autoscaling taking action by reducing vCPUs (due to the nature of our example). The number of vCPUs has dramatically decreased.
![Zerops Scaling -- vertical cool down](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98somrt610p2umi9ofh6.png)

After a while, [Zerops](https://contabo.com/en/zerops/) will perform the horizontal autoscaling as well. As the k6 script finished generating load and we don't have any more coming, the starting point with two containers will be reached as shown in the screenshot.
![Zerops Scaling -- horizontal cool down](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xemsx4bslozny1sax61c.png)

## Summary

[Zerops](https://contabo.com/en/zerops/) is performing both vertical and horizontal autoscaling out of the box with a single click. Just like shown in the example above. No need to have a special code or knowledge. It both saves money and gives you more power when required.

For a deeper dive into what’s happening behind the scenes of Zerops autoscaling, check out [my previous article](https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di).