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!
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:
- A CloudFormation template to create the SNS topic and CloudWatch Alarms
- 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.