<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Thomas Conté's MSDN Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/tconte/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/tconte/feed.xml</link>
    <description>Thomas Conté&amp;#039;s MSDN Blog - Windows Azure Cloud Architecture</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:32:22 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>This blog has moved!</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/this-blog-has-moved</link>
      <pubDate>Mon, 22 Sep 2014 05:17:46 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2014/09/22/this-blog-has-moved/</guid>
      <description><![CDATA[I am now blogging at this address: www.hypernephelist.com !
I will leave this blog and its existing...]]></description>
      <content:encoded><![CDATA[<p>I am now blogging at this address: <a href="http://www.hypernephelist.com/">www.hypernephelist.com</a> !</p>
<p>I will leave this blog and its existing messages in place for archival purposes, but please update your bookmarks and RSS feeds if you want to keep tracking my adventures :-)</p>]]></content:encoded>
    </item>
    <item>
      <title>Building your Ubuntu PHP development box on Azure using Vagrant</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/building-your-ubuntu-php-development-box-on-azure-using-vagrant</link>
      <pubDate>Thu, 19 Jun 2014 03:27:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2014/06/19/building-your-ubuntu-php-development-box-on-azure-using-vagrant/</guid>
      <description><![CDATA[With Azure you can easily create and use a development environment in the Cloud. You can create a...]]></description>
      <content:encoded><![CDATA[<p>With Azure you can easily create and use a development environment in the Cloud. You can create a Virtual Machine from the <a href="https://manage.windowsazure.com/">Management Portal</a> or from the <a href="http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/">command line</a>, but if you want more automation, you can use a tool like <a href="http://www.vagrantup.com/">Vagrant</a> to automatically start and configure your Virtual Machine so that it is ready to use, instead of following manual installation steps to configure your environment.</p>
<p>Our friends at MS Open Tech have recently published a <a href="https://github.com/MSOpenTech/Vagrant-Azure">Vagrant Azure Provider</a> that allows Vagrant to control and provision machines in Windows Azure. I would like to show you how to use this provider to start a VM and then automatically provision it with a development environment suitable for PHP/LAMP and <a href="http://symfony.com/">Symfony2</a> development.</p>
<p>First, you should <a href="http://www.vagrantup.com/downloads.html">download and install Vagrant for Windows</a>. Then, installing the Azure plugin is as simple as:</p>
<pre><code>vagrant plugin install vagrant-azure </code></pre>
<p>Then we are going to base our configuration on the "dummy Azure box" provided by MS Open Tech:</p>
<pre><code>vagrant box add azuredummybox https://github.com/msopentech/vagrant-azure/raw/master/dummy.box </code></pre>
<p>Next, create an empty directory as your project root, and create a Vagrantfile in it. Here is a Vagrantfile for Azure, let's look at it in detail.</p>
<div class="highlight highlight-ruby">
<pre><span class="no">Vagrant</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="s1">'2'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
    <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s1">'trusty64'</span>

    <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">provider</span> <span class="ss">:azure</span> <span class="k">do</span> <span class="o">|</span><span class="n">azure</span><span class="p">,</span> <span class="n">override</span><span class="o">|</span>
        <span class="n">override</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s1">'azuredummybox'</span>
        <span class="n">override</span><span class="o">.</span><span class="n">ssh</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="s1">'azureuser'</span> <span class="c1"># assigned below</span>
        <span class="n">override</span><span class="o">.</span><span class="n">ssh</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="s1">'Pass123!'</span> <span class="c1"># assigned below</span>

        <span class="n">azure</span><span class="o">.</span><span class="n">mgmt_certificate</span> <span class="o">=</span> <span class="s1">'C:\Dev\management_cert.pfx'</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">mgmt_endpoint</span> <span class="o">=</span> <span class="s1">'https://management.core.windows.net'</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">subscription_id</span> <span class="o">=</span> <span class="s1">'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">storage_acct_name</span> <span class="o">=</span> <span class="s1">'mystorage'</span> <span class="c1"># optional. A new one will be generated if not provided.</span>

        <span class="c1"># Ubuntu Precise 12.04 LTS</span>
        <span class="c1">#azure.vm_image = 'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_4-LTS-amd64-server-20140428-en-us-30GB'</span>
        <span class="c1"># Ubuntu Trusty 14.04 LTS</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">vm_image</span> <span class="o">=</span> <span class="s1">'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04-LTS-amd64-server-20140416.1-en-us-30GB'</span>

        <span class="n">azure</span><span class="o">.</span><span class="n">vm_user</span> <span class="o">=</span> <span class="s1">'azureuser'</span> <span class="c1"># defaults to 'vagrant' if not provided</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">vm_password</span> <span class="o">=</span> <span class="s1">'Pass123!'</span> <span class="c1"># min 8 characters. should contain a lower case letter, an uppercase letter, a number and a special character</span>

        <span class="n">azure</span><span class="o">.</span><span class="n">vm_name</span> <span class="o">=</span> <span class="s1">'phpdevbox'</span> <span class="c1"># max 15 characters. contains letters, number and hyphens. can start with letters and can end with letters and numbers</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">cloud_service_name</span> <span class="o">=</span> <span class="s1">'phpdevbox'</span> <span class="c1"># same as vm_name. leave blank to auto-generate</span>
        <span class="c1">#azure.deployment_name = '' # defaults to cloud_service_name</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">vm_location</span> <span class="o">=</span> <span class="s1">'West Europe'</span> <span class="c1"># e.g., West US</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">ssh_private_key_file</span> <span class="o">=</span> <span class="s1">'C:\Dev\id_rsa'</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">ssh_certificate_file</span> <span class="o">=</span> <span class="s1">'C:\Dev\myCert.pem'</span>

        <span class="c1"># Provide the following values if creating a *Nix VM</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">ssh_port</span> <span class="o">=</span> <span class="s1">'22'</span>

        <span class="c1"># Open HTTP port</span>
        <span class="n">azure</span><span class="o">.</span><span class="n">tcp_endpoints</span> <span class="o">=</span> <span class="s1">'80,8000'</span>
    <span class="k">end</span>

    <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">provision</span> <span class="ss">:shell</span><span class="p">,</span> <span class="ss">:path</span> <span class="o">=&gt;</span> <span class="s2">"bootstrap.sh"</span>
<span class="k">end</span>
</pre>
</div>
<p>Here are the parameters you need to change:</p>
<ul>
<li><code>override.ssh.username</code> should be the same as azure.vm_user: the login that Vagrant will use to connect via ssh.</li>
<li><code>override.ssh.password</code> and <code>azure.vm_password</code>: the password.</li>
<li><code>azure.mgmt_certificate</code>: you need to point Vagrant to your Azure management certificate. There are a couple ways to obtain it, the simplest is to <a href="https://github.com/Azure/azure-content/blob/master/articles/linux-create-management-cert.md">extract it from your Publish Settings file</a>.</li>
<li><code>azure.subscription_id</code>: this is your Subscription ID, you will find it in your Publish Settings file as well.</li>
<li><code>azure.vm_image</code>: the name of the image you want to use; I included two examples for Ubuntu Precise Pangolin and Trusty Tahr, but you should double-check for the latest versions, for example using <code>azure vm image list</code> from the CLI tools.</li>
<li><code>azure.vm_name</code>: the host name for your VM.</li>
<li><code>azure.cloud_service_name</code>: this will be the public name for your VM; preferably the same as the host name.</li>
<li><code>azure.vm_location</code>: the name of the data center you want to use; <code>azure vm location list</code> to retrieve a list of locations.</li>
<li><code>azure.ssh_private_key_file</code> and <code>azure.ssh_certificate_file</code> are used to configure your VM's ssh access. We have a tutorial explaining how to <a href="http://azure.microsoft.com/en-us/documentation/articles/linux-use-ssh-key/">generate Windows Azure compatible keys for Linux</a>.</li>
<li><code>azure.tcp_endpoints</code>: let's open both the standard HTTP port (80) and port 8000 in order to run the PHP built-in Web server from userspace.</li>
</ul>
<p>So far, we have configured everything we need to create and provision the Virtual Machine, now we need to configure the development environment for LAMP, i.e. install PHP, MySQL, etc. An easy way to do it is to instruct Vagrant to run a shell script to bootstrap the VM, this is what the following line does:</p>
<div class="highlight highlight-ruby">
<pre><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">provision</span> <span class="ss">:shell</span><span class="p">,</span> <span class="ss">:path</span> <span class="o">=&gt;</span> <span class="s2">"bootstrap.sh"</span>
</pre>
</div>
<p>This will automatically run the <code>bootstrap.sh</code> script in the VM. Let's have a look at the script.</p>
<div class="highlight highlight-bash">
<pre><span class="c">#!/bin/bash</span>

<span class="c"># Add Node repo</span>
add-apt-repository ppa:chris-lea/node.js

apt-get update

<span class="c"># LAMP etc.</span>

<span class="c"># MySQL password</span>
debconf-set-selections <span class="o">&lt;&lt;&lt;</span> <span class="s1">'mysql-server mysql-server/root_password password Pass123!'</span>
debconf-set-selections <span class="o">&lt;&lt;&lt;</span> <span class="s1">'mysql-server mysql-server/root_password_again password Pass123!'</span>

<span class="c"># LAMP</span>
apt-get -q -y install lamp-server^

<span class="c"># Misc PHP stuff</span>
apt-get -q -y install php-apc php5-xdebug php5-intl

<span class="c"># Git</span>
apt-get -q -y install git

<span class="c"># Node</span>
apt-get install -q -y python-software-properties python g++ make nodejs

<span class="c"># Azure CLI</span>
npm install azure-cli -g

<span class="c"># PHP configuration</span>

<span class="k">for</span> f in /etc/php5/cli/php.ini /etc/php5/apache2/php.ini
<span class="k">do</span>
  sed -i <span class="s2">"s/;date.timezone =/date.timezone = Europe\/Paris/"</span> <span class="nv">$f</span>
  <span class="nb">echo</span> <span class="s2">"xdebug.max_nesting_level=250"</span> &gt;&gt; <span class="nv">$f</span>
<span class="k">done</span>
</pre>
</div>
<p>This script basically installs everything you need to run a LAMP application, plus the Azure Command-Line Interface and configures some Symfony2-specific parameters. In more detail:</p>
<ul>
<li>Add <a href="https://chrislea.com/2013/03/15/upgrading-from-node-js-0-8-x-to-0-10-0-from-my-ppa/">Chris Lea's repository</a> to facilitate installing a recent version of Node.</li>
<li>Update the repository database.</li>
<li>Pre-configure a root password for MySQL.</li>
<li>Install the magic LAMP server bundle.</li>
<li>Install Git.</li>
<li>Install Node.JS and a few required packages.</li>
<li>Configures a couple of recommended parameters for Symfony2 in the php.ini.</li>
</ul>
<p>Now all you need to do to start your development environment is to run the magic Vagrant commands to start and provision the VM:</p>
<pre><code>vagrant up --provider=azure vagrant provision </code></pre>
<p>The machine is now ready to use! You can log in using the following command:</p>
<pre><code>vagrant ssh </code></pre>
<p>Now that your development machine environment is entirely automated, you can check it into your source code repository and keep track of any changes you need to make to it. Other developers can use this configuration in order to exactly reproduce the same development environment, automatically.</p>
<p>Let's test our new environment, for example we can check out a sample Symfony2 project:</p>
<pre><code>git clone https://github.com/tomconte/symfony-azure.git </code></pre>
<p>Run Composer:</p>
<pre><code>cd symfony-azure curl -sS https://getcomposer.org/installer | php php composer.phar install </code></pre>
<p>Symfony will prompt you for your configuration information; you can accept all the defaults, but don't forget to give the right root password for MySQL! Now you should be able to check everything is OK and run the demo app:</p>
<pre><code>php app/check.php php app/console server:run 0.0.0.0:8000 </code></pre>
<p>I hope this article gave you a good idea of how to use Vagrant together with Azure to easily spin up automated and standardized development environments.</p>]]></content:encoded>
    </item>
    <item>
      <title>Installing MariaDB Galera Cluster on Windows Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/installing-mariadb-galera-cluster-on-windows-azure</link>
      <pubDate>Thu, 20 Mar 2014 07:46:38 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2014/03/20/installing-mariadb-galera-cluster-on-windows-azure/</guid>
      <description><![CDATA[MariaDB Enterprise Cluster in the Azure Marketplace
Update: this article describes how to install...]]></description>
      <content:encoded><![CDATA[<h2>MariaDB Enterprise Cluster in the Azure Marketplace</h2>
<p>Update: this article describes how to install MariaDB manually on Azure, using the Classic stack. If you are looking for an out-of-the-box solution, you will now find <a href="https://azure.microsoft.com/en-us/blog/mariadb-enterprise-cluster-on-azure/">MariaDB Enterprise Cluster in the Azure Marketplace</a>&nbsp;: a production-grade configuration that you can deploy in one click. Check it out!</p>
<h2><a class="anchor" name="install-mariadb-galera-cluster-and-create-a-vm-image" href="#install-mariadb-galera-cluster-and-create-a-vm-image"></a>Install MariaDB Galera Cluster and create a VM Image</h2>
<p>First, we need to install the MariaDB/Galera bits on a virtual machine that we will use as an image to create all the instances in our cluster. Let's create an Ubuntu 12.04 VM:</p>
<pre><code>azure vm create mariadb b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_3-LTS-amd64-server-20140130-en-us-30GB --location "West Europe" --vm-size small --ssh 22 --ssh-cert myCert.pem azureuser </code></pre>
<p>Then we will install the MariaDB Galera Cluster by following the <a href="https://mariadb.com/kb/en/getting-started-with-mariadb-galera-cluster/">installation instructions</a>:</p>
<pre><code>sudo apt-get install python-software-properties sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db sudo add-apt-repository 'deb http://mirrors.linsrv.net/mariadb/repo/5.5/ubuntu precise main' sudo apt-get update sudo apt-get install mariadb-server </code></pre>
<p>We will now deprovision, shut down and capture the VM image.</p>
<p>In the VM, run:</p>
<pre><code>sudo waagent -deprovision </code></pre>
<p>Then log off and from your admin workstation:</p>
<pre><code>azure vm shutdown mariadb azure vm capture mariadb mariadb-galera-image </code></pre>
<h2><a class="anchor" name="create-the-virtual-machines" href="#create-the-virtual-machines"></a>Create the Virtual Machines</h2>
<p>Now that we have a base image, let's start the virtual machines that will form the cluster. Looking at the <a href="http://www.codership.com/wiki/doku.php?id=galera_deployment">Galera Cluster deployment variants</a>, I am aiming for variant 3a, i.e. a three-node cluster with a distributed load-balancer, in this case using the JDBC driver built-in load balancing capabilities. However I would also like to be able to test variant 3, using the Windows Azure load-balancer. This means I need to have the VMs in the same Cloud Service (to be able to load-balance the traffic) and in a Virtual Network (so I can address each node with a stable IP).</p>
<p>I am going to use three nodes, as recommended in the <a href="https://mariadb.com/kb/en/getting-started-with-mariadb-galera-cluster/">Getting Started</a> document to avoid "split-brain" situations. However, note that Galera also provides an <a href="http://www.codership.com/wiki/doku.php?id=galera_arbitrator">arbitrator</a> that can be used as a lightweight member instead of a full node.</p>
<p>Let's create an Affinity Group:</p>
<pre><code>azure account affinity-group create --location "West Europe" galerawest </code></pre>
<p>And a VNet:</p>
<pre><code>azure network vnet create --address-space 10.0.0.0 --cidr 8 --subnet-name mariadb --subnet-start-ip 10.0.0.0 --subnet-cidr 24 --affinity-group galerawest galeravnet </code></pre>
<p>And now will create three VMs for the cluster. We need to put them in the right VNet/subnet, and we use the <code>--connect</code> option to put them in the same Cloud Service.</p>
<pre><code>azure vm create -v --vm-name mariadb1 --virtual-network-name galeravnet --subnet-names mariadb --affinity-group galerawest \ --vm-size small --ssh 22 --ssh-cert ./certs/myCert.pem --no-ssh-password mariadbcluster mariadb-galera-image tom azure vm create -v --vm-name mariadb2 --virtual-network-name galeravnet --subnet-names mariadb --affinity-group galerawest \ --vm-size small --ssh 23 --ssh-cert ./certs/myCert.pem --no-ssh-password --connect mariadbcluster mariadb-galera-image tom azure vm create -v --vm-name mariadb3 --virtual-network-name galeravnet --subnet-names mariadb --affinity-group galerawest \ --vm-size small --ssh 24 --ssh-cert ./certs/myCert.pem --no-ssh-password --connect mariadbcluster mariadb-galera-image tom </code></pre>
<p>We now have our three nodes. For the sake of simplicity, we won't add a DNS server to the setup and just use the IP addresses of our machines. In our case, these will be 10.0.0.4, 10.0.0.5 and 10.0.0.6.</p>
<h2><a class="anchor" name="configuring-the-cluster" href="#configuring-the-cluster"></a>Configuring the cluster</h2>
<p>We are now going to configure our cluster. I used Codership's <a href="http://www.codership.com/wiki/doku.php?id=mysql_galera_configuration">Galera configuration page</a> as reference, as well as a couple how-to articles:</p>
<ul>
<li><a href="http://planet.mysql.com/entry/?id=616914">http://planet.mysql.com/entry/?id=616914</a></li>
<li><a href="https://www.digitalocean.com/community/articles/how-to-configure-a-galera-cluster-with-mariadb-on-ubuntu-12-04-servers">https://www.digitalocean.com/community/articles/how-to-configure-a-galera-cluster-with-mariadb-on-ubuntu-12-04-servers</a></li>
<li><a href="http://www.severalnines.com/clustercontrol-mysql-galera-tutorial">http://www.severalnines.com/clustercontrol-mysql-galera-tutorial</a></li>
</ul>
<p>First, stop the mysqld that is probably running on all the nodes after the initial installation:</p>
<pre><code>sudo service mysql stop </code></pre>
<p>We are going to create a new configuration file for the cluster parameters, i.e.:</p>
<pre><code>vi /etc/mysql/conf.d/cluster.cnf </code></pre>
<p>And here is the configuration I used:</p>
<pre><code>[mysqld] query_cache_size=0 binlog_format=ROW default-storage-engine=innodb innodb_autoinc_lock_mode=2 query_cache_type=0 bind-address=0.0.0.0 # Galera Provider Configuration wsrep_provider=/usr/lib/galera/libgalera_smm.so #wsrep_provider_options="gcache.size=32G" # Galera Cluster Configuration wsrep_cluster_name="test_cluster" wsrep_cluster_address="gcomm://10.0.0.4,10.0.0.5,10.0.0.6" # Galera Synchronization Congifuration wsrep_sst_method=rsync #wsrep_sst_auth=user:pass # Galera Node Configuration wsrep_node_address="10.0.0.4" wsrep_node_name="mariadb1" </code></pre>
<p>As you can see, I gave my cluster a <code>wsrep_cluster_name</code> and listed the nodes' IP addresses in <code>wsrep_cluster_address</code>. You have copy this configuration file on all the nodes, and change the <code>wsrep_node_name</code> and <code>wsrep_node_address</code> parameters on each.</p>
<p>You should also copy Debian "maintenance" configuration from the first node to all the others, so that the credentials for the special <code>debian-sys-maint</code> user are the same on all nodes. Just copy the contents of <code>/etc/mysql/debian.cnf</code> to the other nodes. If you want to quickly edit a configuration file on a remote node as root, you can use:</p>
<pre><code>ssh -t 10.0.0.6 sudo vi /etc/mysql/debian.cnf </code></pre>
<h2><a class="anchor" name="adding-disks" href="#adding-disks"></a>Adding disks</h2>
<p>I am solely focusing on testing clustering here, but at this point you should really attach a bunch of disks to each node, create a RAID array and change the <code>datadir</code> in MySQL's <code>my.cnf</code> in order to store the data properly.</p>
<p>Create Storage Accounts, one per VM:</p>
<pre><code>for i in {1..3}; do azure storage account create --affinity-group galerawest galerastor$i; done </code></pre>
<p>Let's create 4 x 256GB disks per VM, each group of 4 going to a separate Storage Account:</p>
<pre><code>for j in {1..3}; do for i in {1..4}; do azure vm disk attach-new mariadb$j 256 http://galerastor$j.blob.core.windows.net/disks/mariadb-$j-disk-$i.vhd; done; done </code></pre>
<p>Once you have created and attached the disks, you should see four new devices appear; <code>/dev/sdc</code> to <code>/dev/sdf</code>:</p>
<pre><code>root@mariadb1:~# ls -l /dev/sd* brw-rw---- 1 root disk 8, 0 Mar 18 15:14 /dev/sda brw-rw---- 1 root disk 8, 1 Mar 18 15:14 /dev/sda1 brw-rw---- 1 root disk 8, 16 Mar 18 15:14 /dev/sdb brw-rw---- 1 root disk 8, 17 Mar 18 15:14 /dev/sdb1 brw-rw---- 1 root disk 8, 32 Mar 18 15:14 /dev/sdc brw-rw---- 1 root disk 8, 48 Mar 18 15:14 /dev/sdd brw-rw---- 1 root disk 8, 64 Mar 18 15:14 /dev/sde brw-rw---- 1 root disk 8, 80 Mar 18 15:14 /dev/sdf </code></pre>
<p>We are now going to <a href="https://raid.wiki.kernel.org/index.php/RAID_setup">create a RAID striped array using <code>mdadm</code></a>.</p>
<p>On each machine, install <code>mdadm</code>:</p>
<pre><code>apt-get install mdadm </code></pre>
<p>On Ubuntu, APT insists on installing Postfix as a dependency; just select "No configuration" in the Postfix configuration screen that pops up (you can always run <code>dpkg-reconfigure postfix</code> later).</p>
<p>Now let's create the stripez:</p>
<pre><code>mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=4 /dev/sdc /dev/sdd /dev/sde /dev/sdf </code></pre>
<p>Save the configuration:</p>
<pre><code>mdadm --detail --scan &gt;&gt; /etc/mdadm/mdadm.conf </code></pre>
<p>Create the filesystem, the mount point, and mount the disk:</p>
<pre><code>mkfs.ext4 /dev/md0 mkdir /data mount -t ext4 /dev/md0 /data </code></pre>
<p>If you already have some data, you will need to <a href="http://askubuntu.com/questions/137424/moving-mysql-datadir">move it to the new location</a>. I will go for the easy option of moving the whole datadir and pointing to the new location with a symlink. You should not have to reconfigure AppArmor as instructed in the link above.</p>
<pre><code>service mysql stop mv /var/lib/mysql /data ln -s /data/mysql /var/lib/mysql service mysql start </code></pre>
<p>You can do this operation on each node, one by one.</p>
<h2><a class="anchor" name="starting-the-cluster" href="#starting-the-cluster"></a>Starting the cluster</h2>
<p>Now let's start the cluster on the first node, using <code>--wsrep-new-cluster</code> to create the cluster.</p>
<pre><code>sudo service mysql start --wsrep-new-cluster </code></pre>
<p>And on the other nodes:</p>
<pre><code>sudo service mysql start </code></pre>
<p>You should now be able to see the three nodes in the cluster:</p>
<pre><code>mysql -u root -p -e 'SELECT VARIABLE_VALUE as "cluster size" FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME="wsrep_cluster_size"' +--------------+ | cluster size | +--------------+ | 3 | +--------------+ </code></pre>
<h2><a class="anchor" name="testing-the-cluster" href="#testing-the-cluster"></a>Testing the cluster</h2>
<p>Let's create a test database and table:</p>
<pre><code>mysql -u root -p -e 'CREATE DATABASE playground;' mysql -u root -p -e 'CREATE TABLE playground.jdbctests ( id INT NOT NULL AUTO_INCREMENT, t VARCHAR(64), h VARCHAR(64), d DATE, PRIMARY KEY(id) );' </code></pre>
<p>Now you should be able to connect to any node, insert data in the table, select from another node, etc., and make sure the replication works. If you need to allow access to the database:</p>
<pre><code>grant all on playground.* to 'root'@'%' identified by '&lt;password&gt;'; </code></pre>
<p>Let's use a little test Java program too see how load-balancing works. You can download the <a href="http://dev.mysql.com/downloads/connector/j/">MySQL JDBC driver</a>, and read up on the <a href="http://dev.mysql.com/doc/connector-j/en/connector-j-multi-host-connections.html">multi-host connections</a> capabilities.</p>
<p>I used some sample source code from the following blog post:</p>
<ul>
<li><a href="http://openlife.cc/blogs/2012/june/howto-use-mysql-jdbc-loadbalancer-galera-multi-master-clusters">http://openlife.cc/blogs/2012/june/howto-use-mysql-jdbc-loadbalancer-galera-multi-master-clusters</a></li>
</ul>
<p>I changed the output messages somewhat and added logging of the node where the connection was established, so we can check our connections are load-balanced.</p>
<p>I also modified the JDBC URL:</p>
<ul>
<li>listed the nodes we want to load-balance</li>
<li>added <code>loadBalanceBlacklistTimeout</code> (see <a href="https://blogs.oracle.com/carriergrademysql/entry/how_to_use_jdbc_connector">How to use JDBC (Connector/J) with MySQL Cluster</a>)</li>
<li>added <code>connectTimeout</code> in case a node is down</li>
</ul>
<p>Here is the code:</p>
<div class="highlight highlight-java">
<pre><span class="kn">import</span> <span class="nn">java.sql.*</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MysqlTest</span>
<span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span> <span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
    <span class="o">{</span>
        <span class="n">Connection</span> <span class="n">conn</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

        <span class="k">while</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span>
        <span class="o">{</span>
            <span class="c1">// Connect to MySQL</span>
            <span class="k">try</span>
            <span class="o">{</span>
                <span class="n">String</span> <span class="n">userName</span> <span class="o">=</span> <span class="s">"root"</span><span class="o">;</span>
                <span class="n">String</span> <span class="n">password</span> <span class="o">=</span> <span class="s">"xxx"</span><span class="o">;</span>
                <span class="n">String</span> <span class="n">hosts</span> <span class="o">=</span> <span class="s">"10.0.0.4,10.0.0.5,10.0.0.6"</span><span class="o">;</span>
                <span class="n">String</span> <span class="n">url</span> <span class="o">=</span> <span class="s">"jdbc:mysql:loadbalance://"</span> <span class="o">+</span> <span class="n">hosts</span> <span class="o">+</span> <span class="s">"/playground?loadBalanceBlacklistTimeout=30000&amp;connectTimeout=1000"</span><span class="o">;</span>
                <span class="n">Class</span><span class="o">.</span><span class="na">forName</span> <span class="o">(</span><span class="s">"com.mysql.jdbc.Driver"</span><span class="o">).</span><span class="na">newInstance</span> <span class="o">();</span>
                <span class="n">conn</span> <span class="o">=</span> <span class="n">DriverManager</span><span class="o">.</span><span class="na">getConnection</span> <span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">userName</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
                <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span> <span class="o">(</span><span class="s">"Connected to "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getMetaData</span><span class="o">().</span><span class="na">getURL</span><span class="o">());</span>
            <span class="o">}</span>
            <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span>
            <span class="o">{</span>
                <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">.</span><span class="na">println</span> <span class="o">(</span><span class="s">"Cannot connect to database server: "</span> <span class="o">+</span> <span class="n">e</span><span class="o">);</span>
            <span class="o">}</span>

            <span class="k">try</span>
            <span class="o">{</span>
                <span class="n">Statement</span> <span class="n">s</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="na">createStatement</span> <span class="o">();</span>
                <span class="kt">int</span> <span class="n">count</span><span class="o">;</span>
                <span class="n">count</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">executeUpdate</span><span class="o">(</span><span class="s">"INSERT INTO jdbctests (t, h, d) VALUES ('Inserted from Java code.', @@hostname, now());"</span><span class="o">);</span>
                <span class="n">s</span><span class="o">.</span><span class="na">close</span> <span class="o">();</span>
                <span class="c1">//System.out.println (count + " rows were inserted");</span>
            <span class="o">}</span>
            <span class="k">catch</span> <span class="o">(</span><span class="n">SQLException</span> <span class="n">e</span><span class="o">)</span>
            <span class="o">{</span>
                <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">.</span><span class="na">println</span> <span class="o">(</span><span class="s">"Error when executing SQL statement: "</span> <span class="o">+</span> <span class="n">e</span><span class="o">);</span>
            <span class="o">}</span>

            <span class="c1">// Close connection</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">conn</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
            <span class="o">{</span>
                <span class="k">try</span>
                <span class="o">{</span>
                    <span class="n">conn</span><span class="o">.</span><span class="na">close</span> <span class="o">();</span>
                    <span class="c1">//System.out.println ("Database connection terminated");</span>
                <span class="o">}</span>
                <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span>
                <span class="o">{</span>
                    <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="s">"Error when closing database connection: "</span> <span class="o">+</span> <span class="n">e</span> <span class="o">);</span>
                <span class="o">}</span>
            <span class="o">}</span>
            <span class="k">try</span> <span class="o">{</span> <span class="n">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">1000</span><span class="o">);</span> <span class="o">}</span> <span class="k">catch</span><span class="o">(</span> <span class="n">Exception</span> <span class="n">e</span> <span class="o">)</span> <span class="o">{</span> <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span></pre>
</div>
<p>You can compile and run the program like this:</p>
<pre><code>javac MysqlTest.java java -cp ./mysql-connector-java-5.1.29/mysql-connector-java-5.1.29-bin.jar:. MysqlTest </code></pre>
<p>You should see connections going out to all the nodes in the cluster at random.</p>
<p>You can test the behavior in case of node failure by shutting down one of the nodes (e.g. from the admin portal). You should not see any errors, just one of the nodes disappearing from the log.</p>
<h3><a class="anchor" name="failover" href="#failover"></a>Failover</h3>
<p>To use failover instead of load-balancing, you can change the JDBC URL to:</p>
<pre><code>jdbc:mysql://" + hosts + "/playground?loadBalanceBlacklistTimeout=30000&amp;connectTimeout=1000 </code></pre>
<p>i.e. remove the "loadbalance" option. However this will use standard failover where the secondary host is considered a read-only slave.</p>
<p>To configure multi-master failover:</p>
<pre><code>String hosts = "address=(type=master)(host=10.0.0.4)(protocol=tcp)(port=3306),address=(type=master)(host=10.0.0.5)(protocol=tcp)(port=3306),address=(type=master)(host=10.0.0.6)(protocol=tcp)(port=3306)"; String url = "jdbc:mysql://" + hosts + "/playground?autoReconnect=true&amp;failOverReadOnly=false&amp;connectTimeout=1000"; </code></pre>
<p>This extended syntax allows you to specify that the failover hosts are also masters.</p>
<h2><a class="anchor" name="using-the-windows-azure-load-balancer" href="#using-the-windows-azure-load-balancer"></a>Using the Windows Azure load balancer</h2>
<p>As discussed above, you can also take advantage of the integrated Windows Azure load balancer (WALB) to implement the connection to the cluster.</p>
<p>Let's create a load-balancer set:</p>
<pre><code>azure vm endpoint create --lb-set-name mysql mariadb1 3306 azure vm endpoint create --lb-set-name mysql mariadb2 3306 azure vm endpoint create --lb-set-name mysql mariadb3 3306 </code></pre>
<p>Now you can use your Cloud Service address, mariadbcluster.cloudapp.net in my case, to connect to your cluster. The connections will automatically be load-balanced on all nodes using a round-robin algorithm.</p>
<h2><a class="anchor" name="running-some-benchmarks" href="#running-some-benchmarks"></a>Running some benchmarks</h2>
<p>Here are some notes on how to benchmark your MariaDB cluster using <a href="http://sysbench.sourceforge.net/docs/#database_mode">sysbench</a>.</p>
<p>Install <code>sysbench</code>:</p>
<pre><code>sudo apt-get install sysbench sudo apt-get install mysql-client-core-5.5 </code></pre>
<p>Create a test database using the <code>mysql</code> client:</p>
<pre><code>create database sysbench; grant all on sysbench.* to 'root'@'%' identified by 'xxx!'; </code></pre>
<p>I will run the benchmark client using the load-balanced endpoint address that I created above.</p>
<p>Prepare the benchmark, e.g.:</p>
<pre><code>sysbench --test=oltp --oltp-table-size=1000000 --mysql-host=mariadbcluster.cloudapp.net --mysql-db=sysbench \ --mysql-user=root --mysql-password='xxx' prepare </code></pre>
<p>Run the benchmark, e.g. 1 minute, 4 threads, simple test:</p>
<pre><code>sysbench --test=oltp --oltp-table-size=1000000 --mysql-host=mariadbcluster.cloudapp.net --mysql-db=sysbench \ --mysql-user=root --mysql-password='xxx' --max-time=60 --oltp-read-only=on --num-threads=4 --max-requests=0 run </code></pre>
<p>5 minutes, 16 threads, complex test:</p>
<pre><code>sysbench --test=oltp --oltp-table-size=1000000 --mysql-host=mariadbcluster.cloudapp.net --mysql-db=sysbench \ --mysql-user=root --mysql-password='xxx' --max-time=300 --oltp-read-only=on --num-threads=16 \ --max-requests=0 --oltp-test-mode=complex run </code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Using PowerShell Remoting for disk provisioning automation</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/using-powershell-remoting-for-disk-provisioning-automation</link>
      <pubDate>Mon, 07 Oct 2013 02:50:12 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/10/07/using-powershell-remoting-for-disk-provisioning-automation/</guid>
      <description><![CDATA[Windows Azure PowerShell cmdlets allow you to automate the construction of your infrastructure,...]]></description>
      <content:encoded><![CDATA[<p>Windows Azure PowerShell cmdlets allow you to automate the construction of your infrastructure, typically creating and starting VMs, and attaching additional disks to expand the default storage configuration. This articles show a little recipe to also use <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ee706585(v=vs.85).aspx">Remote PowerShell</a> to create and format a striped volume in the same script.</p>  <h2 id="attaching-disks">Attaching disks</h2>  <p>The first thing to do is to attach some new disks to the virtual machine. I will not detail the process here as it is well documented elsewhere. Here is one way to do it:</p>  <pre><code>Get-AzureVM -ServiceName $sn -Name $n | Add-AzureDataDisk -CreateNew -DiskSizeInGB 128 -DiskLabel &quot;DataDisk0&quot; -LUN 1 | Add-AzureDataDisk -CreateNew -DiskSizeInGB 128 -DiskLabel &quot;DataDisk1&quot; -LUN 2 | Update-AzureVM
</code></pre>

<p>This will create two new raw devices in your virtual machine.</p>

<h2 id="remote-powershell">Remote PowerShell</h2>

<p>We are going to use PowerShell remoting to run scripts on a Virtual Machine without having to connect using Remote Desktop. You can read a complete overview of Remote PowerShell on <a href="http://blogs.technet.com/b/heyscriptingguy/archive/2013/09/07/weekend-scripter-remoting-the-cloud-with-windows-azure-and-powershell.aspx">Scripting Guy's blog</a> for example, but here is what we need to use in a nutshell:</p>

<p>First, we need to retrieve the WinRM URI that will be used to connect to the remote VM:</p>

<pre><code>$uri = Get-AzureWinRMUri -ServiceName $sn -Name $n
</code></pre>

<p>Then we need to construction a PowerShell session option object to work around those pesky security restrictions... Of course I wouldn't advise doing this in production, and read up on how to install the correct certificates.</p>

<pre><code>$option = New-PSSessionOption -SkipCACheck -SkipCNCheck
</code></pre>

<p>And finally we need to collect some credentials, this command will open a UI prompt for you to type in a username and password.</p>

<pre><code>$cred = Get-Credential
</code></pre>

<p>Using these variables, we should be able to start an interactive PowerShell session on the remote host:</p>

<pre><code>Enter-PSSession -ConnectionUri $uri -SessionOption $option -Credential $cred
</code></pre>

<p>Later, we will use <code>Invoke-Command</code> to run a script remotely.</p>

<h2 id="using-diskpart">Using DISKPART</h2>

<p>You can feed DISKPART a text file containing the commands to run. Here is what you typically need to do to create a striped volume using the two disks we just attached. The disk and volume indices are hard-coded, so make sure to change them if your disk configuration differs from the Windows Azure standard! The indices below are based on the standard VM configuration of one persistent disk on C: and one temporary, local disk on D:.</p>

<pre><code>select disk 2
clean
convert dynamic
select disk 3
clean
convert dynamic
create volume stripe disk=2,3
select volume 2
assign letter=E
format fs=ntfs quick
</code></pre>

<h2 id="putting-it-all-together">Putting it all together</h2>

<p>Now we can assemble our two scripts. The first one creates the disks, then calls the second one on the remote instance via Invoke-Command.</p>

<p>remote_createstripe.ps1:</p>

<pre><code>$sn = 'arriis'
$n = 'iis1'

$uri = Get-AzureWinRMUri -ServiceName $sn -Name $n
$option = New-PSSessionOption -SkipCACheck -SkipCNCheck
$cred = Get-Credential

Get-AzureVM -ServiceName $sn -Name $n | Add-AzureDataDisk -CreateNew -DiskSizeInGB 128 -DiskLabel &quot;DataDisk0&quot; -LUN 1 | Add-AzureDataDisk -CreateNew -DiskSizeInGB 128 -DiskLabel &quot;DataDisk1&quot; -LUN 2 | Update-AzureVM

$session = New-PSSession -ConnectionUri $uri -SessionOption $option -Credential $cred

Invoke-Command -Session $session -FilePath C:\dev\ps\diskstripe\createstripe.ps1
</code></pre>

<p>The second script just creates a text file containing the DISKPART commands and runs it.</p>

<p>createstripe.ps1:</p>

<pre><code>New-Item -Name createstripe.txt -ItemType file -Force

Add-Content -Path createstripe.txt &quot;select disk 2&quot;
Add-Content -Path createstripe.txt &quot;clean&quot;
Add-Content -Path createstripe.txt &quot;convert dynamic&quot;
Add-Content -Path createstripe.txt &quot;select disk 3&quot;
Add-Content -Path createstripe.txt &quot;clean&quot;
Add-Content -Path createstripe.txt &quot;convert dynamic&quot;
Add-Content -Path createstripe.txt &quot;create volume stripe disk=2,3&quot;
Add-Content -Path createstripe.txt &quot;select volume 2&quot;
Add-Content -Path createstripe.txt &quot;assign letter=E&quot;
Add-Content -Path createstripe.txt &quot;format fs=ntfs quick&quot;

diskpart /s createstripe.txt
</code></pre>

<p>Hope this helps!</p>]]></content:encoded>
    </item>
    <item>
      <title>Advanced Cookie-based session affinity with Application Request Routing</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/advanced-cookie-based-session-affinity-with-application-request-routing</link>
      <pubDate>Thu, 19 Sep 2013 02:03:43 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/09/19/advanced-cookie-based-session-affinity-with-application-request-routing/</guid>
      <description><![CDATA[As you probably know, the Windows Azure Platform has a built-in load-balancing service that you can...]]></description>
      <content:encoded><![CDATA[<p>As you probably know, the Windows Azure Platform has a built-in load-balancing service that you can typically use to create &quot;farms&quot; of Web servers for high availability and performance scale-out. You can find some detailed information on how to set this up in the Windows Azure documentation, like this article: <a href="http://www.windowsazure.com/en-us/manage/windows/common-tasks/how-to-load-balance-virtual-machines/">Load Balancing Virtual Machines</a>.</p>  <p>One well-known characteristic of the Windows Azure load balancer is that it only provides a round-robin algorithm, and does not support so-called &quot;sticky&quot; sessions. This is not a problem for most Web sites, however it can prove challenging when migrating more complex projects to the Cloud, where the architecture may rely on sticky sessions provided by an on-premises load balancing solution.</p>  <p>We typically recommend using our <a href="http://www.iis.net/downloads/microsoft/application-request-routing">IIS Application Request Routing (ARR) module</a> to implement sticky session if they are required for the application. There are several resources describing this solution, here are a few interesting links:</p>  <ul>   <li><a href="http://www.iis.net/learn/extensions/planning-for-arr/using-the-application-request-routing-module">Using the Application Request Routing Module</a> : this is the standard installation documentation from the IIS team, that you could for example follow to deploy ARR on Windows Virtual Machines. </li>    <li>The excellent <a href="http://coderead.wordpress.com/">Richard Astbury</a> from our partner <a href="http://www.two10degrees.com/">Two10Degrees</a> has an <a href="https://github.com/richorama/AzureARR">AzureARR project hosted on Github</a> that packages ARR in a Web Role for use in Cloud Services. </li> </ul>  <p>However, I recently came across a partner who required more control over the sticky-session algorithm that what ARR provides. In order to better understand the requirements, let's first look at how ARR implements session stickyness.</p>  <p>The technique ARR uses is usually known as a &quot;Cookie Insertion&quot; method. The idea is that ARR, standing in between a client browser and the server farm, will intercept incoming requests, choose a server to &quot;stick&quot; the session to, and will insert in the server response a custom HTTP Cookie that contains the name of the server. To be precise, ARR does not insert the server name in clear text, but rather a hash of the name, but the effect is the same. In subsequent requests, the browser will &quot;honor&quot; the Cookie and send it back to ARR. ARR will then keep on sending the requests in that &quot;session&quot; to the server whose name is in the Cookie.</p>  <p>To illustrate, let's look at some HTTP requests using curl:</p>  <pre><code>$ curl -i http://arriis.cloudapp.net/
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Mon, 29 Jul 2013 10:35:50 GMT
Accept-Ranges: bytes
ETag: &quot;7c57e964478cce1:0&quot;
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
X-ServerName: iis1
Date: Mon, 16 Sep 2013 13:06:44 GMT
Content-Length: 10

hello iis1
</code></pre>

<p>This first request is made directly to the load-balanced endpoint of a Cloud Service consisting of two IIS Virtual Machines. As you can see, the request was served by a VM named &quot;iis1&quot;. If I run this same command again, the next request will be served by &quot;iis2&quot;, the second server in my Cloud Service: (response headers edited for brevity)</p>

<pre><code>$ curl -i http://arriis.cloudapp.net/
HTTP/1.1 200 OK
X-ServerName: iis2

hello iis2
</code></pre>

<p>The &quot;X-ServerName&quot; header is not standard in IIS, I added it manually in order to facilitate troubleshooting the load-balancing setup. You would typically remove it in a production environment! Adding the header can be done using the custom <a href="http://www.iis.net/configreference/system.webserver/httpprotocol/customheaders">HTTP Response Headers section in IIS Manager</a>.</p>

<p>Now that we have seen the standard round-robin algorithm offered by Windows Azure, let's look at the HTTP responses when ARR is added as a front-end to the IIS servers:</p>

<pre><code>$ curl -i http://arrarr.cloudapp.net/
HTTP/1.1 200 OK
Content-Length: 10
Content-Type: text/html
Last-Modified: Mon, 29 Jul 2013 10:34:59 GMT
Accept-Ranges: bytes
ETag: &quot;d092b446478cce1:0&quot;
Server: Microsoft-IIS/7.5
Set-Cookie: ARRAffinity=6e04a77b7c6917600d9ff7ee980a4d4a8a0dfb135247289a9c58cf88587bec23;Path=/;Domain=arrarr.cloudapp.net
X-Powered-By: ASP.NET
X-ServerName: iis2
X-Powered-By: ARR/2.5
X-Powered-By: ASP.NET
Date: Mon, 16 Sep 2013 13:12:43 GMT

hello iis2
</code></pre>

<p>As you can see, we now have a couple additional HTTP headers in the response: the &quot;X-Powered-By: ARR/2.5&quot; confirms that we are going through ARR, and the Set-Cookie header asks the client to add a Cookie named &quot;ARRAffinity&quot; to all subsequent requests.</p>

<p>To simulate a well-behaved browser that honors Cookie headers, we can manually add the Cookie in curl:</p>

<pre><code>$ curl -i --cookie ARRAffinity=6e04a77b7c6917600d9ff7ee980a4d4a8a0dfb135247289a9c58cf88587bec23 http://arrarr.cloudapp.net/
HTTP/1.1 200 OK
X-ServerName: iis2
X-Powered-By: ARR/2.5

hello iis2
</code></pre>

<p>As long as we keep on sending the Cookie header, all our requests will now go to the same server in our farm. Fantastic!</p>

<p>For most use cases, Cookie Insertion Just Works and is very easy to setup using ARR. However, here is one very specific case where it falls short!</p>

<h2 id="where-cookie-insertion-falls-short">Where Cookie Insertion falls short</h2>

<p>My partner has the following situation: he has a number of customers who use a library, or SDK, to connect to his Web services. Session affinity is required for his services to function, because a good amount of customer data is cached in memory in order to accelerate his processing algorithms. Because the client is a code library that does not maintain client-side state, it will not honor Cookies that are sent by the application or ARR: it only sends the header values (and Cookies) as hard-coded in the library.</p>

<p>They currently implement session affinity using a hardware load-balancing device that provides &quot;rule-based load balancing&quot;. This load-balancing technique does not require inserting an additional Cookie: it uses the value of an existing application Cookie to calculate a hash that points to a given server. The load-balancing device is configured to inspect the value of a specific application Cookie which is sent by the library, let's call it &quot;CUserID&quot;.</p>

<p>How can we reproduce this behavior using ARR?</p>

<p>Since we are looking for a hash-based load balancing algorithm, let's look at what ARR offers, extracted from the <a href="http://technet.microsoft.com/en-us/library/dd443524(v=ws.10).aspx">TechNet documentation for ARR</a>:</p>

<ul>
  <li>Server variable hash – Distributes traffic based on a hashed value of a server variable. </li>

  <li>Query string hash – Distributes traffic based on the hashed value of the query string value. When more than one query string name is specified, the concatenated string of the corresponding query string value is used for the hash. </li>

  <li>Request hash – Distributes traffic based on the hashed value of the configured server variable or URL. For example, if the server variable is QUERY_STRING, the hashed value is based on the names in the request query string. </li>
</ul>

<p>Unfortunately, none of these options can be directly used to hash on a Cookie value! </p>

<p>But here is what we can do by adding a simple Rewrite Rule: we can capture the value of the Cookie using a Condition, and then create a custom Server Variable that contains the value of the Cookie. We can then use the Server Variable Hash algorithm on this custom variable to bind the session based on the Cookie value!</p>

<p>Let's look at the ARR configuration step by step.</p>

<p>First, you should disable Client Affinity if it was enabled.</p>

<p>Then, using IIS Manager, in the URL Rewrite module at the server level, add a new rule before the ARR rule (in this example, &quot;myfarm_with_cookie&quot;):</p>

<p><img title="arr1" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr1" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/5618.arr1_27D7EA35.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/5618.arr1_5F00_27D7EA35.png" width="1036" height="41" /></p>

<p>Configure the rule to match regular expressions (not wildcards - this is important for the next steps). Use .* as the pattern to match.</p>

<p><img title="arr2" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr2" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/5127.arr2_22F53679.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/5127.arr2_5F00_22F53679.png" width="596" height="301" /></p>

<p>In the Conditions panel, add a new condition, where you will match a pattern on the HTTP_COOKIE server variable. For the pattern, use something like this:</p>

<pre><code>CookieName=([0-9.A-Za-z]+)
</code></pre>

<p>This will match the Cookie value and store it in {C:1}.</p>

<p><img title="arr3" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr3" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/4578.arr3_6C1AE232.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/4578.arr3_5F00_6C1AE232.png" width="590" height="290" /></p>

<p>Now in the Server Variables panel, create a new variable, e.g. LB_COOKIE in my example, and assign to it the value {C:1} to retrieve the value of the Cookie matched by the condition.</p>

<p><img title="arr4" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr4" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/1263.arr4_35408DEC.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/1263.arr4_5F00_35408DEC.png" width="588" height="222" /></p>

<p>Finally, in the Action panel, just route the request to the server farm like the default rule does.</p>

<p><img title="arr5" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr5" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/1362.arr5_3B87647A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/1362.arr5_5F00_3B87647A.png" width="587" height="224" /></p>

<p>Now you have a brand new server variable you can use to base your load-balancing on!</p>

<p>Just go in the ARR module configuration and select the &quot;Server variable hash&quot; algorithm, and type the name of your variable in the text box, e.g. LB_COOKIE in my case.</p>

<p><img title="arr6" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="arr6" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/2705.arr6_04AD1034.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/2705.arr6_5F00_04AD1034.png" width="490" height="227" /></p>

<p>You should now have a brand new Cookie-based load balancing setup, with no Cookie insertion.</p>]]></content:encoded>
    </item>
    <item>
      <title>Deploying Cloudera CDH on Windows Azure Virtual Machines using Cloudera Manager</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/deploying-cloudera-cdh-on-windows-azure-virtual-machines-using-cloudera-manager</link>
      <pubDate>Fri, 14 Jun 2013 02:07:49 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/06/14/deploying-cloudera-cdh-on-windows-azure-virtual-machines-using-cloudera-manager/</guid>
      <description><![CDATA[Updated 3/20/2014 to use the latest version of the command-line tool.  Introduction  In this...]]></description>
      <content:encoded><![CDATA[<p><a class="anchor" href="#introduction" name="introduction"><span class="octicon octicon-link"></span></a></p>  <p><strong>Updated</strong> 3/20/2014 to use the latest version of the command-line tool.</p>  <h2>Introduction</h2>  <p>In this tutorial, I would like to show you how to install CDH, the <a href="http://www.cloudera.com/content/cloudera/en/products/cdh.html">Cloudera Hadoop distribution</a> on Windows Azure Virtual Machines, using their automated installer and management toold called <a href="http://www.cloudera.com/content/cloudera/en/products/cloudera-manager.html">Cloudera Manager</a>.</p>  <p>For this tutorial, since we will be deploying the platform on Linux VMs, I am going to assume that we are going to install everything from a Linux command line exclusively, using our <a href="https://github.com/WindowsAzure/azure-sdk-tools-xplat">cross-platform CLI tools</a> and the Management Portal Web interface when required. In other words: you don't need a Windows machine to follow this tutorial ;-)</p>  <p>In summary, here are the steps that we are going to follow:</p>  <ul>   <li>Preparing our SSH keys for authentication on the cluster </li>    <li>Creating the Virtual Network that defines which internal IP addresses the VMs will use </li>    <li>Creating our VMs </li>    <li>Configuring the DNS so that all machines can resolve their internal names and IP addresses </li>    <li>Run the Cloudera Manager installer to create the Hadoop cluster </li> </ul>  <p>I hope you enjoy this document, and please send feedback!</p>  <h2><a class="anchor" href="#preparing-our-ssh-keys" name="preparing-our-ssh-keys"><span class="octicon octicon-link"></span></a>Preparing our SSH keys</h2>  <p>First, let's create an SSH key and certificate to use for authentication. This will be useful in order to log on to all our VMs without being prompted for passwords. You can find a more detailed SSH procedure in the <a href="http://www.windowsazure.com/en-us/manage/linux/how-to-guides/ssh-into-linux/">Windows Azure documentation for Linux Virtual Machines</a>. This is slightly different from what you would do usually to generate new SSH keys (i.e., use <code>ssh-keygen</code>).</p>  <pre><code>$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout myPrivateKey.key -out myCert.pem
Generating a 2048 bit RSA private key
....+++
.......................................................+++
writing new private key to 'myPrivateKey.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:92
Locality Name (eg, city) []:Issy-les-Moulineaux
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Microsoft
Organizational Unit Name (eg, section) []:DPE
Common Name (e.g. server FQDN or YOUR name) []:Thomas Conte
Email Address []:tconte@microsoft.com
</code></pre>

<p>Windows Azure needs the SSH public key to be embedded in a PEM formatted certificate, but you could enter any information you want, it doesn't matter. It's only the generated public key we are interested in!</p>

<p>Let's secure the key and move it to our <code>.ssh</code> directory so that it used by default.</p>

<pre><code>$ chmod 600 myPrivateKey.key
$ mv myPrivateKey.key ~/.ssh/id_rsa
</code></pre>

<p>I have found that Cloudera Manager does not like the private key format very much though. Let's convert it right now and keep the generated file somewhere, we will need it later:</p>

<pre><code>$ openssl rsa -in ~/.ssh/id_rsa -out PrivateKeyCDH
</code></pre>

<h2><a class="anchor" href="#creating-the-virtual-network" name="creating-the-virtual-network"><span class="octicon octicon-link"></span></a>Creating the Virtual Network</h2>

<p>We will use the cross-platform command-line tools to create our VMs. If you haven't got them installed, first go through the steps to install the <a href="http://www.windowsazure.com/en-us/develop/nodejs/how-to-guides/command-line-tools/">Windows Azure Command-Line Tools for Mac and Linux</a>. I won't go into the details here, but once installed you should be able to type <code>azure</code> from the command line and get a friendly response back, plus you must import your publish settings so that the CLI is connected to your Windows Azure subscription.</p>

<p><strong>Update</strong>: you can now create a Virtual Network using the command-line tool.</p>

<p>Create the Affinity Group:</p>

<pre><code>azure account affinity-group create --location &quot;West Europe&quot; ezwest
</code></pre>

<p>Register the DNS server:</p>

<pre><code>azure network dnsserver register -i mycdh 10.0.0.4
</code></pre>

<p>Create the Virtual Network:</p>

<pre><code>azure network vnet create --address-space 10.0.0.0 --cidr 8 --subnet-name cdh --subnet-start-ip 10.0.0.0 --subnet-cidr 24 --affinity-group ezwest --dns-server-id mycdh cdhvnet
</code></pre>

<p>Read on if you want to understand more about the Virtual Network configuration, otherwise you can just skip to the next step!</p>

<p>The first thing we need to create is an Affinity Group, a kind of alias that will represent the datacenter where all our infrastructure will be created. In the Portal, on the left-hand side, at the bottom of the menu, click on Settings, then click on the Affinity Groups tab at the top of the main area. Click on Add an Affinity group, give it a name (I will use &quot;cdh-AG&quot;) and select the datacenter (I will use North Europe).</p>

<p>The simplest way to create a complete Virtual Network configuration is to import a ready-made configuration file. Here is the one I used, you can save it into a file and edit it as needed. It is also possible to create a Virtual Network &quot;manually&quot; from the Portal and then export this configuration to a file to inspect or modify it (see Annex 1).</p>

<p>If you look at the XML configuration file below, you will find the following information:</p>

<ul>
  <li>The Virtual Network name, <code>cdh</code>, and the Affinity Group where it will be created, <code>cdh-AG</code> </li>

  <li>The address space that will be used to assign IP addresses to the Virtual Machines (<code>10.0.0.0/20</code>) </li>

  <li>Within this address space, one or several subnets the VMs will be deployed in, <code>10.0.0.0/23</code>, each being given a name that you can use when configuring the Virtual Machines (<code>Subnet-1</code>) </li>

  <li>The IP address of a DNS server (more about this below), which of course resides within the address space of the Virtual Network, i.e. <code>10.0.0.4</code> in this case </li>

  <li>A reference from the Virtual Network to the DNS server: all VMs will get assigned this DNS when they request their network configuration from Windows Azure </li>
</ul>

<p>The trick here is: how did I choose <code>10.0.0.4</code> as the address of the DNS server? I know by experience that Windows Azure will allocate IP addresses sequentially within the Subnet, and that in this case, the first one it will allocate is <code>10.0.0.4</code>. In short, this means that the first VM I start will become the DNS for the rest of the machines in the Virtual Network.</p>

<p>This is really a shortcut, and the longer way is to create the Virtual Network in the Portal, start the first VM, make a note of its IP address, and then register it as a DNS using <em>New</em>, <em>Networks</em>, <em>Virtual Network</em>, <em>Register DNS Server</em>. Importing the configuration file is just a faster way to get everything configured!</p>

<p>And now the configuration file:</p>

<div class="highlight highlight-xml">
  <pre><span class="nt">&lt;NetworkConfiguration</span> <span class="na">xmlns:xsd=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema&quot;</span> <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="na">xmlns=</span><span class="s">&quot;http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration&quot;</span><span class="nt">&gt;</span>
  <span class="nt">&lt;VirtualNetworkConfiguration&gt;</span>
    <span class="nt">&lt;Dns&gt;</span>
      <span class="nt">&lt;DnsServers&gt;</span>
        <span class="nt">&lt;DnsServer</span> <span class="na">name=</span><span class="s">&quot;mycdh&quot;</span> <span class="na">IPAddress=</span><span class="s">&quot;10.0.0.4&quot;</span> <span class="nt">/&gt;</span>
      <span class="nt">&lt;/DnsServers&gt;</span>
    <span class="nt">&lt;/Dns&gt;</span>
    <span class="nt">&lt;VirtualNetworkSites&gt;</span>
      <span class="nt">&lt;VirtualNetworkSite</span> <span class="na">name=</span><span class="s">&quot;cdh&quot;</span> <span class="na">AffinityGroup=</span><span class="s">&quot;cdh-AG&quot;</span><span class="nt">&gt;</span>
        <span class="nt">&lt;AddressSpace&gt;</span>
          <span class="nt">&lt;AddressPrefix&gt;</span>10.0.0.0/20<span class="nt">&lt;/AddressPrefix&gt;</span>
        <span class="nt">&lt;/AddressSpace&gt;</span>
        <span class="nt">&lt;DnsServersRef&gt;</span>
          <span class="nt">&lt;DnsServerRef</span> <span class="na">name=</span><span class="s">&quot;mycdh&quot;</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;/DnsServersRef&gt;</span>
        <span class="nt">&lt;Subnets&gt;</span>
          <span class="nt">&lt;Subnet</span> <span class="na">name=</span><span class="s">&quot;Subnet-1&quot;</span><span class="nt">&gt;</span>
            <span class="nt">&lt;AddressPrefix&gt;</span>10.0.0.0/23<span class="nt">&lt;/AddressPrefix&gt;</span>
          <span class="nt">&lt;/Subnet&gt;</span>
        <span class="nt">&lt;/Subnets&gt;</span>
      <span class="nt">&lt;/VirtualNetworkSite&gt;</span>
    <span class="nt">&lt;/VirtualNetworkSites&gt;</span>
  <span class="nt">&lt;/VirtualNetworkConfiguration&gt;</span>
<span class="nt">&lt;/NetworkConfiguration&gt;</span></pre>
</div>

<p>Make sure you use your Affinity Group name in the AffinityGroup attribute of the VirtualNetworkSite element!</p>

<p>To import this configuration, in the Portal, go to <em>New</em>, <em>Networks</em>, <em>Virtual Network</em>, and select <em>Import Configuration</em>. The Virtual Network will be created and will then appear in the Networks module in the Portal.</p>

<h2><a class="anchor" href="#creating-our-admindns-vm" name="creating-our-admindns-vm"><span class="octicon octicon-link"></span></a>Creating our Admin/DNS VM</h2>

<p>We will prepare a Storage Account in our new Affinity Group, where the Virtual Machine disks will be stored. We can do this from the command line:</p>

<pre><code>$ azure account storage create --affinity-group cdh-AG cdhstorage
</code></pre>

<p>Now let's find a good Linux image to use. You can use the following command to list all current images.</p>

<pre><code>$ azure vm image list
</code></pre>

<p>In my case I am going to choose Ubuntu 12.04:</p>

<pre><code>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_2-LTS-amd64-server-20130415-en-us-30GB
</code></pre>

<p>Now let's create our first VM. This VM will become our DNS server and will also be used to run the Cloudera Manager. For this first step we ask for a &quot;small&quot; size since we don't need to run much stuff. We specify that we want to open the SSH port for remote shell, and add our SSH certificate so that the VM is properly setup for authentication. We also specify our Virtual Network and Subnet so that the VM gets assigned an IP address from the prefix we selected. Finally, we need to specify the Affinity Group, obviously the same we used when creating the Virtual Network.</p>

<pre><code>$ azure vm create --virtual-network-name cdh --subnet-names Subnet-1 --affinity-group cdh-AG --vm-size small \
--ssh 22 --ssh-cert ./myCert.pem mycdh \
b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_2-LTS-amd64-server-20130415-en-us-30GB tom 'xxx'
info:    Executing command vm create
+ Looking up image
+ Looking up cloud service
+ Creating cloud service
+ Retrieving storage accounts
+ Configuring certificate
+ Creating VM
info:    vm create command OK
</code></pre>

<p>You can check the configuration details of your new VM using the following command:</p>

<pre><code>$ azure --json vm list
</code></pre>

<p>This will output a JSON document containing the details for all your VMs, and the one you just created should look like this:</p>

<pre><code>{
    &quot;DNSName&quot;: &quot;mycdh.cloudapp.net&quot;,
    &quot;VMName&quot;: &quot;mycdh&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.4&quot;,
    &quot;InstanceStatus&quot;: &quot;ReadyRole&quot;,
    &quot;InstanceSize&quot;: &quot;Small&quot;,
    &quot;InstanceStateDetails&quot;: &quot;&quot;,
    &quot;OSVersion&quot;: &quot;&quot;,
    &quot;Image&quot;: &quot;b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_2-LTS-amd64-server-20130415-en-us-30GB&quot;,
    &quot;DataDisks&quot;: &quot;&quot;,
    &quot;Network&quot;: {
        &quot;Endpoints&quot;: [
            {
                &quot;LocalPort&quot;: &quot;22&quot;,
                &quot;Name&quot;: &quot;ssh&quot;,
                &quot;Port&quot;: &quot;22&quot;,
                &quot;Protocol&quot;: &quot;tcp&quot;,
                &quot;Vip&quot;: &quot;137.117.198.34&quot;
            }
        ]
    }
}
</code></pre>

<p>As you can see, the internal IP address assigned to your VM is indeed <code>10.0.0.4</code>, and thus will be used as DNS server by all the VMs we will create next. Now let's turn this VM into an actual DNS!</p>

<p>You can now ssh into your new VM, if your SSH configuration is correct you should be able to log in without a password prompt. Otherwise just make sure you correctly copied your private key file into your <code>.ssh</code> directory.</p>

<h2><a class="anchor" href="#configuring-the-dns-using-bind" name="configuring-the-dns-using-bind"><span class="octicon octicon-link"></span></a>Configuring the DNS using BIND</h2>

<p>Now if you look at your DNS configuration, you should see something like this:</p>

<pre><code># cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.0.0.4
search 52601348a18744ec9cbf2e9c992072da.mycdh.3744114705.europewest.internal.cloudapp.net
</code></pre>

<p>This is fine, however in order to install our DNS we need to be able to resolve names until we have installed BIND on our VM! For now, temporarily comment out 10.0.0.4 and use a public DNS:</p>

<pre><code>#nameserver 10.0.0.4
nameserver 8.8.8.8
</code></pre>

<p>Now you should have DNS resolution in order to run apt-get and install BIND:</p>

<pre><code># apt-get install bind9
# /usr/sbin/named
</code></pre>

<p>Now that BIND is running, you should be able to switch your DNS back to <code>10.0.0.4</code> in <code>/etc/resolv.conf</code>.</p>

<p>We are going to configure BIND in order to resolve our host names. We don't need a real domain, as this name resolution will only take place within the bounds of our deployment; I am going to use a fake TLD, &quot;internal&quot;, and setup the forward and reverse zone files.</p>

<p>You can edit named.conf.local and add the following lines:</p>

<pre><code>zone &quot;internal&quot; {
        type master;
        file &quot;/etc/bind/db.internal&quot;;
};

zone &quot;10.in-addr.arpa&quot; {
        type master;
        file &quot;/etc/bind/db.10&quot;;
};
</code></pre>

<p>You then basically copy db.empty as a starting point and add your configuration. This should look like this:</p>

<p>In <code>db.internal</code>:</p>

<pre><code>$TTL    86400
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                          86400 )       ; Negative Cache TTL
;
@       IN      NS      ns.internal.
ns      IN      A       10.0.0.4
mycdh   IN      A       10.0.0.4
</code></pre>

<p>In <code>db.10</code>:</p>

<pre><code>$TTL    86400
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                          86400 )       ; Negative Cache TTL
;
@       IN      NS      ns.internal.
4.0.0   IN      PTR     mycdh.internal.
</code></pre>

<p>Now you should run a few tests using <code>nslookup</code> to make sure everything is in order, e.g. the forward and reverse resolutions work.</p>

<p>To make BIND re-read its configuration file, find its PID using <code>ps aux</code> or in <code>/var/run/named/named.pid</code>, and send it a SIGHUP signal, i.e. <code>kill -HUP (pid)</code>.</p>

<h2><a class="anchor" href="#initializing-the-cluster" name="initializing-the-cluster"><span class="octicon octicon-link"></span></a>Initializing the cluster</h2>

<p>We are now ready to create more virtual machines! These will be the actual nodes in our Hadoop cluster, and we will name them with numeric suffixes so that we can identify them easily. We will use a similar command line as above, except we are going to request larger intance sizes.</p>

<p>You can quickly create four instances from the Bash prompt using a mini-script like this:</p>

<pre><code>$ for i in 1 2 3 4; do
&gt; azure vm create --virtual-network-name cdh --subnet-names Subnet-1 --affinity-group cdh-AG --vm-size large --ssh 22 --ssh-cert ./myCert.pem mycdh-$i b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_2-LTS-amd64-server-20130415-en-us-30GB tom 'Pass123!'
&gt; done
</code></pre>

<p>This should give you four more machines in a few minutes. You can quickly check the DNS names and IP addresses allocated like this for example:</p>

<pre><code>$ azure --json vm list | egrep '(DNSName|IPAddress)'
    &quot;DNSName&quot;: &quot;mycdh-2.cloudapp.net&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.6&quot;,
    &quot;DNSName&quot;: &quot;mycdh-3.cloudapp.net&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.7&quot;,
    &quot;DNSName&quot;: &quot;mycdh-4.cloudapp.net&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.8&quot;,
    &quot;DNSName&quot;: &quot;mycdh-1.cloudapp.net&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.5&quot;,
    &quot;DNSName&quot;: &quot;mycdh.cloudapp.net&quot;,
    &quot;IPAddress&quot;: &quot;10.0.0.4&quot;,
</code></pre>

<p>These are the name and addresses that we will add to our BIND configuration in order to resolve all the names and addresses.</p>

<p>We are then going to add some additional storage, for my test I will use 150GB drives, however you could use as much as 1TB per disk, and even add several of these disks to each machine if you need even more storage.</p>

<pre><code>$ for i in 1 2 3 4; do
&gt; azure vm disk attach-new mycdh-$i 150
&gt; done
</code></pre>

<p>Now we are going to switch to the first machine we created, our &quot;admin&quot; machine, to further configure our new VMs. Let's copy our SSH key over so that we can connect to all other VMs from our first one:</p>

<pre><code>$ scp ~/.ssh/id_rsa mycdh.cloudapp.net:.ssh/
</code></pre>

<p>Once logged on our admin machine, let's add the IP addresses of our nodes to BIND's configuration (use <code>kill -HUP</code> again on the <code>named</code> process to force BIND to re-read its configuration files).</p>

<p>In <code>db.internal</code>:</p>

<pre><code>mycdh-1 IN      A       10.0.0.5
mycdh-2 IN      A       10.0.0.6
mycdh-3 IN      A       10.0.0.7
mycdh-4 IN      A       10.0.0.8
</code></pre>

<p>In <code>db.10</code>:</p>

<pre><code>5.0.0   IN      PTR     mycdh-1.internal.
6.0.0   IN      PTR     mycdh-2.internal.
7.0.0   IN      PTR     mycdh-3.internal.
8.0.0   IN      PTR     mycdh-4.internal.
</code></pre>

<p>To make things easier, you can also add the following line to <code>/etc/resolv.conf</code> in order to make &quot;internal&quot; the default domain searched, so that you don't have to type the complete name each time:</p>

<pre><code>search internal
</code></pre>

<p>You should now be able to log on to all three other VMs using their FQDN, this will initialize the SSH trust and make sure you can connect without being prompted for a password.</p>

<pre><code>$ ssh mycdh-1
$ ssh mycdh-2
$ ssh mycdh-3
$ ssh mycdh-4
</code></pre>

<p>Now we have to make it so we can run commands on all nodes as root via <code>sudo</code>. We are going to overwrite the configuration created by the Windows Azure Agent at first boot (<code>/usr/sbin/waagent</code>) to make sure we can <code>sudo</code> without a password. Of course you should replace the account there by the account you created when you ran the <code>azure vm create</code> commands. First on our admin machine:</p>

<pre><code># echo &quot;tom ALL = (ALL) NOPASSWD: ALL&quot; &gt; /etc/sudoers.d/waagent
</code></pre>

<p>Let's do this on all our nodes:</p>

<pre><code>$ for i in mycdh-1 mycdh-2 mycdh-3 mycdh-4
&gt; do
&gt; ssh -t $i &quot;sudo bash -c 'echo \&quot;tom ALL = (ALL) NOPASSWD: ALL\&quot; &gt; /etc/sudoers.d/waagent'&quot;
&gt; done
</code></pre>

<p>Now you can run commands as root without ever being prompted for a password. What really happens is that you ssh implicitly using your current user name (&quot;tom&quot; in my case), since it exists on all machines. You can then execute &quot;sudo&quot; via ssh, and pass it a command to execute as root in turn, e.g.:</p>

<pre><code>$ ssh tomcdh-2 sudo id
</code></pre>

<p>If you get warnings from sudo about not being able to resolve host names, you need to add the &quot;search&quot; option to resolv.conf on the other machines as well:</p>

<pre><code>$ for i in mycdh-1 mycdh-2 mycdh-3 mycdh-4
&gt; do
&gt; ssh -t $i &quot;sudo bash -c 'echo \&quot;search internal\&quot; &gt;&gt; /etc/resolv.conf'&quot;
&gt; done
</code></pre>

<p>You will then need to partition, format and mount the new disk on every machine. You can easily log in as root using:</p>

<pre><code>$ ssh -t mycdh-1 sudo -i
</code></pre>

<p>And then use the following shell snippet. It will partition with <code>sfdisk</code> the Windows Azure volume that you attached previously using <code>azure vm disk attach-new</code>, format it, add it to <code>fstab</code>, and finally mount it on <code>/data</code>.</p>

<pre><code>echo &quot;,&quot; | sfdisk /dev/sdc
mkfs.ext3 /dev/sdc1
echo &quot;/dev/sdc1 /data ext3 defaults 0 0&quot; &gt;&gt; /etc/fstab
mkdir /data
mount /data
</code></pre>

<p>Do this for all four VMs. Now you should have a formatted disk mounted on <code>/data</code> on every of your future nodes.</p>

<h2><a class="anchor" href="#preparing-for-installation" name="preparing-for-installation"><span class="octicon octicon-link"></span></a>Preparing for installation</h2>

<p>Let's <code>apt-get update</code> on our admin VM and all four nodes:</p>

<pre><code>$ sudo apt-get update
$ for i in mycdh-1 mycdh-2 mycdh-3 mycdh-4; do ssh -t $i sudo apt-get update; done
</code></pre>

<p>I am now looking at the <a href="http://www.cloudera.com/content/cloudera-content/cloudera-docs/CM4Free/latest/Cloudera-Manager-Free-Edition-Installation-Guide/Cloudera-Manager-Free-Edition-Installation-Guide.html">&quot;Cloudera Manager Free Edition Installation Guide&quot;</a>, and more precisely the section &quot;Automated Installation of Cloudera Manager and CDH&quot;.</p>

<p>Let's download and run the installer:</p>

<pre><code>$ wget http://archive.cloudera.com/cm4/installer/latest/cloudera-manager-installer.bin
$ chmod +x ./cloudera-manager-installer.bin
$ sudo ./cloudera-manager-installer.bin
</code></pre>

<p>You should now see the beautiful retro-vintage text-based Cloudera Manager installation screen! Accept (and read) all the licenses, then the installation will proceed and, if everything goes well, you will be presented with the following success screen.</p>

<p>As instructed, we need to connect to the Web application on port 7180. Since we are running on a server VM, we don't have a browser we can launch to open localhost, so let's instead open the port to the outside world:</p>

<pre><code>$ azure vm endpoint create mycdh 7180
</code></pre>

<blockquote>
  <p>Note :this currently fails in the CLI, see <a href="https://github.com/WindowsAzure/azure-sdk-tools-xplat/issues/543">Issue #543</a>. For now we have to use the Portal to create endpoints on VMs that are part of a Virtual Network.</p>
</blockquote>

<p>In the Management Portal, go to the Virtual Machines module, and select your admin VM in the list (&quot;mycdh&quot; in my case), then go to the Endpoints tab. You should see a rule fort TCP port 22 (SSH). On the dialog that appears, select Add Endpoint, click Next, then give a name to your new endpoint (e.g. &quot;ClouderaManager&quot;), and enter 7180 for the Public and Private port fields. The new rule should now appear in the list of Endpoints.</p>

<p>Now you can connect to the Cloudera Manager interface using the external URL for your admin machine (e.g. in my case, <a href="http://mycdh.cloudapp.net:7180/">http://mycdh.cloudapp.net:7180/</a>) and start the Web-based installation wizard.</p>

<h2><a class="anchor" href="#using-the-cloudera-manager-to-deploy-the-hadoop-cluster" name="using-the-cloudera-manager-to-deploy-the-hadoop-cluster"><span class="octicon octicon-link"></span></a>Using the Cloudera Manager to deploy the Hadoop cluster</h2>

<p>On the &quot;Thank you&quot; screen, click Continue.</p>

<p>On the &quot;Specify hosts&quot;, use the following pattern to describe your hosts:</p>

<pre><code>mycdh-[1-4].internal
</code></pre>

<p>You need to specify the FQDN, otherwise you will run into problems later when the JobTracker looks up the IP address for validation: the reverse lookup must exactly match the hostname used.</p>

<p>On the first &quot;Cluster Installation&quot; screen, I removed Impala but otherwise I left all the other options by default.</p>

<p>On the &quot;SSH credentials&quot; screen, you will need to check &quot;Another user&quot; and enter the username used to log on your VMs. You will also need to check &quot;All hosts accept same private key&quot; and provide the key we generated above. I run <a href="http://www.cygwin.com/">Cygwin</a> on my Windows machine and I used this command to copy the key from my Linux workstation in Windows Azure to <code>C:\TEMP</code>:</p>

<pre><code>$ scp tom@tombuntu.cloudapp.net:~/cloudera_beta/PrivateKeyCDH /cygdrive/c/TEMP
</code></pre>

<p>Click Continue and confirm that you are not using an SSH passphrase.</p>

<p>On the &quot;Installation in progress&quot; screen, everything should install successfully. If you encounter SSH credentials issue, this screen will help you troubleshoot the problem.</p>

<p>The next screen will install all the selected parcels to your machines.</p>

<p>On the &quot;Host inspection&quot; screen, everything should be <strong>green</strong>! Pay special attention to host name resolution warnings at this stage, they may cause problems later if they are not resolved now.</p>

<p>On the &quot;CDH4 services&quot; screen, choose the services you want to install. I will choose &quot;Core with Real-Time Delivery&quot; for a base Hadoop stack plus HBase. If you want to fine-tune which services will run on which hosts, you can click on &quot;Inspect Role Assignments&quot; to check what will run where. You will see that the first machine gets a larger share of services, and in production you might want to make it a larger instance size.</p>

<p>On the &quot;Database setup&quot; screen I kept the Embedded Database, then clicked on Test Connection, then Continue.</p>

<p>When you reach the &quot;Review configuration changes&quot; page, remove the <code>/mnt/resource</code> disk from all nodes running the HDFS service (this is a local, non-persistent disk and we don't want to store data on it). You will also need to lower the tolerance accordingly (i.e. set it to 0 in this case). Likewise for the NameNode and MapReduce services, remove all occurences of <code>/mnt/resource</code>.</p>

<p><strong>That's it!</strong> All the services should start successfully. Congratulations!</p>

<h2><a class="anchor" href="#accessing-the-hue-web-ui" name="accessing-the-hue-web-ui"><span class="octicon octicon-link"></span></a>Accessing the Hue Web UI</h2>

<p>Now you may want to access the nice Web UI that Cloudera integrates in their distribution. On the main Services page, click on &quot;hue1&quot; and you will see a status page where you will find a &quot;Hue Web UI&quot; button. Press it, and it should fail! The management portal redirects you to the internal address of the Hue node, e.g. <code>http://mycdh-1.internal:8888/</code>, and this address is not known from the outside world. You should use the corresponding public address, i.e. <code>mycdh-1.cloudapp.net</code> in my case, and open the port (8888) by creating an endpoint in the Windows Azure Management Portal. You can proceed in the same manner whenever you need to access a service directly on one of the nodes.</p>

<h2><a class="anchor" href="#annex-1-manually-creating-a-virtual-network" name="annex-1-manually-creating-a-virtual-network"><span class="octicon octicon-link"></span></a>Annex 1: Manually creating a Virtual Network</h2>

<p>Connect to your Windows Azure management portal, and select New, Networks, Virtual Network, Quick Create, and just select a name and a location (data center) for your Virtual Network; all our VMs will later be created in the data center you select. You can also select the size and the address prefix, I will just go for the defaults.</p>

<p>Once the Virtual Network is created, click on its name and then on Dashboard to see how it was configured. You will see that an Affinity Group was automatically created, for example if you named your Virtual Network &quot;cdh&quot; if will be associated with a &quot;cdh-AG&quot; Affinity Group. Now if you click on Configure, you will find another important piece of information: a Subnet was automatically created for you, typically named &quot;Subnet-1&quot; if you went for the Quick Create defaults. We will need these two parameters later to create our Virtual Machines.</p>

<h2><a class="anchor" href="#annex-2-installation-the-windows-azure-command-line-tools-for-maclinux" name="annex-2-installation-the-windows-azure-command-line-tools-for-maclinux"><span class="octicon octicon-link"></span></a>Annex 2: Installation the Windows Azure command-line tools for Mac/Linux</h2>

<p>First install Node.JS, don't forget to check on the <a href="http://nodejs.org/download/">Node.JS downloads</a> page what is the latest version!</p>

<pre><code>$ sudo apt-get update
$ sudo apt-get install build-essential
$ wget http://nodejs.org/dist/v0.10.5/node-v0.10.5.tar.gz
$ tar xf node-v0.10.5.tar.gz
$ cd node-v0.10.5/
$ ./configure
$ sudo make install
</code></pre>

<p>Then install the Windows Azure CLI using <code>npm</code>:</p>

<pre><code>$ sudo npm install azure-cli -g
</code></pre>

<p>Then initialize the Windows Azure CLI:</p>

<pre><code>$ azure account import Azure-credentials.publishsettings
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Uploading large files to Windows Azure Blob Storage using Python</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/uploading-large-files-to-windows-azure-blob-storage-using-python</link>
      <pubDate>Sat, 20 Apr 2013 03:22:02 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/04/20/uploading-large-files-to-windows-azure-blob-storage-using-python/</guid>
      <description><![CDATA[In my last post, I shown some simple examples of uploading files to Blob Storage directly from the...]]></description>
      <content:encoded><![CDATA[<p>In my last post, I shown some simple examples of uploading files to Blob Storage directly from the Linux command-line using the Windows Azure Python SDK.</p>
<p>There is a limitation with this simple method: you cannot upload file larger than 64 MB without using a block-based upload approach, that will let you upload files up to 200 GB in size. We have some <a href="http://www.windowsazure.com/en-us/develop/python/how-to-guides/blob-service/#large-blobs">sample Python code in the Windows Azure Python Developer Center</a> that shows how to upload large files using blocks.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to interact with Windows Azure Blob Storage from Linux using Python</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/how-to-interact-with-windows-azure-blob-storage-from-linux-using-python</link>
      <pubDate>Wed, 17 Apr 2013 08:41:29 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/04/17/how-to-interact-with-windows-azure-blob-storage-from-linux-using-python/</guid>
      <description><![CDATA[We have many Windows Azure SDKs that you can use on Linux to access Windows Azure Blob Storage and...]]></description>
      <content:encoded><![CDATA[<p>We have many Windows Azure SDKs that you can use on Linux to access Windows Azure Blob Storage and upload or download files, all hosted on <a href="https://github.com/windowsazure">GitHub</a>. For example, you could write scripts in <a href="https://github.com/WindowsAzure/azure-sdk-for-python">Python</a> or <a href="https://github.com/WindowsAzure/azure-sdk-for-node">Node.JS</a> to upload files to Blob storage.</p>  <p>If you just want to interact with Windows Azure Storage from the command line on a vanilla Linux installation though, chances are that Python is the scripting environment that is pre-installed, not requiring you to compile anything before you can start working.</p>  <p>Here is a quick cheat sheet on how to use the Python SDK from the command line:</p>  <h2 id="install-the-windows-azure-sdk-for-python">Install the Windows Azure SDK for Python</h2>  <p>Just download the latest tarball from Git, extract the contents, and run the install script as root to install the SDK:</p>  <pre><code>curl -L -O https://github.com/WindowsAzure/azure-sdk-for-python/archive/master.tar.gz
tar xzf master.tar.gz
cd azure-sdk-for-python-master/src
sudo python setup.py install
</code></pre>

<p>Now in order to make it simpler to use the Blob Service using the Python module, you can set your Storage credentials as environment variables. You can find these values in the Windows Azure Management Portal, just select your Storage Account and click on the &quot;Manage Access Keys&quot; button to open the pop-up dialog that shows both the account name and the secret keys.</p>

<pre><code>export AZURE_STORAGE_ACCOUNT=tcontepub
export AZURE_STORAGE_ACCESS_KEY='secret key'
</code></pre>

<p>Now it's super simple to access Blob Storage directly from the Python interpreter; just run <code>python</code>, import the module and create the <code>BlobService</code> object:</p>

<pre><code>from azure.storage import BlobService
blob_service = BlobService()
</code></pre>

<p>Now here are a few code snippets you can use...</p>

<h2 id="list-containers-in-the-account">List containers in the account</h2>

<pre><code>for i in blob_service.list_containers():
   print i.name
</code></pre>

<h2 id="create-a-container">Create a container</h2>

<pre><code>blob_service.create_container('foo')
</code></pre>

<h2 id="upload-a-file-into-a-container">Upload a file into a container</h2>

<p>The first parameter is the container name, then the name of the Blob that will be created, then the data to be sent (typically the contents of a file you read from the local filesystem).</p>

<pre><code>blob_service.put_blob('foo', 'README.txt', file('/usr/share/doc/grep/README').read(), 'BlockBlob')
</code></pre>

<p>Hope this helps. You can find more information and examples on the <a href="https://github.com/WindowsAzure/azure-sdk-for-python">Windows Azure SDK for Python Github page</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Analyzing page view logs using Pig on Windows Azure HDInsight</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/analyzing-page-view-logs-using-pig-on-windows-azure-hdinsight</link>
      <pubDate>Tue, 16 Apr 2013 11:33:03 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2013/04/16/analyzing-page-view-logs-using-pig-on-windows-azure-hdinsight/</guid>
      <description><![CDATA[In this article, I would like to take you through the steps to run your first Pig script on a...]]></description>
      <content:encoded><![CDATA[  <p>In this article, I would like to take you through the steps to run your first Pig script on a significant dataset, using the new public preview of <a href="http://www.windowsazure.com/en-us/manage/services/hdinsight/">Windows Azure HDInsight</a>, our distribution of Apache Hadoop made available as a service in the Cloud.</p>  <h1 id="the-dataset">The Dataset</h1>  <p>First, let's discuss the dataset: I think Pig is particularly well suited to analysis of log files, where the structure is not always very formal and where Pig's flexibility really makes our life easier. I chose to use a publicly available dataset, Wikipedia's <a href="http://dumps.wikimedia.org/other/pagecounts-raw/">&quot;Page view statistics for Wikimedia projects&quot;</a>, which contains hourly dumps of page view counters for all of Wikipedia's projects. The page describes the dataset and how it is built, but here is a little extract of one of the log files, <code>pagecounts-20130401-000000.gz</code>:</p>  <pre><code>fr Paris 100 15325959
fr Paris_en_chansons 2 92560
fr Paris_quadrifolia 1 16023
fr Paris_qui_dort 1 9933
fr Paris_sous_les_bombes 1 15323
fr Paris_va_bien 1 11360
fr Parisianisme 2 21184
fr Parisii 1 15944
</code></pre>

<p>As you can see, the logs are in a simple ASCII format. This snippet means that on April 1st 2013, during the first hour in the day (you get that bit from the file name), there were in the &quot;fr&quot; Wikipedia project, 100 views for the page for &quot;Paris&quot;, 2 views for &quot;Paris en chansons&quot;, etc. The last number is the total bytes sent.</p>

<p>Let's say we want to do some analysis of the page views on Wikipedia on April 1st and maybe see what good pranks were going on around the world! In order to have a full view of the traffic for that whole day we need to analyze all the logs for that day, which represents 24 gzipped files, containing 160 millions lines for a total of 7+ GB uncompressed data.</p>

<p>This is not a huge dataset, but it is significant enough to allow us to utilize the HDInsight cluster. And once we have a working analysis for this dataset, we can reuse the exact same method to run on a bigger sample, for example a full month, and scale our HDInsight cluster accordingly in order to get our results faster!</p>

<p>So, let's review all the steps needed to run our analysis.</p>

<h1 id="create-a-storage-account">Create a storage account</h1>

<p>HDInsight gives you the option to use Windows Azure Storage to store your data. This is very useful in the case of ephemeral Hadoop clusters, because it will leave the data intact even when you delete the cluster; you can then choose whether you want to keep the data around to run more analysis jobs later, or if you would rather delete the data in order to save on storage costs.</p>

<p>To create a storage account for your HDInsight cluster, log into the Windows Azure Management Console, choose New, Storage, Quick Create, and just choose a unique DNS prefix for your account. Please note that if you are using the Public Preview of HDInsight, you will need to create your account in the &quot;East US&quot; region.</p>

<p><img title="0_create_storage_account" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="0_create_storage_account" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/2308.0_create_storage_account_321D4A30.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/2308.0_5F00_create_5F00_storage_5F00_account_5F00_321D4A30.png" width="1163" height="534" /></p>

<h1 id="create-the-hdinsight-cluster">Create the HDinsight cluster</h1>

<p>Now that you have a storage account for your data, you will need to create the actual HDInsight compute cluster. This is not a terribly difficult task: just go to New, Compute, HDInsight cluster, Quick Create, and just fill in the options: cluster DNS prefix, number of nodes (4 in our example), and a very secret administration password.</p>

<p><img title="1_create_hdinsight_cluster" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="1_create_hdinsight_cluster" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/4530.1_create_hdinsight_cluster_6A5E51F9.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/4530.1_5F00_create_5F00_hdinsight_5F00_cluster_5F00_6A5E51F9.png" width="1164" height="799" /></p>

<p>Once you click the OK icon, the cluster will go through several stages before it's fully operational, and this could take a few minutes; here is the final status you should see in the console:</p>

<p><img title="2b_creating_hdinsight_cluster_complete" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="2b_creating_hdinsight_cluster_complete" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/7658.2b_creating_hdinsight_cluster_complete_09A3897E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/7658.2b_5F00_creating_5F00_hdinsight_5F00_cluster_5F00_complete_5F00_09A3897E.png" width="1163" height="331" /></p>

<h1 id="connect-to-the-head-node">Connect to the head node</h1>

<p>Now that your cluster is up and running, there are a few things you can do directly from the Web management console. However, the best way to play around with Hadoop is probably to drop to the command line, and you can do that by connecting to the Head node via RDP: just click on the Connect button at the bottom of the HDInsight page on the Windows Azure Management Portal. Use the admin credentials you used when creating the cluster.</p>

<p>Once you are logged on the Head node, you will find a few shortcuts placed on the Desktop:</p>

<ul>
  <li>Hadoop Command Line </li>

  <li>Hadoop MapReduce Status </li>

  <li>Hadop Name Node Status </li>
</ul>

<p>The first you can try is double-click on the &quot;Hadoop MapReduce Status&quot; to open the status Web page. CLicking on the JobTracker link will take you to the page you can use to track the progress of running jobs.</p>

<p>To enter the Hadoop command line, double click on the &quot;Hadoop Command Line&quot; shortcut. A friendly help message will appear and leave you with a blank command line. Here you can start playing with HDInsight and get your bearings.</p>

<p>Now that we are reading to do some work, let's see how we can get our data into the system.</p>

<h1 id="how-to-download-the-data-efficiently">How to download the data efficiently!</h1>

<p>There are many ways to import data into the HDInsight cluster in order to work with it. In the default configuration, HDInsight is configured to use Windows Azure Storage, which means that we could use any storage explorer tool (like <a href="http://www.cerebrata.com/products/cloud-storage-studio/introduction">Cloud Storage Studio</a> or <a href="http://clumsyleaf.com/products/cloudxplorer">CloudXplorer</a>) to upload data in the storage account we created above.</p>

<p>However, because I am located in Europe and both my source data and the HDInsight cluster are located in the United States, I am going to try and optimize the download times by retrieving the data directly from the Head node (which should be reasonably fast since I am at least downloading from the same continent!) and then copy the data from the Head node to Windows Azure Storage, which will be very fast because they are located in the same data center.</p>

<p>Before we start downloading, just take a quick look at the disk configuration for the Head node: as you can see, you will find most of the available node storage in the C: drive, which has around 2 TB of free disk space. This is probably the right place to use as temporary storage for our data files!</p>

<p>Run Notepad on the server and just copy/past the following PowerShell script, then save it e.g. in C:\TEMP\GetFiles.ps1 (create that directory first if it doesn't exist).</p>

<pre><code>$wc = New-Object System.Net.WebClient
foreach ($i in 0..23) {
    $n = &quot;{0:D2}&quot; -f $i
    echo $n
    $url = &quot;http://dumps.wikimedia.org/other/pagecounts-raw/2013/2013-04/pagecounts-20130401-&quot; + $n + &quot;0000.gz&quot;
    $wc.DownloadFile($url, [System.IO.Path]::Combine($pwd.Path, $url.SubString($url.LastIndexOf(&quot;/&quot;)+1)))
}
</code></pre>

<p>This script will proceed to download in the current directory all 24 files from the Wikipedia dumps Web site, for the 1st of April; of course you can easily change the script to download another day if you prefer. I have found that sometimes there are missing hourly files, in which case the download will fail with a 404 error; you can ignore these, it just means that we will be missing some information for a given day.</p>

<p>To use the script, run PowserShell using the shortcut in the default Windows Server task bar. Then create a subdirectory in C:\TEMP (e.g. &quot;wp0401&quot;) and run the script from there:</p>

<pre><code>PS C:\TEMP&gt; mkdir wp0401
PS C:\TEMP&gt; cd .\wp0401
PS C:\TEMP\wp0401&gt; ..\GetFiles.ps1
</code></pre>

<h1 id="using-grunt">Using Grunt</h1>

<p>Now that we have downloaded our source data, let's use Grunt, the interactive shell interface to Pig, to copy our source data into the Hadoop cluster so we can run jobs on it. From the Hadoop command line, change to the Pig directory and run Pig:</p>

<pre><code>c:\apps\dist\hadoop-1.1.0-SNAPSHOT&gt;cd ..\pig-0.9.3-SNAPSHOT
c:\apps\dist\pig-0.9.3-SNAPSHOT&gt;.\bin\pig.cmd
2013-04-11 14:38:49,966 [main] INFO  org.apache.pig.Main - Logging error messages to: C:\apps\dist\hadoop-1.1.0-SNAPSHOT\logs\pig_1365691129964.log
2013-04-11 14:38:50,260 [main] INFO  org.apache.pig.backend.hadoop.executionengine.HExecutionEngine - Connecting to hadoop file system at: asv://myhdi@tomhdi.blob.core.windows.net
2013-04-11 14:38:51,068 [main] INFO  org.apache.pig.backend.hadoop.executionengine.HExecutionEngine - Connecting to map-reduce job tracker at: jobtrackerhost:9010
</code></pre>

<p>The informational message confirms that Pig is now connected to our Windows Azure Storage account; you can interact with it using the usual Hadoop commands as available from Grunt, e.g. <code>ls</code>, <code>cd</code>, <code>mkdir</code>, etc. For example, in order to copy all our data from <code>C:\TEMP\wp0401</code> where we downloaded it, you can run something like:</p>

<pre><code>grunt&gt; copyFromLocal  C:\TEMP\wp0401 .
</code></pre>

<p>This will copy the whole directory (it will take a little while), and you can check everything is there like this:</p>

<pre><code>grunt&gt; ls
asv://myhdi@tomhdi.blob.core.windows.net/user/admin/.Trash      &lt;dir&gt;
asv://myhdi@tomhdi.blob.core.windows.net/user/admin/wp0401      &lt;dir&gt;
grunt&gt; ls wp0401
asv://myhdi@tomhdi.blob.core.windows.net/user/admin/wp0401/pagecounts-20130401-000000.gz&lt;r 1&gt;   89406292
asv://myhdi@tomhdi.blob.core.windows.net/user/admin/wp0401/pagecounts-20130401-010000.gz&lt;r 1&gt;   81022448
asv://myhdi@tomhdi.blob.core.windows.net/user/admin/wp0401/pagecounts-20130401-020000.gz&lt;r 1&gt;   83019186
</code></pre>

<p>The &quot;ASV&quot; prefix means that the files are stored in Windows Azure Storage, which means that it will stay there even after you shut down the HDInsight cluster.</p>

<h1 id="the-pig-script">The Pig script</h1>

<p>And now let's look at the Pig script we are going to run!</p>

<p>Actually, Grunt lets you run Pig jobs interactively, which means that you can copy/paste each line in the command window and thus build the script gradually. Pig will not really execute each line when you enter it, but it will build its processing pipeline, and it will only start the process once it reaches the final &quot;store&quot; statement.</p>

<p>Typically, if you want to run a quick test to have a look at the results, and validate or tweak your processing, you are not going to run the script on the complete dataset but on a sample, for example in our case we could use just one file instead of all 24 files for one day.</p>

<pre><code>records = load '/user/admin/wp0401/pagecounts-20130401-*.gz' using PigStorage(' ') as (project:chararray, page:chararray, requests:int, size:int);
filtered_records = filter records by project == 'en';
records_by_page = group filtered_records by page;
sum_pages = foreach records_by_page generate group, SUM(filtered_records.requests);
result = order sum_pages by $1 desc;
result_limit = limit result 100;
store result_limit into 'wp_april_1st';
</code></pre>

<p>This script will produce the result we are looking for, i.e. the most popular English language pages on Wikipedia on April 1st. Let's read it line by line!</p>

<pre><code>records = load '/user/admin/wp0401/pagecounts-20130401-*.gz' using PigStorage(' ') as (project:chararray, page:chararray, requests:int, size:int);
</code></pre>

<p>This first statement is our LOAD command, where we instruct Pig while files to read, and how to parse them. In this case, the fields are separated by space characters, and we just have three fields: the &quot;Wikipedia project&quot;, which also gives us the language, the page name, the number of requests within the hour, and the total bytes sent for that page. As you can see, we can give names to these fields, and assign them a data type like &quot;chararray&quot; (string) or int.</p>

<pre><code>filtered_records = filter records by project == 'en';
</code></pre>

<p>The FILTER statement is used, you guessed it, to filter our records. Here, we only keep records from the main Wikipedia project in English.</p>

<pre><code>records_by_page = group filtered_records by page;
</code></pre>

<p>Now, because we want to aggregate all the requests per page, we GROUP the records using the &quot;page&quot; field.</p>

<pre><code>sum_pages = foreach records_by_page generate group, SUM(filtered_records.requests);
</code></pre>

<p>The FOREACH directive is used to create a projection of our data; here we are going to generate a new record set containing the group name (remember from the previous step, this is the &quot;page&quot; field) and the SUM of the values of the &quot;requests&quot; field. To understand where the &quot;filtered_records.requests&quot; expression comes from, you could use the handy DESCRIBE command to visualize the structure of the records generated by the GROUP command:</p>

<pre><code>grunt&gt; describe records_by_page;
records_by_page: {group: chararray,filtered_records: {(project: chararray,page: chararray,requests: int,size: int)}}
</code></pre>

<p>This is how you can see that you need to sum the &quot;requests&quot; field from the &quot;filtered_records&quot; tuple in order to find the total number of requests for a given page.</p>

<pre><code>result = order sum_pages by $1 desc;
result_limit = limit result 100;
</code></pre>

<p>These two lines are fairly self-explanatory: ORDER will sort our records, and LIMIT will only keep the first 100, because we certainly do not want to deal with the incredibly long tail of Wikipedia pages that are only accessed once per day!</p>

<pre><code>store result_limit into 'wp_april_1st';
</code></pre>

<p>And our final statement will tell Pig to store the results in the &quot;wp<em>april</em>1st&quot; file. This is the statement that will actually tell Pig to work its magic and start running the MapReduce jobs. As you can guess, depending on the amount of data that you LOADed in the first statement, the computation can take quite a while.</p>

<h1 id="thats-all-folks">That's all, folks!</h1>

<p>The output in Grunt is fairly verbose, and while the jobs are running you can check their progress using the Hadoop Status Page link on the Desktop (Grunt also gives you some URLs pointing directly to the jobs status pages). Pig will generate and chain several MapReduce jobs as required.</p>

<p><img title="5_job_running" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="5_job_running" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/78/34/metablogapi/6558.5_job_running_66E251C0.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-78-34-metablogapi/6558.5_5F00_job_5F00_running_5F00_66E251C0.png" width="788" height="447" /></p>

<p>At the end, Pig will give you some interesting stats about the run, including the total time taken to process the data, the amount of data read, the MapReduce jobs generated, etc. For example, in my test, it took about 6 minutes to process 160 million lines of logs:</p>

<ul>
  <li>Started At: 2013-04-16 07:26:39 </li>

  <li>Finished At: 2013-04-16 07:33:58 </li>

  <li>Input(s): Successfully read 163288474 records (8954 bytes) from: &quot;/user/admin/wp0401/pagecounts-20130401-*.gz&quot; </li>

  <li>Output(s): Successfully stored 100 records in: &quot;asv://myhdi@tomhdi.blob.core.windows.net/user/admin/wp<em>april</em>1st&quot; </li>
</ul>

<p>And of course, the result! The easiest way to read the results in our case (because there are only 100 records) is to just cat the file from Grunt:</p>

<pre><code>cat wp_april_1st
</code></pre>

<p>But you could also use copyToLocal to copy the file to your local disk and read it using Notepad. Without further ado, the results:</p>

<pre><code>Main_Page                       8695731
Special:Search                  2735038
Special:Random                  1844895
April_Fools%27_Day              502216
Diaper                          405342
Dumpster                        379215
Durian                          352748
Airport_terminal                345508
Waffle                          295425
The_Walking_Dead_(TV_series)    265686
undefined                       259862
Game_of_Thrones                 235499
Ground_Zero_(1987_film)         232729
</code></pre>

<p>No surprise, the main page comes first, followed by two &quot;special&quot; pages. &quot;April Fool's Day&quot; is the most viewed page after that. I think the next three results, &quot;Diaper&quot;, &quot;Dumpster&quot; and &quot;Durian&quot;, plus &quot;Waffle&quot; after that may have something to do with Google's <a href="http://www.google.com/landing/nose/">Google Nose</a> joke :-)</p>

<p>Hope you enjoyed this little tutorial!</p>]]></content:encoded>
    </item>
    <item>
      <title>How to use the Windows Azure Service Management API from cURL to create Virtual Machines</title>
      <link>https://docs.microsoft.com/archive/blogs/tconte/how-to-use-the-windows-azure-service-management-api-from-curl-to-create-virtual-machines</link>
      <pubDate>Thu, 29 Nov 2012 06:40:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas Conte]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tconte/2012/11/29/how-to-use-the-windows-azure-service-management-api-from-curl-to-create-virtual-machines/</guid>
      <description><![CDATA[Recently a partner asked me for some simples examples of using the Windows Azure Management API from...]]></description>
      <content:encoded><![CDATA[<p>Recently a partner asked me for some simples examples of using the Windows Azure Management API from that most universal Swiss army knife of HTTP debug tools, namely the infamous cURL. Specifically, he was looking for an example of how to create a Linux machine from the command line using just cURL.</p>
<p>There is already a blog post out there showing you how to access the Management API using cURL (<a href="http://blog.toddysm.com/2012/01/accessing-windows-azure-rest-apis-with-curl.html">http://blog.toddysm.com/2012/01/accessing-windows-azure-rest-apis-with-curl.html</a>) however it doesn't cover the creation of VMs, so here are a few more samples.</p>
<p>First, the blob post above shows you how to create a new certificate and upload it to the Windows Azure management portal. But there is a simpler way, using a "Publish Settings" profile, that you use for example with the <a href="https://www.windowsazure.com/en-us/manage/downloads/">cross-platform Command Line Tools</a>.</p>
<p>Here is the article that will explain you how to download this publishing profile: <a href="https://www.windowsazure.com/en-us/manage/linux/common-tasks/manage-certificates/">https://www.windowsazure.com/en-us/manage/linux/common-tasks/manage-certificates/</a></p>
<p>As explained in the article, you can extract from the profile the Base64-encoded certificate necessary to authenticate your calls to the Management API.</p>
<p>Let's say you saved the Base64 version in a file called cert.base64; here's how I decoded it:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">python -c 'print open("cert.base64", "rb").read().decode("base64")' &gt; cert.pfx</pre>
<p>Then I checked it using OpenSSL: (password is empty)</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">openssl pkcs12 -info -in cert.pfx</pre>
<p>And finally you can convert it to PEM format: (again, password is empty)</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">openssl pkcs12 -in cert.pfx -out cert.pem -nodes</pre>
<p>Now you can used this certificate with the -E option in cURL! You should also extract from the Publish Settings file your Subscription ID, that you will use to construct the URLs below.</p>
<p>Here are some examples... Again, the goal is to create a Linux virtual machine; in order to do that, we must first create a Hosted Service, and then "deploy" a Linux VM inside this Hosted Service. Here are the steps using cURL. All the URLs begin with your subscription ID, that you can find in the publishing profile as well.</p>
<p>Create a Hosted Service:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">curl -v -d @CreateHostedService.xml \
-H 'Content-Type: application/xml' \
-H 'x-ms-version: 2010-10-28' \
-E cert.pem: \
https://management.core.windows.net/(subscription-id)/services/hostedservices</pre>
<p>The CreateHostedService.xml file contains the parameters for the Hosted Service:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;CreateHostedService xmlns="http://schemas.microsoft.com/windowsazure"&gt;
  &lt;ServiceName&gt;testfromapi&lt;/ServiceName&gt;
  &lt;Label&gt;VGVzdCBmcm9tIEFQSQ==&lt;/Label&gt;
  &lt;Description&gt;&lt;/Description&gt;
  &lt;Location&gt;West Europe&lt;/Location&gt;
&lt;/CreateHostedService&gt;
</pre>
<p>The most important part in this file are the Location element, in which you indicate the data center you want to create this Hosted Service in (I chose West Europe), and the ServiceName element, that will be used later to deploy a VM.</p>
<p>List the Hosted Service:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">curl -H 'Content-Type: application/xml' \
-H 'x-ms-version: 2010-10-28' \
-E cert.pem: \
https://management.core.windows.net/(subscription-id)/services/hostedservices/testfromapi</pre>
<p>This should return your newly created Hosted Service.</p>
<p>List OS images:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">curl -H 'Content-Type: application/xml' \
-H 'x-ms-version: 2012-03-01' \
-E cert.pem: \
https://management.core.windows.net/(subscription-id)/services/images</pre>
<p>This will return a list of all available OS images. Let's use Ubuntu: CANONICAL__Canonical-Ubuntu-12-04-amd64-server-20120528.1.3-en-us-30GB.vhd</p>
<p>Now the last step, create the VM:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">curl -v -d @TestLinuxVM.xml \
-H 'Content-Type: application/xml' \
-H 'x-ms-version: 2012-03-01' \
-E cert.pem: \
https://management.core.windows.net/(subscription-id)/services/hostedservices/testfromapi/deployments</pre>
<p>Please note the URL references the name of the Hosted Service we created earlier ("testfromapi" in my example).</p>
<p>Now let's look at the XML file:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">&lt;Deployment xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure"&gt;
  &lt;Name&gt;testlinux&lt;/Name&gt;
  &lt;DeploymentSlot&gt;Production&lt;/DeploymentSlot&gt;
  &lt;Label&gt;testlinux&lt;/Label&gt;
  &lt;RoleList&gt;
    &lt;Role i:type="PersistentVMRole"&gt;
      &lt;RoleName&gt;testlinux&lt;/RoleName&gt;
      &lt;OsVersion i:nil="true" /&gt;
      &lt;RoleType&gt;PersistentVMRole&lt;/RoleType&gt;
      &lt;ConfigurationSets&gt;
        &lt;ConfigurationSet i:type="LinuxProvisioningConfigurationSet"&gt;
          &lt;ConfigurationSetType&gt;LinuxProvisioningConfiguration&lt;/ConfigurationSetType&gt;
          &lt;HostName&gt;testlinux&lt;/HostName&gt;
          &lt;UserName&gt;azure&lt;/UserName&gt;
          &lt;UserPassword&gt;P@ssw0rd!&lt;/UserPassword&gt;
          &lt;DisableSshPasswordAuthentication&gt;false&lt;/DisableSshPasswordAuthentication&gt;
        &lt;/ConfigurationSet&gt;
        &lt;ConfigurationSet i:type="NetworkConfigurationSet"&gt;
          &lt;ConfigurationSetType&gt;NetworkConfiguration&lt;/ConfigurationSetType&gt;
          &lt;InputEndpoints&gt;
            &lt;InputEndpoint&gt;
              &lt;LocalPort&gt;22&lt;/LocalPort&gt;
              &lt;Name&gt;SSH&lt;/Name&gt;
              &lt;Protocol&gt;tcp&lt;/Protocol&gt;
            &lt;/InputEndpoint&gt;
          &lt;/InputEndpoints&gt;
        &lt;/ConfigurationSet&gt;
      &lt;/ConfigurationSets&gt;
      &lt;DataVirtualHardDisks /&gt;
      &lt;Label&gt;dGVzdGF0b3Rv&lt;/Label&gt;
      &lt;OSVirtualHardDisk&gt;
        &lt;MediaLink&gt;https://portalvhdsfxcplfk8zny2r.blob.core.windows.net/vhds/testfromapi-testlinux-2012-11-29-593.vhd&lt;/MediaLink&gt;
        &lt;SourceImageName&gt;CANONICAL__Canonical-Ubuntu-12.04-amd64-server-20120924-en-us-30GB.vhd&lt;/SourceImageName&gt;
      &lt;/OSVirtualHardDisk&gt;
      &lt;RoleSize&gt;Small&lt;/RoleSize&gt;
    &lt;/Role&gt;
  &lt;/RoleList&gt;
&lt;/Deployment&gt;
</pre>
<p>This one is more complicated! There are good articles on <a href="http://michaelwasham.com/tag/virtual-machines/">Michael Washam's blog</a> if you want to dig into the details, but here are the important parts:</p>
<ul>
<li>In the LinuxProvisioningConfigurationSet, enter the login and password for the user that will be created on the machine.</li>
<li>The InputEndpoint definition opens port 22 (SSH).</li>
<li>In the OSVirtualHardDisk section, you must indicate the URL of the blob that will contain the new OS Disk for the machine; you can choose any name for the VHD, as long as it is unique! Also make sure the URL points to a Storage Account located in the same data center as the Hosted Service you created above, otherwise the call will fail (you cannot boot a machine in Dublin with an OS Disk in Singapore :)</li>
</ul>
<p>This should give you a running Linux VM.</p>
<p>You can retrieve the details of the VM once it's created:</p>
<pre style="background-color: #fbfbfb; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px;">curl -H 'Content-Type: application/xml' \
-H 'x-ms-version: 2012-08-01' \
-E cert.pem: \
https://management.core.windows.net/(subscription-id)/services/hostedservices/testfromapi/deployments/testlinux</pre>
<p>You will get an XML response including, among other details, the status of the instance (Running, Stopped, etc.) and, most importantly, the public port that was assigned for SSH when the VM was created (look for the PublicPort element). This should let you connect to your new VM via SSH.</p>
<p>Enjoy!</p>]]></content:encoded>
    </item>
  </channel>
</rss>