{"id":6053,"date":"2018-02-15T10:29:24","date_gmt":"2018-02-15T18:29:24","guid":{"rendered":"\/developerblog\/?p=6053"},"modified":"2020-03-14T17:28:06","modified_gmt":"2020-03-15T00:28:06","slug":"copy-custom-vm-images-on-azure","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/copy-custom-vm-images-on-azure\/","title":{"rendered":"Copy Custom VM Images on Azure"},"content":{"rendered":"<h2>Background<\/h2>\n<p>We recently worked with <a href=\"https:\/\/www.namogoo.com\/\">Namogoo<\/a>, a cloud security and analytics startup that works with e-commerce and retail companies to protect against\u00a0ads that direct customers to competitors. Virtual machines (VMs) with custom images are a key part of Namogoo&#8217;s system.\u00a0 These images include all proprietary software and configurations required for their offerings. The VMs that host these images are used in an elastic manner and auto-scaled as required by traffic patterns.<\/p>\n<p>Namogoo made the decision to migrate a large portion of their infrastructure from AWS to Azure with support from Microsoft. This code story explains how we created an azure-cli extension that simplifies the process of distributing VM images globally.<\/p>\n<h2>The Challenge<\/h2>\n<p>The idea behind custom images is that you bring up a VM, do all the installation and configuration needed for the solution based on it to work and finally, package the VM&#8217;s disks as an image to be re-used later on. Since the image already includes the specific customization for a solution, using it enables the environment to come online faster compared to using other methods (like using a &#8220;stock&#8221; image followed by init scripts). This tie factor becomes more important in complex systems that use auto-scaling because when more machines are needed to serve traffic, you want them available sooner rather than later.<\/p>\n<p>A custom image is a resource in a specific region; as a result, it needs to be replicated in all target regions.<\/p>\n<p>Replicating images across regions in Azure requires users to:<\/p>\n<ol>\n<li>Create disk snapshots from a source image<\/li>\n<li>Create a storage account in the destination region<\/li>\n<li>Create a container in the destination storage account<\/li>\n<li>Copy a snapshot blob to the destination container<\/li>\n<li>Create a snapshot of the blob\u00a0in the destination region<\/li>\n<li>Create an image from the snapshot<\/li>\n<li>Clean up temporary resources<\/li>\n<\/ol>\n<p>Steps 2-6 need to be performed across each destination region to which we are copying the image. Furthermore, these steps are just an outline and the actual procedure is between 10 to 15 steps, depending on the scenario. As you can see, the process is not straightforward. Although there are a few automation scripts available on the web, they require some tinkering to get the job done.<\/p>\n<p>Namogoo updates their images frequently and deploys them in many different regions and this whole process is tedious and time-consuming on Azure as compared to AWS, which was making it difficult to migrate more workloads to Azure. The objective of our solution was to create a way to <strong>copy images between regions<\/strong> that would be <strong>simple to use<\/strong>.<\/p>\n<p><!--more--><\/p>\n<h2>The Solution<\/h2>\n<p>Together with the relevant product groups at Microsoft, we decided to create a new <em>azure-cli\u00a0<\/em>command extension\u00a0that consolidates the process outlined above into a single command.<\/p>\n<p>Example of the image copy command:<\/p>\n<pre class=\"width:400 lang:default decode:true\" title=\"How to use\">az image copy --source-resource-group mySources-rg --source-object-name myImage --target-location uksouth --target-resource-group \"images-repo-rg\"<\/pre>\n<p>A command extension is a relatively\u00a0<a href=\"https:\/\/www.kernel.org\/pub\/software\/scm\/git\/docs\/howto\/new-command.html\">new feature<\/a>\u00a0in the Azure CLI that offers new functionality not available when the CLI is first installed. This feature is similar to Git subcommands, vscode extensions, etc. that allow users to extend the core application.<\/p>\n<p>The list-available command lists publicly available extensions:<\/p>\n<pre title=\"Listing available extensions\" class=\"lang:default decode:true\">az extension list-available<\/pre>\n<p>A specific extension can be added to your CLI using the add command:<\/p>\n<pre title=\"Adding an extension\" class=\"lang:default decode:true\">az extension add --name myExtension<\/pre>\n<p>Developing an extension is very similar to developing other CLI commands and consists of the following components:<\/p>\n<ol>\n<li>Parameter definition<\/li>\n<li>Logic<\/li>\n<li>Help text<\/li>\n<\/ol>\n<p>You can find <a href=\"https:\/\/github.com\/Azure\/azure-cli-extensions\">more information on Azure CLI extensions<\/a>\u00a0on GitHub.<\/p>\n<h2>Image Copy Extension<\/h2>\n<h3>Using Internal Commands<\/h3>\n<p>Our extension encapsulates the process of copying the image by using existing CLI commands. To keep the extension independent and decoupled from the implementation of commands used, we decided to invoke commands externally (as if they were called from a script).<\/p>\n<p>The extension works in steps, where each step goes through the following stages:<\/p>\n<ol>\n<li>Prepare the command to run<\/li>\n<li>Run the command<\/li>\n<li>Process the result<\/li>\n<\/ol>\n<p>For example, the following function creates a new resource group:<\/p>\n<pre title=\"Create Resource Group\" class=\"lang:python decode:true\">from azext_imagecopy.cli_utils import run_cli_command, prepare_cli_command\r\n\r\ndef create_resource_group(resource_group_name, location):\r\n    # check if target resource group exists\r\n    cmd = prepare_cli_command(['group', 'exists',\r\n                               '--name', resource_group_name], output_as_json=False)\r\n\r\n    cmd_output = run_cli_command(cmd)\r\n\r\n    if 'false' in cmd_output:\r\n        # create the target resource group\r\n        logger.warn(\"Creating resource group: %s\", resource_group_name)\r\n        cmd = prepare_cli_command(['group', 'create',\r\n                                   '--name', resource_group_name,\r\n                                   '--location', location])\r\n\r\n        run_cli_command(cmd)<\/pre>\n<h3>Performance<\/h3>\n<p>Copying an image from one region to another can take some time since the actual copying is performed asynchronously by Azure storage\u00a0infrastructure on spare bandwidth between the regions. If the regions are close, this process can be relatively quick and only take around 4 minutes. However, it can take considerably longer between far apart regions (for example, copying between Europe and Australia can sometimes take 30 minutes).<\/p>\n<p>In cases where it is necessary to copy an image to several regions, it makes sense to do so in parallel to save time and avoid redundant actions on the source image.<\/p>\n<h3>Using the extension<\/h3>\n<p>First, install the extension:<\/p>\n<pre class=\"lang:default decode:true\">az extension add --name image-copy-extension<\/pre>\n<p>Then, call it as you would any other <span class=\"lang:default decode:true crayon-inline\">az<\/span>\u00a0command:<\/p>\n<pre class=\"lang:default decode:true\">az image copy --source-resource-group mySources-rg --source-object-name myImage --target-location uksouth northeurope --target-resource-group \"images-repo-rg\" --cleanup<\/pre>\n<p>The extension piggybacks on the <em>image<\/em> parent command and extends it to add the <em>copy<\/em> functionality. The parameters listed describe the source and destination. The last one, <span class=\"lang:default decode:true crayon-inline \">&#8211;cleanup<\/span>\u00a0 deletes all temporary resources created in the process like snapshots, storage accounts, etc. In addition, all resources created (temporary and permanent) are tagged by the <span class=\"lang:default decode:true crayon-inline \">created_by=image-copy-extension<\/span>\u00a0 tag.<\/p>\n<p>While the command is running you can see the various stages as well as the progress of the parallel copy process to each region. Below is an example of such an output:<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-6055 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/command-output.png\" alt=\"\" width=\"1838\" height=\"1020\" \/><\/p>\n<h3>Source Disk<\/h3>\n<p>The whole process of copying an image relies upon the existence of the disk used to create the source image. Currently, there&#8217;s no way to access the actual resources behind the source image so we have to use the disk originally used to create the image.<\/p>\n<h2>Conclusion<\/h2>\n<p>Copying custom VM images between regions is\u00a0a\u00a0basic requirement for many Azure deployments; as such, the learning in this code story is reusable in many other scenarios. The image-copy extension has helped to streamline Namogoo&#8217;s DevOps processes on Azure. It will also enable other customers to reduce deployment times by providing a\u00a0quick and easy way to copy images across regions in Azure.<\/p>\n<p>Adding functionality to azure-cli is simple and extensions can be shared publicly for everyone to use, or privately within your organization. If you try out\u00a0<a href=\"https:\/\/github.com\/Azure\/azure-cli-extensions\/tree\/master\/src\/image-copy\"><strong><em>image-copy-extension<\/em><\/strong><\/a>, let us know what you think in the comments below.<\/p>\n<h3>Resources<\/h3>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-cli-extensions\/tree\/master\/src\/image-copy\">Image-copy-extension GitHub repo<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-cli-extensions\">Azure-cli extension documentation and template GitHub repo<\/a><\/li>\n<\/ul>\n<hr \/>\n<p>Cover image <a href=\"https:\/\/freerangestock.com\/\">source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We created an azure-cli extension to simplify the process of distributing VM images globally.<\/p>\n","protected":false},"author":21424,"featured_media":10803,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[16],"tags":[39,60,67,94,378],"class_list":["post-6053","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","tag-amazon-web-services-aws","tag-azure","tag-azure-cli","tag-azure-storage","tag-vm-image"],"acf":[],"blog_post_summary":"<p>We created an azure-cli extension to simplify the process of distributing VM images globally.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6053","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21424"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=6053"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/6053\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/10803"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=6053"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=6053"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=6053"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}