By James Hall •

Tutorial: Serverless Scheduled Tasks

Tutorial: Serverless Scheduled Tasks

Level: Beginner / Intermediate

Not all logic in web applications needs to be executed when the user hits the page. If you’d like to execute a task in your web application on a recurring basis, e.g. cancellation emails, recurring billing & archiving files, then you might want to use a ‘crontab’ – they’re invaluable for scheduling tasks.

In this tutorial we’ll be writing and deploying a function that runs every minute, without the need to provision a server. We’ll be using Node.js, AWS Lambda, CloudWatch Events, and orchestrate them using the Serverless Framework. You’ll need either a Mac, or a Linux machine, or even a Windows machine if you have bash installed.

What is Lambda?

Lambda is Amazon’s FaaS (Function-as-a-Service) offering. Code is sent to Amazon and it is executed when triggered. You’re billed in 100ms increments, and you get 1 million requests completely free each month. Triggers can be many different things, including HTTP requests (via API Gateway), S3 file events, and – as we’ll see in this example – scheduled CloudWatch Events.

Getting started

It’s assumed you have a basic understanding of the web to follow this tutorial. If you’re unfamiliar with Node.js or AWS, don’t worry!

You’ll need to download Node.js if you haven’t already, and have a suitable text editor — we recommend Atom as it’s free and easy to get started with.

Why Serverless?

If you’ve ever used a crontab, you’ll know how useful they are for scheduling tasks. Some good usage examples in web applications are things like sending out cancellation emails, recurring billing, and archiving files.

Crontabs work ok when you just have the one box, but as you try and follow the principles of a 12-factor app, you start adding more web nodes. There’s often no obvious place to run your scheduled tasks.

In a 12-factor app boxes can be killed off or scaled up at any time. What ends up happening is you spin up this single ‘clock’ node that deals with the scheduling of tasks. Or you plonk it on another server which then accesses one of the load-balanced web nodes to do your bidding for you.

There’s a better way

Serverless scheduling is an ideal solution and involves zero server management. You simply write some code, hand it over to Amazon programmatically, then they spin up a container for you on the specified schedule, run the code, and kill the container.

Amazon deal with everything from executing the code, right through to the hardware. There’s no single point of failure. Plus it’s just as easy to deploy to production — if not easier — than a crontab.

If you’d like to follow along with the code, it’s all available on GitHub.

1. Get serverless framework

Install the serverless command globally. Serverless framework is a provider-agnostic way to deploy FaaS (Function-as-a-Service) applications. It is both a command-line tool and a lightweight library that ships with your function.

We’ll be installing the beta version of this (1.0.0). If you’re reading this post and this version has already been released, then you can omit the @beta part.

npm install -g serverless@beta

If you’re on a Mac, you may need to add ‘sudo’ to the beginning of this command as Node.js doesn’t ship with a writeable global folder after installation on this platform.

2. Login to your AWS account

Since we’re going to be doing everything programmatically instead of going through web interface (which is known as the Management Console), we’ll need to get your AWS API credentials.

Either sign into your AWS account or make a new one, then head to “Security Credentials”. If you’re signing up for a new account, it will ask you for your credit card details. Don’t worry — nothing in this tutorial will cost you any money.

If you’ve been using AWS for a while, it’s likely you’re using IAM instead, create the access for a user within there, ensure it has enough permissions to create Lambdas and CloudWatch events. If you don’t know what IAM is, skip this bit!

Click “Create New Access Key” — then copy them down. You need to add them to a local configuration file. You may already have this file if you’ve used AWS command-line tools before. If you installed Atom earlier, simply run:

atom ~/.aws/credentials

~/.aws/credentials

[default]
aws_access_key=YOURACCESSKEYHERE
aws_secret_access=ANDYOURSECRET

3. Create and test your functions

Create a function using the command-line tool:

serverless create --name cron --template aws-nodejs

Running this command will generate a set of files. You need to add the event to the serverless.yml file as seen below. We’ll set this to run once a minute.

serverless.yml:

service: cron           # This is the name of your service
provider:
  name: aws             # This is the provider we’re deploying to
  runtime: nodejs4.3    # You have a choice of Node, Java, or 
                        # Python, but you can run native binaries too
functions:
  cron:
    handler: handler.run  # This will require the handler.js file,
                          # and execute the exported run function 
    events:
      - schedule: rate(1 minute)

This is setting up the event to trigger the function, at a rate of once per minute. If you were to set this to 5 minutes, make sure you pluralise minute to minutes. You can also use cron syntax:

cron(0 10 1 * ? *) <- This would run at 10am, on the first of every month

One of the files generated will be handler.js, this is the code that runs every minute. In this example we’re going to be using RequestBin. This service allows you to create testing endpoints that you can point things at. It’s especially useful for webhooks. Once you’ve got a RequestBin URL, update this file with the following code, and insert your URL in place of the example one:

handler.js:

'use strict'
const request = require('request')
module.exports.run = (event, context, callback) => {
  request.post('http://requestb.in/sa6rahsa', {
    form: { timestamp: new Date() } },
    (err, response, body) => {
      if (! err) {
        callback(null, { message: 'We ran your scheduled job', body })
      } else {
        callback(true, err)
      }
    }
  )
}

We’ll need to install that request dependency. This is simple in node, you just add it to a packages.json file, like this:

{
  "name": "cron",
  "version": "0.0.1",
  "description": "A simple cron example",
  "author": "James Hall - Parallax Agency Ltd",
  "license": "MIT",
  "dependencies": {
    "request": "^2.73.0"
  }
}

Then run:

npm install

Once this is all working, you’re ready to test your function locally. We’ll make a simple file that just calls the function we wrote.

local-run.js:

'use strict'
const handler = require('./handler')
handler.run(null, null, (err, output) => {
  console.log(output)
})

Now let’s run it:

node local-run.js

If that’s all working you should see a new request pop up in your RequestBin. Visit the RequestBin URL you created earlier with ?inspect on the end of the URL. You should start seeing the requests coming in, once per minute. Now we’ve finished writing our first version, let’s deploy it:

sls deploy

You can now test to see if this worked in production. Just wait a minute until a new request pops up.

This is the foundation of your scheduled task! You can run any code you like in here. It can call out to other native binaries, it can log into other servers, whatever it is you need it to do.

Using it in your build server

This is the easy bit. If you’re already using a build server, simply add your AWS credentials to its environment variables, and add ‘sls deploy’ to your deploy script.

At Parallax we tend to have a Makefile with a deploy section that then executes the flavour-of-the-month tool, be it Grunt, Gulp, or something else entirely.

How does this work underneath?

Serverless logs into AWS and creates 2 resources. Firstly a Lambda function, this is the code that actually runs. It compiles your JS, along with the dependencies, ZIPs it up and deploys it. It then adds a CloudWatch event that runs at the time you specified. It then attaches the CloudWatch event as a trigger for the lambda function.

Where to from here?

Start exploring the growing number of serverless plugins (though be aware some aren’t 1.0 compatible just yet) – have a play, and let us know in the comments what you’ve made using Serverless.

This post is also available on Medium.