Why would you need this procedure?
Let’s say that you want/need to export your running EC2 instance to another platform. It may be a VirtualBox, VMWare, QEmu virtual machine or perhaps a physical machine. The AWS Import Export Utility in the awscli is very restricted:
- the EC2 instance may have only one EBS volume attached to it;
- there is no way to do it using only the Web console, you have to use the cli tool;
- you can only export instances you have previously imported.
The approach proposed on this post doesn’t rely on that Amazon tool and, therefore is not bound by those restrictions. The basic idea is:
- attach the EBS volume of the target machine on another instance;
- use dd to dump the volume into a file;
- download/transfer that file to a more convenient place;
- reconfigure the image so that it works outside of the cloud;
- convert the raw image to the desired VM.
The following sections go in details about how to accomplish each described step.
What instances will this work on?
This will work for any Linux Distribution except Amazon Linux. I have used this to export Debian, Ubuntu, Red Hat and CentOS Instances.
What will you need and how I refer to Systems.
AWS Export Instance. This is the Linux instance you want to export from the AWS server.
AWS Host Instance . A separate instance in the same zone as the Instance you wish to export. This instance will need to have remote root access via ssh using ssh keys. You will use this instance to mount the Volumes hold the instance to be exported. It does not need to have a lot of space since you will be mounting the volumes on the system not copying them to it. I used the basic server with 8G of space and exported a couple of 20G systems with no issues.
Local Linux System. A local system (Virtual or Physical server) running Linux of your choice. Preferably this is the local system that you host your VMs on such as a system running qemu or Virtual Box. This is used to hold an image of the AWS Export Instance and perform the needed file changes and conversions. You will use this system to login to the AWS Host Instance and make copies the volumes of the instance to be converted and make the needed changes so the instance will boot in your Virtual environment. So this system will need to have enough room to hold several copies of your AWS Export Instance.
Create the AWS Host Instance and enable root SSH access to it. Copy the ssh key from the provided login user (this could be Fedora, ubuntu, or ec2-user depending on the distribution you have installed) to the root user:
sudo cp -R ~/.ssh /root/
To enable root access edit /etc/ssh/sshd_config and change the line PermitRootLogin to read:
On the Amazon console, do the following steps:
- turn off the AWS Host Instance
- turn off the AWS Export Instance
- Make note of the Instance ID for each system.
- detach the root volume from the AWS Export Instance.
- Once it is detached:
- attach the volume on AWS Host Instance (it must be attached as a secondary volume).
- Go back to the Instance list and start the AWS host Instance.
- log into the AWS host Instance from your Local Linux System and dump the EBS volume into a local file.
Considering it is attached as /dev/xvdf, you can use the following command to accomplish that last step.
ssh -i ~/your_key root@ec2-XX-XX-XX-X.compute-1.amazonaws.com 'dd if=/dev/xvdf bs=1M | gzip' | gunzip | dd of=./ec2-image.raw
We need to know the offset for the beginning of the root partition, inside the block file. Use fdisk:
[root@lptp2 ~]# fdisk -l ./ec2-image.raw Disk ./ec2-image.raw: 8 GiB, 8589934592 bytes, 16777216 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: gpt Disk identifier: A285F90B-B6D2-4DFB-BB36-CFCA7E815241 Device Start End Sectors Size Type ./ec2-image.raw1 34 2048 2015 1007.5K BIOS boot ./ec2-image.raw2 2049 16777182 16775134 8G Linux filesystem
The offset is equal to Start times sector size. In my case 2049 times 512, which equals 1049088. Now, I can mount the img file as a partition:
sudo mount -o loop,offset=1049088 ./ec2-image.raw /mnt/
As root, use chroot to change the base to the ec2 system. It is also necessary to disable the cloud specific services. Change the root password so you can login a the console. The last thing I had to do was to include a line on /etc/security/access to enable login through the tty console.
chroot /mnt/disk;rm /etc/systemd/system/multi-user.target.wants/cloud*;passwd
The last thing I had to do with some OS’s was to include a line on /etc/security/access to enable login through the tty console.
echo "+ : ALL : ALL" >> /etc/security/access.conf;
It is also a good idea to review your /etc/fstab to remove entries about partitions that won’t be there (at least at first).
Now, get off chroot and unmount the partition:
Convert the raw image to the desired VM
In this post, I show how to make a VirtualBox VM out of it. So, this last section varies accordingly with your goal. To make the VirtualBox VM, start by converting from raw to VDI using VBoxManage:
VBoxManage convertfromraw ./ec2-image.raw ./ec2-image.vdi –format VDI
For qemu use
Now you can use the VirtualBox GUI to create a new VirtualBox machine. Instead of asking to create a new disk, use the newly created ec2.vdi.
And it is done!! Now you can start your VM and have fun with it.
After that prepare a filesystem on a new image file:
$ dd if=/dev/zero of=vmdk-image.raw bs=1M count=10240 # create a 10gb image file
$ losetup -fv vmdk-image.raw # mount as loopback device
$ cfdisk /dev/loop0 # create a bootable partition, write, and quit
$ losetup -fv -o 32256 vmdk-image.raw # mount the partition with an offset
$ fdisk -l -u /dev/loop0 # get the size of the partition
$ mkfs.ext4 -b 4096 /dev/loop1 $(((20971519 – 63)*512/4096)) # format using the END number
Now you need to copy everything from the EC2 image to the empty image:
$ losetup -fv ec2-image.raw
$ mkdir -p /mnt/loop/1 /mnt/loop/2 # create mount points
$ mount -t ext4 /dev/loop1 /mnt/loop/1 # mount vmdk-image
$ mount -t ext4 /dev/loop2 /mnt/loop/2 # mount ami-image
$ cp -a /mnt/loop/2/* /mnt/loop/1/