If you have a regular Azure Resource Manager virtual machine, you can convert it to be a source image for a VM Scale Set.
Update 3/21/17: Since Azure Managed Disks were introduced, it’s now recommended to create scale sets based on Managed Disks instead of the traditional storage account method. You get several advantages:
For a description of how to convert an Azure virtual machine to a VM Scale Set based on Managed Disks, see Derek Martin’s ARM Templates and Managed Disks article.
The rest of this post is based on creating a scale set using self-managed storage. Hopefully I’ll update it at some point 🙂
In this example I’ll convert an Azure VM running a Minecraft server, to a load balanced VM scale set of servers. Multiple Minecraft client connections will hit a load balancer and be routed to different VMs in the set. This would enable you to start with a single VM, and scale it out to handle a much larger load.
In a nutshell, to convert a single VM into a scale set you need to: capture a generalized image of the VM and copy that image into the storage account you’ll use for the set, then deploy a VM Scale Set with a custom image pointing to the generalized image. The steps are:
1. Generalize the VM (e.g. run sysprep on Windows or waagent –deprovision on Linux).
2. Stop deallocate the VM.
3. Set the VM state as generalized.
4. Save the image to a storage account.
5. Copy the image to the storage account where you want to create the scale set.
6. Deploy a VM Scale Set template with the image->uri property set to the image location.
Now let’s go through those steps in more detail..
Before starting make sure you have an Azure VM that you can log in to. For the Minecraft scenario the starting point would be to deploy a Minecraft server VM using the Minecraft Server Azure template. See Creating a Minecraft server using an Azure Resource Manager template for more information on how to do that.
Note: in the steps below I will mostly use Azure CLI examples. Steps 1-4 for PowerShell are described in detail in Stephane Laponte’s excellent blog post STEP BY STEP: HOW TO CAPTURE YOUR OWN CUSTOM VIRTUAL MACHINE IMAGE UNDER AZURE RESOURCE MANAGER. Another useful PowerShell resource for steps 1-4, particularly if you have a Windows VM is the Azure documentation: How to capture a Windows virtual machine in the Resource Manager deployment model.
1. Generalize the VM
The first step in preparing a VM to be a source image for new VM deployments is to log in to the machine and generalize the image so it can be assigned a new name/user/password/certificate etc. at VMSS deployment time.
On Windows that means running sysprep. On Linux call the VM agent with the –deprovision argument: sudo waagent –deprovision.
2. Stop deallocate the VM
Stop deallocate the VM so the OS drive image can be captured.
For PowerShell the command is Stop-AzureRmVm. The CLI command is: vm deallocate . e.g.
3. Set the VM state as generalized
Now tell Azure that the VM is generalized.
The PowerShell command is:
Set-AzureRmVM –ResourceGroupName –Name –Generalized
The Azure CLI command is: azure vm generalize . E.g.
4. Save the image to a storage account
Now it’s time to capture the generalized image and save it in a storage account. The PowerShell command is Save-AzureRmVMImage. The CLI command is: azure vm capture mineset , and you’ll get back a template for the captured image which includes the properties->storageProfile->osDisk->image->uri setting, which is the link to the captured image that you’ll need when copying it to a new storage account.
5. Copy the image to the storage account where you’ll create the scale set
If the generalized VM image capture is already in the storage account and container you want it to be in, fine. In most cases at this point you’ll probably want to create a new storage account that you will use for the scale set you’ll be creating. You can copy the image to the new storage account using PowerShell, CLI, or a storage explorer like CloudBerry Explorer. I like the CloudBerry tool because it offers a nice split screen to show 2 storage accounts at a time and easily copy blobs between them.
Make a note of the URI for the new image as it will be used when deploying the scale set.
6. Deploy a VM Scale Set template with the image->uri property set to the new image location
The last step is to create a VM Scale Set with the image URI property set to the new image. There are some example ARM templates which allow you to specify a custom image, like this one in Aure Quickstart Templates: https://github.com/Azure/azure-quickstart-templates/tree/master/201-vmss-windows-customimage, but for the Minecraft server scenario, as well as being a Linux image, I also wanted to create a public IP address, and a load balancer with a rule to load balance incoming requests to the default Minecraft server port of 25565 to every VM in the set. The specialized template I created is here: vmss-minecraft-custom.json.
Deploying this template to Azure as a new custom deployment in the portal allows the URI of the new image to be specified as a deployment parameter (along with the number of VMs, VM size, etc.).
Once the template is successfully deployed, a VM Scale Set of 10 Minecraft servers is now running behind a load balancer. Yay! Now the set of Minecraft servers can handle 10 times the incoming load, or I could scale this out to 40 servers.
Note the Minecraft world on each VM in the scale set is exactly how it was when I generalized the original VM, with the same operators, whitelist settings etc. When users start making changes to different VMs the worlds will diverge, but I can always reimage the VMs in the scale set to set them back to the source image.
One manual thing I had to do was start the Minecraft server on each VM (i.e. SSH to each VM using the inbound NAT rules defined in the template and run sudo systemctl start minecraft-server). This shouldn’t be necessary, and it may have been because I had shut down the Minecraft server before generalizing the image.
This was a basic walkthrough of converting a standalone Azure VM to a VM Scale Set. A next logical step would be to configure the VMSS template to use Azure autoscale. This way instead of launching a fixed number of VMs and manually scaling in or out, you could save costs by automatically scaling in or out depending on a workload such as average CPU speed.