Google
 
Showing posts with label Web. Show all posts
Showing posts with label Web. Show all posts

Friday, May 15, 2015

Configuring and testing AWS Elastic Load Balancer

Load balancing is an essential component for the scalability and fault tolerance of web applications. Major cloud computing providers have different offerings for load balancing.
In this post I'll explore AWS's (Amazon Web Services) ELB (Elastic Load Balancing) feature, and test it to see how it distributes the load on front-end web servers, and in case of unavailability of one of the front-end servers, how traffic is directed to the healthy instance(s).



I'll use Linux based image, but the concepts apply to Windows images. I assume that the reader has the basic knowledge on how to create an AWS account and create EC2 (Elastic Compute Cloud) virtual machine. If not, don't worry, following the steps below will give you a good understanding.

So the experiment goes as follows:


1- Create a base image for front-end web servers: 

 

  1. Go to AWS console and select "Launch Instance", from the list of images, select "Ubuntu Server 14.04 LTS".
  2. Complete the wizard till you reach the "Configure Security Group" step. In this is the step we select the proper ports we need AWS to open. Select SSH (22) to connect to the instance to configure it, and HTTP (80) to serve web traffic.
  3. When you're prompted to select the key pair, make sure to choose an existing one you have already downloaded or create a new one and keep it in a safe place.
  4. Then Launch the instance.

Note: When I first stared using AWS, and being from a windows background, the term "Security Group" was a bit confusing to me, it's about firewall rules not security groups in the sense of Active Directory Groups.

2- Configure Apache web server

The image does not have a web server installed by default, so I'll SSH into the instance and install it.
If you're using MAC or Linux, you should be able to run SSH directly. For Windows users, you can use Putty.
  1. Copy the public IP of the running instance you just created.
  2. Use SSH to connect using this command:  ssh -l ubuntu -i . for example: ssh 54.72.151.182 -l ubuntu -i mykey.pem . note that ubuntu is the username for the image we created this machine from. the .pem file acts as a password.
  3. Now we are inside the instance. It's time to install and configure Apache:

sudo su
apt-get install apache2
sudo a2enmod cgi
service apache2 restart

The above commands simply do the following:
  • Elevate privileges to run as a super user to be able to install software.
  • Install apache using the package manager.
  • Enable CGI, I'll show you why later
  • Restart apache so that CGI configuration takes effect.

Now it's time to test the web server. Visit http://INSTANCE_IP and you should be welcomed with the default apache home page.



3- Create a script to identify the running instance

To test ELB, I need to identify which instance served the request just by looking into the response to a web request. Now I have 2 options: Create static pages on each web fron-end or create some dynamic content that identifies the instance. And I prefer the latter option as I'll use the same image for all front-ends.
EC2 has a nice feature called instance metadata. It's an endpoint accessible from within EC2 instances that can be called to get information about it. From SSH terminal try:


curl http://169.254.169.254/latest/meta-data/

A list of available meta-data will be shown:

ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
hostname
instance-action
instance-id
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services

Appending any of them to the URL will show the value. For example:


curl http://169.254.169.254/latest/meta-data/public-hostname
curl http://169.254.169.254/latest/meta-data/public-ipv4

And I'll use these two meta-data items to identify the instances by showing them within a bash script and then serve it from apache. cd into /usr/lib/cgi-bin

cd /usr/lib/cgi-bin

This is the default location that apache uses to serve CGI content. That's why I enabled CGI in a previous step.
in that folder I'll create a bash script that shows the output of the meta-data. use any text editor. For example run nano in the command line and paste the below script:

#!/bin/bash

echo "Content-type: text/text"
echo ''
echo 'Host name:'
curl http://169.254.169.254/latest/meta-data/public-hostname
echo ''
echo 'Public IP:'
curl http://169.254.169.254/latest/meta-data/public-ipv4


If using nano, ctrl+X, y. save as metadata.sh

Now we need to grant execute permission on this file:

chmod 755 /usr/lib/cgi-bin/metadata.sh

To test the configuration, browse to http://INSTANCE_IP/cgi-bin/metadata.sh
My results look like:

