Home
Home

Simple Remote Monitor for Apache with Perl

Sep 29 09

Simple Remote Monitor for Apache with Perl

pdweinstein

Sometime last week Apache on one of the
servers that manages some websites I host stop responding to
requests, resulting in said websites being unavailable for a day and
half (or so). Unfortunately I didn’t know about the problem until
someone else notified me of the issue. Opps. Not good system
administration that.

There are plenty of solutions for
monitoring network services, but given that I’m just running Apache
for a half dozen non-critical websites on the side, there’s no reason
to go overboard. Any sysadmin worth their weight in salt should be able
to whip something up with Perl in no time.1
So, what’s needed? Well, something that will remotely monitor the
pool of websites and notify me if something is amiss:

monitor_flow.jpg

First up, making a simple HTTP GET request:

my $url = "http://$host";
my $ua = LWP::UserAgent->new;
my $response = $ua->get( $url );

Now we test the response, was it successful? If so, Apache returns an appropriate status code and the requested resource. If Apache is available, but unable to properly process the request it will responded with some relevant status code, which we wish to pass on for further troubleshooting:

if ( !$response->is_success ) {
# Do we have an error code?
if ( $response->code ) {
reportError("$host reports $response->message \n");
} else {

However a failed GET request will result in no status code since Apache probably failed to respond at all. In this case it would be helpful to determine if the issue is with Apache or something else. For that test a network ping is issued:

		# HTTP is down, is the network connection down too?
my $p = Net::Ping->new("icmp");
if ( $p->ping( $host, 2 )) {
reportError ("$host is responding,
but Apache is not.\n");
} else {
reportError ("$host is unreachable.\n");
}
}

Not too difficult that. Now, how do we go about communicating the issue at hand? SMS has always been my preferred method since my phone is usually close at hand and, iPhone or not, SMS is widely implemented and easy to use.

# Send SMS via cellular Email to SMS gateway
my ( $msg ) = @_;
my $to = "7735551234\@txt.att.net";
my $from = "pdw\@weinstein.org";
my $subject = "Service Notification";
my $sendmail = '/usr/lib/sendmail';
open( MAIL, "|$sendmail -oi -t" );
print MAIL "From: $from\n";
print MAIL "To: $to\n";
print MAIL "Subject: $subject\n\n";
print MAIL $msg;
close( MAIL );

All’s done? Not quite. For the fun of it, I figured broadcasting a message that the server was unavailable might be of use to regular visitors. What better way to broadcast a short message than via Twitter?

my ( $msg ) = @_;
my $nt = Net::Twitter::Lite->new(
username => $username,
password => $password );
my $result = eval { $nt->update( $msg ) };

Bring this all together, with cron gives us:

# monitor remote httpd servers every 30 minutes
*/30 * * * *   pdw  /home/pdw/bin/monitor.pl >/dev/null 2>&1
#!/usr/bin/perl
use strict;

use LWP::UserAgent;
use Net::Ping;
use Net::Twitter::Lite;

my @hosts = ( "pdw.weinstein.org", "www.weinstein.org" );

foreach my $host ( @hosts ) {

	my $url = "http://$host";
	my $ua = LWP::UserAgent->new;
	my $response = $ua->get( $url );

	if ( !$response->is_success ) {

		# Do we have an error code?
		if ( $response->code ) {

			reportError("$host reports
			$response->message.\n");

		} else {

		# HTTP is down, is the network connection down too?
		my $p = Net::Ping->new("icmp");
		if ( $p->ping( $host, 2 )) {

			reportError ( "$host is responding, 
			     but Apache is not.\n" );

		} else {

			reportError ( "$host is unreachable.\n" );

		}

	}

}

sub reportError ($) {

	my ( $msg ) = @_;
	my $nt = Net::Twitter::Lite->new(
username => $username,
password => $pasword );

	my $result = eval { $nt->update( $msg ) };

	if ( !$result ) {

		# Twitter has failed us,
		# need to get the word out still...
		smsEmail ( $msg );

	}

}

sub smsEmail ($) {

	my ( $msg ) = @_;
	my $to = "7735551234\@txt.att.net";
	my $from = "pdw\@weinstein.org";
	my $subject = "Service Notification";

	my $sendmail = '/usr/lib/sendmail';
	open(MAIL, "|$sendmail -oi -t");
		print MAIL "From: $from\n";
		print MAIL "To: $to\n";
		print MAIL "Subject: $subject\n\n";
 		print MAIL $msg;
	close( MAIL );

}

1 Of
course if I was worth my weight I would have had something in place
long before it was called for….