One of the first steps many people take in their journey to Azure or Azure Stack is the migration of an existing workload, rather than building net new. Typically most people would recommend choosing a non-production-critical web-based application running within a VM or across multiple VMs.
There are three usual ways people move this sort of workload:
With the workload running within the cloud environment, we are far better positioned to modernise it gradually and when appropriate using cloud-native features.
There are other options available for workload migration, however we find they’re rarely used in the real world just now due either to lack of awareness, or perceived increased complexity. One of those methods which falls squarely into both camps for most people today is containerising an existing workload, and moving it into IaaS.
Containerising in this way can have many benefits – the two core benefits we’ll focus on here though are shrinking of workload footprint, and simplified migration and deployment into the cloud environment.
In order to significantly reduce the knowledge cliff and learning curve needed to containerise an existing workload, a really exciting new community-created and driven PowerShell module was announced at DockerCon a couple of weeks ago, Image2Docker for Windows. Image2Docker also exists for Linux, but for this blog we’ll be focused on the Windows variant.
Image2Docker is able to inspect a VHD, VHDX, or WIM file, identify installed application components (from a select group for now), extract them to an output folder, and then build a Dockerfile from which you can then build a container.
It’s a brilliant tool which begs the question ‘How quickly can I move an existing workload to a cloud provider then?’
… so let’s answer it!
My Azure Stack PoC hosts are being moved to a new rack just now, so for the purposes of this demo I’ll use Azure. The process and principles are identical here though, as we’re just using a Windows Server 2016 VM with Container support enabled.
First of all we will need a workload to move. For this I’ve deployed a very simple website in IIS for this first test – we can get more bold later with databases and jazz, for now this is a plain jane HTML site running on-premises in Hyper-V.
The server we run the Image2Docker cmdlets from will need to have the Hyper-V role installed, so to keep life easy I’m running it from the Hyper-V host that the VM already lives on. I’ve also enabled the Containers role and installed the Docker Engine for Windows Server 2016.
Because the Image2Docker cmdlets mount the VHD/X, it’ll need to be offline to run the tool. You can either take a copy of the VHD/X and run it against that, or as I’m doing in this case, just shut the VM down.
Ok, so with the VM shut down, our first step on the Hyper-V host is to install the Image2Docker cmdlets. This is made extremely easy by virtue of the module being hosted in the PowerShell Gallery.
So simply Install-Module Image2Docker, then Import-Module Image2Docker, and you’re all set!
My VHDX here is hosted in a remote SOFS share, so to remove the need for me to keep having to edit hostnames out of images, I’ve just mapped it to X:
First up we’ll create a folder for the Image2Docker module to output to, both the contents of the IIS site and the resultant Dockerfile will live here.
Now comes time to extract the website and build the Dockerfile.
The documentation claims that the only required parameter is the VHD/X and that it will scan for any application/feature artifacts within the VM automatically. It also claims that you can specify multiple artifacts (e.g. IIS, MSSQL, Apache) for it to scan, and it will extract them all.
Sadly, after reviewing the PowerShell code for it here, it turns out that this is aspirational for now and that the Artifacts parameter is both required, and can only support a single argument for now. C’est la vie, it’s not an issue for our basic IIS site here luckily.
Run the cmdlet, targeting the VM’s VHDX, IIS as the artifact to scan, and the pre-created OutputPath.
After running, the DockerOut folder will contain all the bits of the puzzle needed to build a container based on the IIS website within the VM – hurrah!
Ok! So before we go any further, let’s prep our Docker environment. I already have Docker Engine installed and it’s logged into my Docker account, so let’s get some base images ready.
Because PowerShell == Life, I’ve also installed the Docker PowerShell Module.
Register-PSRepository -Name DockerPS-Dev -SourceLocation https://ci.appveyor.com/nuget/docker-powershell-dev Install-Module -Name Docker -Repository DockerPS-Dev -Scope CurrentUser
This lets us check that there are no containers and no images currently on the server using Docker native and PowerShell cmdlets.
This is where we can either blast ahead with defaults, or make some informed choices…
Looking inside the Dockerfile, right at the top we can see that the base image that this container will be based on is the ASP.NET Windows Server Core image from http://hub.docker.com/r/Microsoft/aspnet
All we’re doing here is running a very simple IIS website here, so why not run this with Nano Server as the base? For comparison, I’ve pulled down the IIS enabled Nano Server Docker image, and the Windows Server Core image referenced in the Dockerfile.
Holy moley! It’s almost a 90% image reduction going from Server Core to IIS-enabled Nano Server! Let’s definitely do that.
Kick off the build process with Docker Build DockerOut, and off we go!
… and just like that, the Image is built.
The image has no associated Repo or Tag yet, so let’s add those, then push it up to Docker Hub.
The iisdemo repo doesn’t exist within my Docker account yet, but that’s fine, just pushing it up will create and initialise it.
… and hey presto, the image is in my Docker repo. This could just as easily be a private repo.
Now that we have the Container in Docker Hub, I can go to any Windows Server Container friendly environment and just pull and run it! Just like that.
In Azure I have deployed a Windows Server 2016 VM with Containers support enabled, which can be a host for whatever containers I deign to run on it. In this simple demo I’ll just be running the one container of course.
Within this VM, getting our pre-built image is as simple as pulling it down from Docker Hub.
Running the container is a single command more, with a map of port 80 in the container to 80 on the host…
… and hey presto! Our website has been containerised, pushed to Docker Hub, pulled down to a VM in Azure (or Azure Stack, or anywhere that can run Windows Containers), and the website is up and running happily.
If we break down the steps that were actually needed here to migrate this workload, we had:
We’re not getting any of the space-saving benefits that have been generated here due to the way I’ve deployed it as a 1:1 mapping of container to VM. Deploying this as a Hyper-V container within Hyper-V 2016 would have seen a ~90% space saving vs the original VM size, which is pretty awesome.
This space saving per container can also be realised the Azure Container Service preview for Windows Containers, which is available in public preview if you deploy a Container Service with Kubernetes as the Orchestrator. The space savings really come into being when operating at scale greater than that of our test site here though, so for a single website like this there’s really no point. There are other resource saving options as well, which fall outwith the scope of this blog.
Obviously this is a very simple workload, and we’re still at very early days with this technology. It hopefully gives a glimpse into the future of how easy it will be to migrate any existing workload to any cloud which supports Windows or Linux containers though.
Once a workload is migrated like-for-like into a cloud environment, extending it and modernising it using cloud-native features within that environment becomes a much simpler proposition. For those for whom working out the best path to do an initial IaaS to IaaS migration is a pain just now (insert fog of war/cloud metaphor), tools like Image2Docker are going to significantly ease the pain and planning required for that first step towards cloud.
So how long did this take me, end to end, including taking screenshots and writing notes? Well the screenshots are there, and I was done in around 30 minutes – this is partly because I’d stripped back pre-reqs like the Core and Nano images in order to get screenshots. Normally these would already be in place and used as the base for multiple images.
Running through the process again with all pre-requisites already in place took around 3 minutes to go from on-premises to running in Azure. So, to answer the question we asked back at the start – not long. Not long at all.