Host name:
ec2-54-72-151-182.eu-west-1.compute.amazonaws.com
Public IP:
54.72.151.182

Note: I'm not advising using bash scripts in production web sites. It just was the easiest way to spit out info returned from the meta-data endpoints with minimal effort.

4- Create 2 more front-ends

Now we have an identifiable instance. Let's create more of it.
  1. Stop the instance from the management console
  2. After the instance has stopped, right click -> image -> create image.
  3. Choose and appropriate name and save.
  4. Navigate to AMI (Amazon Machine Image) and check the creation status of the image.
  5. Once the status is available click launch
  6. In the launch instance wizard, select to launch 2 instances
  7. Select the same security group as the one used before, it will have both 22 and 80 ports open.
  8. Start the original instance. 
  9. Now we have 3 identical servers.
  10. Using the IP address of any instance, navigate to the CGI script, for example: http://52.17.134.221/cgi-bin/metadata.sh
Note that most probably the IP of the first instance is now different after restart.


5- Create an ELB instance

  1. In AWS console, navigate to "Load Balancers".
  2. Click "Create Load Balancer"
  3. Make sure it's working on port 80
  4. Select the same security group
  5. In the health check, in the ping path, enter "/". This means that ELB will use the default apache page for health check. In production, it might not be a good idea to make your home page the health check page.
  6. For quick testing, make the "Healthy Threshold" equal to 3.


Now a bit of explanation is required. This configuration tells ELB to check for the healthiness of a front-end instance every 30 seconds. A check is considered successful if the server responds in 5 seconds.
If a healthy instance does not respond with that period for 2 consecutive failures, it's considered unhealthy. And similarly, an unhealthy instance is considered healthy again if it responds to the check 3 consecutive times.

Now select the 3 instances to use for load balancing. And wait until the ELB instance is created and the 3 instances in the "instances" tab are shown InService.

Now in the newly create ELB, select the value of the DNS name (like test-elb-1856689463.eu-west-1.elb.amazonaws.com) and navigate to the URL of the metadata page. My url looked like:
http://test-elb-1856689463.eu-west-1.elb.amazonaws.com/cgi-bin/metadata.sh

The data displayed in the page will belong to the instance that actually served the request. Refresh the page and and see how the response changes. In my case ELB worked in a round robin fashion and the responses where:


Host name:
ec2-52-17-134-221.eu-west-1.compute.amazonaws.com
Public IP:
52.17.134.221


Host name:
ec2-52-16-189-41.eu-west-1.compute.amazonaws.com
Public IP:
52.16.189.41


Host name:
ec2-52-17-65-93.eu-west-1.compute.amazonaws.com
Public IP:
52.17.65.93

Inspect the network response using F12 tools and note the headers:

HTTP/1.1 200 OK
Content-Type: text/text
Date: Sat, 16 May 2015 19:12:38 GMT
Server: Apache/2.4.7 (Ubuntu)
transfer-encoding: chunked
Connection: keep-alive


Note: nothing special as there is no session affinity.


6- Bring an instance down

Now, let's simulate an instance failure. Let's simply stop the apache service on one of the 3 front-ends. So ssh into one of the 3 instances and run:

sudo service apache2 stop

Refresh the page pointing to the ELB url, note that after a few seconds, you only get responses from the 2 running instances. After about 1 minute, the instance is declared OutOfService in the Instances tab of ELB.

 

7- Bring it back!

This time, turn on apache service by running:

sudo service apache2 start

Wait about one and half minutes, the instance is back to InService status and you start to get responses from it.
The "Healthy Hosts ( Count )" graph shows a very good representation of what happened:

8- Turn them all off!

They are costing you money, unless you are still under the free tier. It's recommended to terminate any EC2 and ELB instances that are no longer used.

Note:
If you intend to leave some instances alive, it's recommended to de-register the instance from ELB when shut down: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-deregister-register-instances.html

 

Summary:

In this post, we've seen ELB in action using its basic settings. The round robin load balancing worked great and health check made our site available to users by eliminating unhealthy instances.
This works great with web applications that don't require session affinity, for applications that require it, well, that's another post.

Wednesday, April 30, 2014

Learning plan for Exam 70-480

