This article will show you how to build a simple website health check using AWS tools. Specifically, we want to monitor the HTTP status code for a website and if it becomes a 500 or the site becomes unreachable, I want to receive an alert message. AWS gives you the tools to build this with only a few lines of code.

The workflow for this application will use AWS CloudWatch Events to periodically trigger a Lambda function. The Lambda will fetch a webpage using a standard GET request. The HTTP status code returned during that GET request will be stored as a custom CloudWatch Metric. We'll create a CloudWatch Alarm to monitor the Metric. If the Alarm exceeds a threshold, it will publish an SNS message and alert me!

Architecture

CloudWatch does much of the heavy lifting for this project. If you are unfamiliar with the functionality, I suggest reading up on the concepts.

This project will be two pieces:

  1. A CloudFormation template to create the SNS topic and CloudWatch Alarms
  2. A Serverless Project that creates the Lambda functions and CloudWatch Events.

Resources

Let's start by creating a CloudFormation template for the SNS Topic and the Alarms.

AWSTemplateFormatVersion: "2010-09-09"
Description: Template for deploying the website Health Checks

Resources:
  
  alert:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: Website Uptime
      Subscription:
        -
          Endpoint: 14125550100
          Protocol: sms

  homeAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref alert
      AlarmName: homeAlarm
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: home-status
      Namespace: Website
      Period: 60
      Statistic: Average
      Threshold: 500

The first resource is for a AWS::SNS::Topic. This topic is defined so that it sends an SMS message to the supplied phone number. Don't forget the country code!

The second resource is the AWS::CloudWatch::Alarm. This alarm is configured to trigger the SNS alert when an alarm is triggered. We'll also preemptively have it track the Website namespace and the metric home-status. This alarm will look for the average value of the metric over 60 seconds and trigger the alarm if it is >= 500 for more than 1 period. This means if we see 500 errors for a minute, the alarm will be triggered and the SNS alert will be published.

This can be deployed by running the CloudFormation AWS CLI command:

aws cloudformation create-stack \
  --stack-name ssc2-healthcheck \
  --template-body file://cloudformation.yml

Serverless App

The next piece is using Serverless to create a Lambda function that will trigger an HTTP request and report that value to CloudWatch Metrics. Further, this Lambda will be configured with CloudWatch Events to trigger every 1 minute!

Let's start with the serverless.yml definition.

service: website-healthcheck

frameworkVersion: '1.13'

provider:
  name: aws
  runtime: nodejs6.10

  stage: production
  region: us-east-1

  iamRoleStatements:
    - Effect: Allow
      Action:
        - "cloudwatch:*"
      Resource:
        - "*"

functions:
  checkHome:
    handler: handler.checkHome
    events:
      -
        schedule:
          name: check-home
          rate: rate(1 minute)

Of note here is the iamRoleStatements. We need to grant the Lambda function access to write the CloudWatch Metrics.

Beyond that, the actual function will be called checkHome and will have a scheduled event that gets triggered every minute.

Now to the last piece, the actual lambda function. In handler.js, create our function as such:

let http       = require('http');
let AWS        = require('aws-sdk');
let cloudwatch = new AWS.CloudWatch();

module.exports = {
  checkHome,
};

function checkHome(event, context, callback) {
  checkStatus({ host: 'www.example.com', path: '/' })
    .then(putMetric)
    .then(() => callback(null, { success: true }))
    .catch(callback);
}

//////////////////////////////////////

/**
 * Makes an HTTP request and resolves with 
 * the HTTP status code 
 */ 
function checkStatus({ host, path }) {
  return new Promise((resolve, reject) => {
    let req = http.request({ host, path }, (res) => {
      res.on('error', reject);
      resolve(res.statusCode);
    });
    req.on('error', reject);
    req.end();
  });
}

/**
 * Puts the CloudWatch metric in the Website namespace
 * as the home-status metric.
 */
function putMetric(statusCode) {
  let params = {
    Namespace: 'Website',
    MetricData: [
      {
        MetricName: 'home-status',
        Timestamp: new Date(),
        Value: statusCode
      }
    ]
  };
  return cloudwatch.putMetricData(params).promise();
}

We export a single function called checkHome. This corresponds to the serverless.yml file above. This function will first make an HTTP request and will then put the CloudWatch Metric using the AWS SDK.

You can then deploy your serverless function with

serverless deploy

Things should now be running! You can log into your CloudWatch dashboard and check out your alerts.