Saturday, August 27, 2011

Migrating a Web Application to Azure: Step 3

Getting rid of file access

One issue to watch out for with an application in the cloud is file access.

File-Access in a highly-elastic environment such as Azure is generally a bad idea because it reduces the ability for a request to be correctly handled by multiple server instances. To combat this, generally, all access to the file system should be changed to access Azure storage, which does scale across multiple server instances.

In our example, we have contrived to have a picture stored in the database, but cached on the file-system on its first use, and subsequent access is to the cached file.

Local Application, Images from Server Files

We have code that looks like this in the original code:

public ServerPath EnsureImage(Picture picture)
{
// ensure the directory exists
var directoryPath = EnsureDirectory(RelativePathToImagesDirectory);

// now ensure the file exists
var filePath = EnsureFile(
directoryPath,
picture.OriginalFilename,
_path =>
{
using (var stream = new FileStream(
_path.Absolute,
FileMode.OpenOrCreate,
FileSystemRights.TakeOwnership | FileSystemRights.Write,
FileShare.None,
1024,
FileOptions.None))
{
stream.Write(picture.Image.ToArray(), 0, picture.Image.Length);
}
});

return filePath;
}

internal ServerPath EnsureFile(ServerPath root,
string filename,
Action<ServerPath> fileCreateFunc)
{
var path = new ServerPath(Path.Combine(root.Relative, filename), Server.MapPath);

if (!(File.Exists(path.Absolute)))
{
fileCreateFunc(path);
}

return path;
}

which results in


images_from_filesystem


Note that the web application is still running locally, and the images refer to files located at relative paths within the server.


To remove the dependency on the file-system, we can use Azure Blob Storage to store the cached files.


The Rise Of The Blob…


Blob storage Azure is very cool. It presents a REST-based API to upload, tag, list and retrieve unstructured binary data like images, documents and videos.


So we can switch the application to interact with the Azure Blob store, referring to the images from the storage in the cloud.


To do this, we use the Azure storage account we created earlier.


configure_blob_storage


The important pieces of information to capture are the Access Key (you get this when you click on the button with the orange outline), and the Azure storage account name which is highlighted.


We then initialize access to the blob storage account and container from within the application:

public void Initialize()
{
if (CloudStorageAccount == null)
{
CloudStorageAccount = CloudStorageAccount.Parse("AccountName=cos204;AccountKey=<put_your_key_here>;DefaultEndpointsProtocol=http");
}

if (CloudBlobClient == null)
{
CloudBlobClient = new CloudBlobClient(CloudStorageAccount.BlobEndpoint, CloudStorageAccount.Credentials);
}

if (ThumbnailsContainer == null)
{
ThumbnailsContainer = CloudBlobClient.GetContainerReference("thumbnails");
}

ThumbnailsContainer.CreateIfNotExist();

if (ThumbnailsContainer.GetPermissions().PublicAccess != BlobContainerPublicAccessType.Container)
{
ThumbnailsContainer.SetPermissions(new BlobContainerPermissions
{PublicAccess = BlobContainerPublicAccessType.Container});
}

if (ImagesContainer == null)
{
ImagesContainer = CloudBlobClient.GetContainerReference("images");
}

ImagesContainer.CreateIfNotExist();

if (ImagesContainer.GetPermissions().PublicAccess != BlobContainerPublicAccessType.Container)
{
ImagesContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container });
}
}

What we are doing here is initializing the Blob Storage infrastructures and creating containers for Thumbnails and Images.


It’s important to note that the demo code shows a hardcoded Azure Connection String just to simplify the code snippet for reference. Normally this would be hidden away in a configuration file.


Local Application, Remote Images


We can now replace the file-access code shown above with its Azure Blob equivalent.

public ServerPath EnsureImage(Picture picture)
{
var filePath = EnsureBlob(
ImagesContainer,
picture.OriginalFilename,
_blob =>
{
var blobStream = _blob.OpenWrite();
try
{
blobStream.Write(picture.Image.ToArray(), 0, picture.Image.Length);
}
finally
{
blobStream.Flush();
blobStream.Close();
}
});

return filePath;
}

internal ServerPath EnsureBlob(CloudBlobContainer blobContainer,
string filename,
Action<CloudBlob> blobCreateFunc)
{
var blob = blobContainer.GetBlobReference(filename);

if (blobContainer.ListBlobs().FirstOrDefault(_blob => _blob.Uri.AbsoluteUri == blob.Uri.AbsoluteUri) == null)
{
blobCreateFunc(blob);
}

return new ServerPath(filename, blob.Uri.AbsoluteUri);
}

With this implementation of EnsureImage(), we get:


images_from_blob


Pretty cool.


We now have a web application running on the local server, with data in the cloud, caching files and referencing Azure Blob storage also in the cloud!


Inspecting Blob Storage


While developing and debugging this stage of the migration, it’s useful to see exactly what is being stored in the Azure Blob storage.


The Windows Azure Management Portal does not have a handy tool to inspect the structure and contents of the Azure Storage account, so we turn to some other tools to do that.


The pack of Windows Azure Tools for Visual Studio 2010 v1.4 enables a storage account browser, which can be configured with the storage account and the key as before, allowing for full access to the storage account.


azure-storage-browser


We can use this to ensure that the blob storage account is structured as we expect, and filled with the contents we expect.


Next, we’ll move the application itself to Azure.


blogBling_150x150_02333