The Microsoft exam 70-480 "Programming in HTML5 with JavaScript and CSS3" is the first step in a couple of MCSD certification paths. It's common in Web, Windows Store apps and SharePoint technologies.
I share the resources I used to get prepared for this exam. Note however that while I'm relatively new to HTML5, CSS3, I have a long web development experience that helped me.
The points below are copied from the skills being measured section in the exam page, augmented with some recommend resources from my side.

General Resources

Implement and manipulate document structures and objects (24%)

  • Create the document structure
    • Structure the UI by using semantic markup, including for search engines and screen readers (Section, Article, Nav, Header, Footer and Aside); create a layout container in HTML
  • Write code that interacts with UI controls
    • Programmatically add and modify HTML elements; implement media controls; implement HTML5 canvas and SVG graphics
  • Apply styling to HTML elements programmatically
    • Change the location of an element; apply a transform; show and hide elements
  • Implement HTML5 APIs
    • Implement storage APIs, AppCache API and Geolocation API
  • Establish the scope of objects and variables
    • Define the lifetime of variables; keep objects out of the global namespace; use the “this” keyword to reference an object that fired an event; scope variables locally and globally
  • Create and implement objects and methods
    • Implement native objects; create custom objects and custom properties for native objects using prototypes and functions; inherit from an object; implement native methods and create custom methods

Preparation resources



Implement program flow (25%)

  • Implement program flow
    • Iterate across collections and array items; manage program decisions by using switch statements, if/then, and operators; evaluate expressions

  • Raise and handle an event
    • Handle common events exposed by DOM (OnBlur, OnFocus, OnClick); declare and handle bubbled events; handle an event by using an anonymous function
  • Implement exception handling
    • Set and respond to error codes; throw an exception; request for null checks; implement try-catch-finally blocks
  • Implement a callback
    • Receive messages from the HTML5 WebSocket API; use jQuery to make an AJAX call; wire up an event; implement a callback by using anonymous functions; handle the “this” pointer
  • Create a web worker process
    • Start and stop a web worker; pass data to a web worker; configure timeouts and intervals on the web worker; register an event listener for the web worker; limitations of a web worker

Preparation resources


Access and secure data (26%)

  • Validate user input by using HTML5 elements
    • Choose the appropriate controls based on requirements; implement HTML input types and content attributes (for example, required) to collect user input
  • Validate user input by using JavaScript
    • Evaluate a regular expression to validate the input format; validate that you are getting the right kind of data type by using built-in functions; prevent code injection
  • Consume data
    • Consume JSON and XML data; retrieve data by using web services; load data or get data from other sources by using XMLHTTPRequest
  • Serialise, deserialise and transmit data
    • Binary data; text data (JSON, XML); implement the jQuery serialise method; Form.Submit; parse data; send data by using XMLHTTPRequest; sanitise input by using URI/form encoding

Preparation resources


Use CSS3 in applications (25%)

  • Style HTML text properties
    • Apply styles to text appearance (colour, bold, italics); apply styles to text font (WOFF and @font-face, size); apply styles to text alignment, spacing and indentation; apply styles to text hyphenation; apply styles for a text drop shadow
  • Style HTML box properties
    • Apply styles to alter appearance attributes (size, border and rounding border corners, outline, padding, margin); apply styles to alter graphic effects (transparency, opacity, background image, gradients, shadow, clipping); apply styles to establish and change an element’s position (static, relative, absolute, fixed)
  • Create a flexible content layout
    • Implement a layout using a flexible box model; implement a layout using multi-column; implement a layout using position floating and exclusions; implement a layout using grid alignment; implement a layout using regions, grouping and nesting
  • Create an animated and adaptive UI
    • Animate objects by applying CSS transitions; apply 3-D and 2-D transformations; adjust UI based on media queries (device adaptations for output formats, displays and representations); hide or disable controls
  • Find elements by using CSS selectors and jQuery
    • Choose the correct selector to reference an element; define element, style and attribute selectors; find elements by using pseudo-elements and pseudo-classes (for example, :before, :first-line, :first-letter, :target, :lang, :checked, :first-child)
  • Structure a CSS file by using CSS selectors
    • Reference elements correctly; implement inheritance; override inheritance by using !important; style an element based on pseudo-elements and pseudo-classes (for example, :before, :first-line, :first-letter, :target, :lang, :checked, :first-child)

