Ruby on Rails in Windows Azure – Part 2 – Creating Windows Azure SDK 1.4 based Application to Host Ruby on Rails Application in Cloud


In the part 1 we have finished a Rails application name “RubyonAzure” as described in the link below:

http://blogs.msdn.com/b/avkashchauhan/archive/2011/04/26/ruby-on-rails-in-windows-azure-part-1-setting-up-ruby-on-rails-in-windows-7-machine-with-test-rails-application.aspx

 

Now to create Windows Azure Application, I took the Simon Davies written base sample from
the link below:

http://archive.msdn.microsoft.com/railsonazure

 I am using the Simon Davies sample base application and then making changed to get it working on Ruby 1.9.2 and Rails 3.0.7. The solution in VS2010 looks as
below:




If you open the RR application you
will see two folders:

1.      
RailsApp folder:  It will include your Rails Application

2.      
Ruby Folder: This will include
Ruby\Bin and Ruby\Lib folder

 


 

Now include the Ruby\Bin and
Ruby\Lib files  in above Ruby Folder:

Note: Please remove the following
folder as this is not needed and your package will be very smaller
comparatively.

            ruby\gems\1.9.1\doc

 

 

Now copy RubyonAzure project in
“RailsApp” folder:


 

Now let’s include “Ruby” Folder
in the VS2010 Solution as below:


Note: Ruby\lib folder is about 90MB so
it will take good amount of time to include this folder in the VS2010 solution.
If you remove \Ruby\lib\ruby\gems\1.9.1\doc from the original Ruby\Lib
folder it will take about 1 hours to add all the files from the Ruby\Lib
folder. 

 

 

 

Now let’s Include RailsApp folder
in the Solution as below:


 

Now Open the Service
Configuration (ServiceConfiguration.cscfg) and add the following:

 

 

<?xml version="1.0"?>
<ServiceConfiguration serviceName="RW" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
<Role name="RR">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey=Storage_Account_Key” />
<Setting name="StorageAccount" value="DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey= Storage_Account_Key" />
<Setting name="RubyFolder" value="Ruby" />
<Setting name="AppFolder" value="RailsApp" />
<Setting name="OutputContainer" value="testoutput" />
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey=Storage_Account_Key" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="true" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="avkash" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="***********************ENCRYPTED_PASSWORD**************************" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2011-05-08T23:59:59.0000000-07:00" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" />
</ConfigurationSettings>
<Certificates>
<Certificate name="Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" thumbprint="*****************************************" thumbprintAlgorithm="sha1" />
</Certificates>
</Role>
</ServiceConfiguration>

 

 

 

Now Open Service Definition (ServiceDefinition.csdef)
and include the following:

 

 

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="RW" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WorkerRole name="RR" enableNativeCodeExecution="true">
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" />
<Setting name="RubyFolder" />
<Setting name="AppFolder" />
<Setting name="StorageAccount" />
<Setting name="OutputContainer" />
</ConfigurationSettings>
<Endpoints>
<InputEndpoint name="Server" port="80" protocol="tcp" />
</Endpoints>
<LocalResources>
<LocalStorage cleanOnRoleRecycle="false" name="App" sizeInMB="200" />
</LocalResources>
<Certificates>
</Certificates>
<Imports>
<Import moduleName="Diagnostics" />
<Import moduleName="RemoteAccess" />
<Import moduleName="RemoteForwarder" />
</Imports>
</WorkerRole>
</ServiceDefinition>

 

 

 

In the VS2010 based Solution, I
have already included the Ruby 1.9.2 and Rails 3.0.0 which is located in Ruby folder. Your RubyonRails application is
located in RailsApp folder inside
the application.

 

Worker Role Source Study:

 

Step 1: When the Worker Role
Starts, we copy Ruby + Rails files and our Ruby app application in two sub
steps:

 

1.      
In step 1, we copy Ruby (Bin and
Lib) folder to Worker Role Local storage folder name \Ruby

2.      
In step 2, we copy our
RubyonRails application to Worker Role local Storage folder name \RailsApp

 

The code responsible for it is as
below:

 

 

string rubyFolderName = RoleEnvironment.GetConfigurationSettingValue("RubyFolder");
if (localStorageRoot.FullName.EndsWith("\\") == false)
this.rubyLocation=string.Format("{0}\\{1}",localStorageRoot.FullName,rubyFolderName);
else
this.rubyLocation = string.Format("{0}{1}", localStorageRoot.FullName, rubyFolderName);
CopyFolder(string.Format("{0}\\{1}", this.roleRoot, rubyFolderName), this.rubyLocation);

string appFolderName = RoleEnvironment.GetConfigurationSettingValue("AppFolder");
if (localStorageRoot.FullName.EndsWith("\\") == false)
this.appLocation = string.Format("{0}\\{1}", localStorageRoot.FullName, appFolderName);
else
this.appLocation = string.Format("{0}{1}", localStorageRoot.FullName, appFolderName);
CopyFolder(string.Format("{0}\\{1}", this.roleRoot, appFolderName), this.appLocation);

this.endPoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Server"].IPEndpoint;

// Start the server
StartProcess();

 

Step 2: Once above Ruby + Rails
& our Ruby application copy process is completed we launch the Ruby
application as below:

 

