Thursday, November 19, 2015

Monitoring your CDN's Webservers

(This post considers options for writing a monitor in powershell that can handle round-robin DNS load-distributed web servers, especially those that you do not control)

Premise

After observing some issues with a vendor's CDN having occasional server/service outages, I decided to write some powershell to dig through and give me a quick analysis so I could determine where the problem was and what effect it should have on the users. Some of you may read this and think "If the site is down, it's down, right?"

Well many websites now spread their resources across CDNs (Content Delivery Networks). This means that while the html page may come from one webserver (or even one CDN), the javascript and CSS file may come from another, and the images and videos from yet another. You can use the Developer Tools in your web browser to dig up out the sources for (at least THIS load) of the website.

On top of this, there are a few approaches to load balancing, where the exact same resources are duplicated across many servers, with the intent of spreading traffic around and reducing the chances of downtime. Citrix's Netscaler is a popular (and VERY capable) option, but sometimes you'll see the vendor go down the cheaper and easier path: Round-Robin DNS.

Approach

In Powershell, you can invoke the GetHostAddresses method to see how many IP addresses are linked to a DNS entry.

[System.Net.DNS]::GetHostAddresses("google.com").ipAddressToString

At the time of this writing, that returns 11 IP addresses. If you want to dig into nslookup, you can watch it cycle through the IPs in that list as you repeatedly query for google.com.

So what if your CDN is using Round Robin, and your customers are complaining of an outage, and the site appears up?


function test-CDNPage{
    Param
        ([string]$url) #Properly Formatted with http:// or https://
    try{
        write-host -ForegroundColor Cyan "Trying $url"
        Invoke-WebRequest -Uri $url
    }
    Catch [System.Net.WebException]
    {
    if ($_.exception -match "403")
        {
            # 403 is expected because CDNs typically dont allow IP access
            Write-host "403" -ForegroundColor Green
            $value = $true
        }
        ELSE
        {
            # A non-403 response is treated as 
            write-host "Non-403 Response" -ForegroundColor Red
            $_
            $value = $false
        }   
    }
    #Add Results to Object
    $thisResult = New-Object psobject -property @{
        url        = $url
        Responding = $value
        }
    return $thisResult
}

$data = @()
$site = "google.com"
$ipaddys = [system.net.dns]::GetHostAddresses($site).IPAddressToString
foreach ($ip in $ipaddys){
    $data += testpage "http://$ip"
}


Now this code probably won't return anything because Google isn't a CDN. They see you trying to access the IP directly and re-direct your web browser to https:\\google.com. They likely also have some smarter handling behind their Round-Robin DNS solution, as they are...Google.

But consider a company like CloudFlare. They may be hosting multiple different websites from multiple different companies on their servers.

When you try to access a site like that, you load a page telling you that "direct IP access is not allowed" in your browser, and powershell's Invoke-WebRequest throws a 403 Terminating error. This is where Powershell can do things that some other monitoring tools can not. The powershell snippet above uses a try/catch statement to look at the error. If the error is a 403, it sets the $value to true. If the error is NOT a 403, it sets the $value to false. I've set this up to output an XML file every minute that is consumable by my client's monitoring system, and now they can see when individual pieces of their vendor's web services go down.

Having solid data like this can frequently get you past the first tier support layer, that at many companies is incapable of solving network issues on their own end, and is typically only a fence I must cross to reach an actual solution.