Preparation resources



I hope this helps someone get ready for the exam. Good luck.

Sunday, February 16, 2014

My sad experience with Google app engine and domain names

So I decided to use Google app engine to host a small website. And no, it's neither the the programming language I'm not familiar with (python, it's cool and I like it by the way) nor the bad development and debugging experience that I had, is what I'm going to tell about this time. So what is it about? read on.

Pick a nice name for your baby
It was super easy to create an app with a temporary name like example.appspot.com. I reserved the domain name with the hosting company I'm used to. So I just need to assign the domain name to the GAE site and that's it. Quite a common task, right?  Sadly not.

You need a Google apps account
First, you need to create a Google apps account. You can't just assign the domain name directly. Anyway, not a big deal when apps had a free tier, but this is no longer the case. So I tried to use my existing free Google apps account that I've created years ago.
For Google to verify your ownership of the domain,  you have to add a value to the domain name DNS records. This sounds OK to prove that you control the domain. To get that value you should go to the Google apps administration page (https://admin.google.com) and click "more controls" then scroll to the right to find the "domains" icon.
Then you should select to add a new domain. and after you get the CANME or TXT record value to add to your DNS and wait for verification,  the domain should now be available for use. Except that it's not.
To make the domain point to the application you created (example.appspot.com) you need to go to the administration home page and select the "App Engine Apps". Then select "Add Services" and enter the app Id.
And now, you select to add a URL to access the app, just to find that you cannot use the domain you've just added!! You can add a sub-domain of the original Google app service login!. What's the point in verifying another domain? I don't know!

Anyway. I went to another option by applying for a new Google apps account with a 30 days trial. And after the tedious steps of domain verification and service addition, finally I have my domain pointing to my GAE app. Hopefully, the domain will still work after the free trial expiration.

End of story? never.


Naked domains: 
Whether you should add www before your domain name or use naked domains is a religious war that I'll not discuss here. But if you want to use naked domains with GAE you're out of luck. It's not supported. 
So if the user writes (example.com) in the browser's address bar, he's not going to your app. You must redirect the user to (www.example.com). Some domain registrars support this feature, but my hosting company doesn't, so I had to do it myself. I pointed the domain (example.com) to a site on my hosting space, and used an asp.net HTTP module to redirect requests to the www version (www.example.com).

Note: Apparently, there is another way to do it. But I'm not sure how get it to work using my hosting provider control panel.

And some spam
Since I did not configure email on my newly created Google apps account, Google kept sending me emails about how much I'm missing. OK. I did not need all this mess in the first place.

Just Why?
My guess is that the whole experience is geared towards promoting the apps model from two sides: you should have a Google apps account, and you should purchase services from Google market place.
The experience was not friendly for people like me, who just wanted to created a web site. When did the www boom happen exactly?

Saturday, September 29, 2012

Tip: Sending email to local folder for ASP.NET local development

Many web applications and sites have features that require sending emails. In production, usually mailSettings element of system.net is configured to send emails using SMTP server.
But when developing on a local machine, you want to check the output email quickly and without relying on a SMTP server. Sometimes a local antivirus can make the process harder.
It's better to configure the mail to send to a local folder, this can be easily done by setting the delivery method to SpecifiedPickupDirectory and setting the local directory location:
<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="c:\Temp"/>
</smtp>
</mailSettings>
</system.net>

Now, when your code sends an email, a .eml file will be saved to c:\Temp, and you can double click it to check its content.

Monday, August 15, 2011

Linking using history.back()

Easy navigation and discoverability are very important aspects of website or application usability. The user should be able to easily dig deep in the content as well as to navigate to broader category of content.

I stumbled upon a site that has a "Back" link in a leaf level content page. As a user I thought it might be a way to go up and visit the related content or the parent category. Unfortunately, history.back() was used. And since I reached this page using a search or a direct link and opened it in a new tab, it led me nowhere.

The designer / developer assumed that the user will reach this page using a specific path and will open it in the same window and tab.

That was a very bad idea as the most of the content nowadays is reached using search engines. And you don't your visitors to leave your site back to Google after getting him to your site!!. Also the design is broken in case the page is opened in a new window or tab.

Maybe the rationale behind using this technique was to get the user back (within the site) to the location from where he got to the current page, be it direct navigation links or internal site search page. If so, a better implementation should be using HTTP referrer, or explicit query string value to indicate the source of the request.

Small details can make big changes in usability. Different scenarios should be taken into consideration when designing the navigation.

Monday, March 8, 2010

Changing AJAXtoolkit:CalendarExtender day format

AJAX toolkit has a set of awesome controls, and very customizable too. But one property that is not customizable OOB is how the day names are displayed in the Calendar extender.
The displayed format is two letters only. But what if you want it to be displayed in three letters?
Here a small cosmetic surgery (i.e. code changes).










BeforeAfter



So, what does it take to do it successfully?
I opened CalendarBehavior.debug.js, which contains the debug version of the extender's JavaScript. Day names were obtained from an array named: dtf.ShortestDayNames:

dayCell.appendChild(document.createTextNode(dtf.ShortestDayNames[(i + firstDayOfWeek) % 7]));

Searching the library code, I found that this array is defined in MicrosoftAjax.debug.js. as:

ShortestDayNames":["Su","Mo","Tu","We","Th","Fr","Sa"]

And fortunately, another array was defined with three letters names:

AbbreviatedDayNames":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]

So all what it takes is to replace ShortestDayNames with AbbreviatedDayNames in both CalendarBehavior.debug.js and CalendarBehavior.js:

dayCell.appendChild(document.createTextNode(dtf.AbbreviatedDayNames[(i + firstDayOfWeek) % 7]));

And build.

It's great to have the source between your hands :)

Wednesday, May 13, 2009

More great things about Visual Web Developer 2008 Express

In case you have not installed it yet: In addition to the great features Visual Web Developer 2008 Express has. SP1 is even much better!!

It now has more project tamplates. The most important one is the web application template (I hate the Website template).
You can also add class library project to the same solution!!


Download and enjoy!!

Wednesday, May 28, 2008

"Page not found" error after installing IIS

After you install IIS on a machine that has .net framework installed and you open an ASP.NET page, you will probably get a "Page not found - 404" error.
This is because IIS did not (recognize) the installed .net framework. You simply need to run this command line:

aspnet_regiis -i

You can run it by opening the .NET Framework SDK command prompt or by changing the current directory to "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727" and run the command. (The path may differ according to the .net version installed)

Friday, December 7, 2007

How to check programmatically if WSS or MOSS are installed

I came into a situation when I needed to check if a SharePoint installation exists on a system, and specifically, WSS (Windows SherePoint Servises) or MOSS (Microsoft Office SharePoint Server).

The two properties can be useful in this case, the idea depends on checking the registry.
Note that I extracted this code from the open source project SharePoint Solution Installer and made minor modifications.

To check for WSS we check the key:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0]
and the value "SharePoint" must be "Installed"

To check for MOSS we check the key:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12.0]
"BuildVersion"="12.0.4518.1016"
and check that the version is 12

Here is the code:

public static bool IsWSSInstalled
{
get
{
string name = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0";

try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(name);
if (key != null)
{
object val = key.GetValue("SharePoint");
if (val != null && val.Equals("Installed"))
{
return true;
}
}
return false;
}

catch (SecurityException ex)
{
throw new Exception("Access deined. Could not read the Windows Registry key '" + name + "'", ex);
}
}
}

public static bool IsMOSSInstalled
{
get
{
string name = @"SOFTWARE\Microsoft\Office Server\12.0";

try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(name);
if (key != null)
{
string versionStr = key.GetValue("BuildVersion") as string;
if (versionStr != null)
{
Version buildVersion = new Version(versionStr);
if (buildVersion.Major == 12)
{
return true;
}
}
}
return false;
}

catch (SecurityException ex)
{
throw new Exception("Access deined. Could not read the Windows Registry key '" + name + "'", ex);
}
}
}

and at the top of your class:
using Microsoft.Win32;
using System.Security;