ProcessStartInfo spawnedProcessInfo = new ProcessStartInfo();
spawnedProcessInfo.UseShellExecute = false;
spawnedProcessInfo.WorkingDirectory = this.appLocation;
spawnedProcessInfo.CreateNoWindow = true;
//string args = String.Format(@"script\server --port {0} --binding {1}", this.endPoint.Port, this.endPoint.Address); // This is old code
// changed by Avkash to get it working
string args = String.Format(@"script\rails server --port {0} --binding {1}", this.endPoint.Port, this.endPoint.Address);
LogInfo(@"Arguments: {0}", args);
spawnedProcessInfo.Arguments = args;
spawnedProcessInfo.FileName = Path.Combine(this.rubyLocation, @"bin\Ruby ");
spawnedProcessInfo.RedirectStandardError = true;
spawnedProcessInfo.RedirectStandardOutput = true;

//Run It
Process spawnedProcess = new Process();
spawnedProcess.ErrorDataReceived += new DataReceivedEventHandler(spawnedProcess_ErrorDataReceived);
spawnedProcess.OutputDataReceived += new DataReceivedEventHandler(spawnedProcess_OutputDataReceived);
spawnedProcess.StartInfo = spawnedProcessInfo;

if (spawnedProcess.Start()) // This line Execute the process as >Ruby script\rails server –-port <Role_Port> --binding <Role_IP_Address>
{
this.id = spawnedProcess.Id;

spawnedProcess.BeginErrorReadLine();
spawnedProcess.BeginOutputReadLine();
LogInfo("Process Id {0} Started",this.id);
}

 

 


Note: When you are running this
application in Development Fabric please set the following

     
<Setting name=”DiagnosticsConnectionString” value=”UseDevelopmentStorage=true”
/>

     
<Setting name=”StorageAccount” value=”UseDevelopmentStorage=true” />

 

Now run the application in the
development fabric and you will see the following results in Compute Emulator
UI:

 

 

 

 

Using the IP address and Port available
in Computer Emulator we can launch the browser to check our application:

 

 


 

Now you can package this
application and deploy on Cloud to test as below:

 

Note: Before packaging the
application, please be sure to set your data connection string to your correct
Windows Azure Storage

     
<Setting name=”DiagnosticsConnectionString” value=”DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey=Storage_Account_Key” />

     
<Setting name=”StorageAccount” value=”DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey=
Storage_Account_Key
” />

     
<Setting name=”Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString” value=”DefaultEndpointsProtocol=https;AccountName=<Storage_Account_Name>;AccountKey=Storage_Account_Key” />

 

After publishing the Windows Azure Application in cloud you can verify it running  as below:

You can download the Windows Azure SDK 1.4 based VS2010  Solution from the link below (This sample includes Ruby 1.9.2 and Rails 3.0.7 package as well):

http://rubyonrailsinazure.codeplex.com/

 

Comments (7)

  1. Scott Watermasysk says:

    It is nice that you **could** do this…but seriously why would you?

    Here are my recommended steps:

    heroku create

    git push heroku master

    go have a beer

    -Scott

  2. Steven Harman says:

    @scottw, You forgot "Step 3: Profit!" 🙂

  3. Adron says:

    Is this serious, or like a pun, joke, or what?!

    Azure needs to have basically the following:

    "git push Azure master"

    or for those command line incapable…

    right click & deploy to Azure.

    But all this… ya gotta be kidding me.

    …my other idea, if you're going the MS route w/ Azure, just go with .NET/C# already and stop fiddling with all this other stuff. There are superior solutions available. Until Azure is 1 to 1 with those, this type of thing shouldn't even be mentioned, it just seems painful.

  4. Jerome says:

    You guys have a point but let's not beat up Avkash. I mean, why do people climb mountains, or walk over hot coals, or poke themselves in the eye with a sharp stick?

  5. Andy Vulhop says:

    Step 1: Create a potentially clumsy workflow that supports something you really want (RoR on Azure)

    Step 2: Elicit feedback.

    Step 3: Smooth over the most painful pain-points.

    Step 4: Go back to Step 2 until sufficiently painless.

    Step 5: Profit!

    What MSFT is doing here is just pushing out v0.1, like you would expect a start-up to do. Deploying is a feature, after all. A lot of this looks automate-able. Hopefully, someone will actually work on convention-izing a bunch of the repetitive elements.

  6. Avkash Chauhan says:

    Thank you so much for all your comments!!

    Your passion for RoR made you write these amazing comments and similar to my passion for RoR made me try running RoR in Azure. With the release of SDK 1.6, few steps above can be simplified as well.

    Yes, I do believe there are places where RoR deployment could be comparative much easier and less painful, however this blog is written to guide, someone who would want to use RoR in Windows Azure and my objective to help anyone who is interested in RoR and Azure.

    Once again, I love your passion for RoR and thank you so much for your comments!!

    Avkash

  7. Tyler Jones says:

    um… windows azure is a fraction of the cost of even a 1 dyno heroku deployment with a database.  (the free heroku setup only includes a 5 MEGAbyte database, so you HAVE to get the $15/mo shared database option)  So that's a pretty strong "why would you"…