| Tell me more, tell me more |
Image creation
Background
This document is an informal ‘How To’ on creation of disk images on the Extility platform. The process described here is one we are trying to simplify, and so what is described is a ‘work in progress’. This documentation is meant to be a guide for technically astute readers, not complete documentation or a technical manual suitable for novices. This document assumes familiarity with the FlexiScale / Extility system.
None of the processes described here require anything other than normal customer access. No programming is required; whilst it is possible to automate the tasks described here with Extility’s API, this is beyond the scope of this article. You will, however, need experience with basic Linux system administration, and familiarity with block devices and file systems in particular.
Images are essentially byte for byte copies of a disk. They are deployed onto new servers by thin provisioning. They differ from snapshots only in that they do not retain a reference to the disk from which they were created, and in that they are included in the list presented to the user of images on which servers can be based.
End customers can create private images, and then share them with other customers, a specific billing entity, or the entire platform (subject to the configuration dictated by the platform Licensee. The licensee can associate them with a product offer that will generate appropriate billing if required.
Two steps are thus required to create a new public image:cl
- Creation of a functional private image
- Publishing of that private image into a shared/public image
The second of these steps is achieved through the PublishImage API call, so this document focuses on the first of these steps.
There are two main ways to build a server image:
- Configuration and snapshotting of a server
- Off-server image creation
These are alternative processes. The first method is the easiest and the most user-friendly. The second is the most flexible.
Configuration and Snapshotting of a server
- This is the simplest method. The steps are as follows:
- Create a server using an existing operating system image as supplied on your platform. Reminder: you will have to abide by the licensing terms of this operating system.
- Install your own custom components
- Test your server, and make any required adjustments
- Stop your server.
- Using the image tab, make an image of the server’s primary disk, or for VMWare the server itself.
- Create a new server, based on this image
- Test this; if it is unsuccessful, delete the server created in step 6 and the image, and repeat from step 3.
In order for your image to present well to end-users, you will have to take some care to ensure that inappropriate data is not included within it. This is especially relevant for operating system images (rather than appliance images). This means that you should ensure that:
- Your own test scripts are erased
- No network-accessible logins are available (so, on a UNIX platform, there are no entries in /etc/passwd or /etc/shadow with fixed passwords)
- Logs and usage information are cleared (consider /var/log, utmp, wtmp, and dmesg files)
- Information which is supposed to be unique per server is regenerated (for instance ssh keys)
The easiest way to ensure these happen is by a “first boot” script. This can also set a unique initial password for one user (see section 4). As an alternative, it is possible to modify the disk directly. To do this:
- Stop your test server
- Create a new server (call this “utility server”)
- Detach the disk from your test server
- Attach the disk to your utility server
- Start the utility server.
- Mount the new disk (e.g. ‘mount /dev/sdb /mnt’)
- Make the appropriate modifications to the mounted disk
- Unmount the new disk (e.g. ‘umount /dev/sdb’)
- Stop the utility server
- Detach the disk from the utility server
- Reattach the disk to the test server (If you used the above workaround, also configure it as the primary disk).
- Create the image as described above.
Off-Server image creation
Extility will boot any disk that a normal x86 server will boot, provided it will run as a virtual machine (Xen/KVM/VMWare subject to the choice of hypervisor by the licensee) . It is thus possible to create images on a separate server either manually, or using packages such as vmbuilder. You can even use a Extility server to do this, which makes transfer for images faster.
This process consists of two parts:
- Making the binary disk image
- Transferring it into the Extility platform
Making the binary disk image
You can use any method you like to make a binary disk image (even a ‘dd’ from a server on your desk), but we suggest you use our tool-chain. This currently consists of two tools: sparsecopy and fsmaker. These tools are optimised for, and only tested on, a Linux platform, though sparsecopy should work on other platforms. However, this does not restrict your choice of operating system within your image; for instance, there is nothing to stop you using this to create an OpenBSD based image.
In short, what these do is as follows:
- sparsecopy is a block copy utility rather like dd, which works with sparse files and does intelligent things with partition tables. Sparse file support means it works more quickly, uses less disk space for the resultant images, and is more compatible with thin provisioning.
- fsmaker takes a filing system root partition that does not necessarily have a kernel or a boot sector (such as you might use in Amazon’s EC-2 service, or on Eucalyptus), and makes it into a bootable hard disk image by adding grub and a optionally a kernel and an initrd.
You will need sfdisk and getext2fs installed to use the fsmaker utility (both of these are available in Ubuntu and Debian stock distributions, and may be available elsewhere). fsmaker also requires that sparsecopy is installed.
To compiling and installing the tool-chain from scratch, download sparsecopy from here and fsmaker from here. Expand each tar archive into somewhere convenient, and type ‘make’ and (as root) ‘make install’.
To install on a debian or ubuntu amd64 system, download a package for sparsecopy from here and fsmaker (2 packages) from here and here.
Both utilities will give appropriate usage instructions if invoked with no parameters.
To use fsmaker, you will first require a copy of your file-system (to be installed in the first partition) of your image. You could create a 2GB file-system, for instance, by
sparsecopy –f2G /dev/null myfs mkfs –t ext3 myfs mount –o loop myfs /mnt rsync –axSHAXD /my/files/to/copy /mnt umount -d /mnt
Alternatively, you could use a utility such as vmbuilder to perform this operation, or you could use the img file from an existing AMI image (you may wish to modify it to remove ec2init and ec2init-userdata which are Amazon specific items).
Then use fsmaker to build your image. In its simplest form this is:
fsmaker myfs myimage.img
fsmaker will use grub, a recent known good kernel and initrd to boot your image, though on first boot you may wish to replace these with kernels inside your file-system. It is possible to specify your own kernel, ram-disk, and even grub installation through command-line options.
Transferring your image to the platform using the Control Panel
The Extility end user control panel UI allows you to upload images using the Fetch Disk/Server or Fetch Image buttons within the Disk and Image sections of the control panel. The only difference between these two buttons is that the former makes a disk and the latter makes an image. It is possible to make a disk/server into an image by using the “New Image” button on the Images tab. An image can be made into a disk either by provisioning a new server using that image, or by cloning it.
When the Fetch button is pressed, a dialog will appear asking for:
- the URL from which the disk or image is to be fetched;
- a username and password to be used (optional);
- the checksum of the image (optional);
- the prospective name of the disk or image;
- the VDC in which the disk (or in the case of an image, the disk underlying the image) should reside; and
- the disk product on which the disk or image will be written.
The URL should be either an http, https, ftp or ftps URL. If the URL does not start with a protocol identifier (e.g. ‘ftp://’), the platform will attempt to make an intelligent guess. Authentication credentials may be encoded within the URL in the normal manner, though in most cases it will be easier to specify a username and password.
The disk should be in the binary format to be written to the disk. Optionally, the disk may be gzip compressed, in which case it will be uncompressed prior to writing. This is strongly recommended because most images are sparse and compression will very substantially reduce the data transfer required to transfer the image. Note that you will be charged for bandwidth transferred. The data is written to the disk in binary format. No special care is needed to ensure thin provisioning works. If the uncompressed file is smaller than the disk, the file will be written at the start of the disk and the remainder of the disk will be filled with thin-provisioned zeroed sectors; in this instance the uncompressed file should be a multiple of 4,096 bytes in size, else the status of the final partial 4,096 byte block is undefined. If the uncompressed image is larger than the size of the disk, the effect is also undefined.
The optional checksum is a checksum over the uncompressed disk image file. It is not a checksum over the entire disk (which may be larger than the image written). The checksum is calculated using the MD5 algorithm (as output from the UNIX utility md5sum). If a checksum is present, and the checksum calculated by the platform does not match the supplied checksum, the job will error and no disk or image will be created.
Pressing the “Add Disk” or “Add Image” button on the Fetch Disk or Fetch Image dialog will not add the image immediately. Rather, it will queue a job to fetch the disk. As disks are in general large, this transfer may take some time. The status of the upload can be monitored on the Jobs tab, or using the appropriate API call. When the disk or image upload is completed, the new disk or image will be made available on the disk or image tab.
Transferring your image to the platform using the API
Exactly the same functionality as set out under 3.2 is available through the Extility API. Please refer to the API documentation for the API call FetchDisk, which takes the parameters described above, with an additional parameter describing whether the upload should be for a disk or an image.
Essentially the call is used as follows. First, FetchDisk is passed a URL from which the disk or image is loaded, authentication information, a name for the new disk or image, the ProductOffer of the disk or image onto which the disk will be written, a VDC indentifier and a flag to say whether a disk or an image should be produced. The call returns a job ID. The request then becomes a job within the job system, and disk fetch continues in the background. The status of the relevant job can be polled for success or failure.
Example code using fetchDisk is presented in Appendix 1. This fetches an Ubuntu desktop ISO, provisions it as a disk, creates a new server with a blank boot disk, attaches the disk to the server, and starts the server. Of course many users will wish to upload a hard disk image rather than an ISO image; the same call is used for this.
For more details, please refer to the API documentation.
Transferring your image to the platform manually
For completeness, images can also be uploaded manually. This is occasionally useful for debugging, or when developing on the platform itself. To achieve this:
- Create a utility server (i.e. a Linux server of any sort)
- Create a new 20GB blank disk
- Attach the blank disk to the server
- Boot the server
- If you have not done so already, install the tool-chain on the server (as above).
- Compress your image (e.g. ‘gzip myimage.img’)
- Transfer your compressed image (myimage.img.gz in the above example) to your server, using normal UNIX tools (e.g. scp, rsync –e ssh, wget, etc.)
- Uncompress the image directly onto the second hard disk (e.g. ‘gzcat myimage.img.gz | sparsecopy - /dev/sdb‘)
- Reread the partition table (e.g. ‘sfdisk –R /dev/sdb‘
- Stop the server
- Create an image of the second disk
First password creation
As any server created from a given image on Extility will have an identical disk configuration, there is an inherent risk to storing passwords (encrypted or otherwise) in a public image, as the password will be the same for all users. Unless the image is appropriately locked down (so, for instance, passwords can only be entered through the console interface) this would present a security risk as any person familiar with the passwords for that image could log into a freshly deployed server using that image via its network interface; this is clearly undesirable.
To avoid this problem, the Extility platform generates a unique initial password that a ‘first boot’ script can obtain and assign as the password to any user on the system. Example scripts are provided here as a debian package and here as a tarball.
These simple scripts can be installed using either of the above two methods. As will be evident, the scripts retrieve an XML file, which will in the future be able to provide ssh public keys and other information.
ISO support
The Extility platform can also support disks in ISO format. This is particularly useful in order to upload ISO images of install CDs or DVDs. The platform will recognise an ISO format disk as such, and mounts it as a virtual CD drive rather than a virtual hard disk. Remaining disks will be mounted as hard disks in disk order. Boot order is set to CD then hard disk.
Appendix 1 – FetchDisk example code
foo
/**
* Your username
*/
define("USERNAME","someone@example.com");
/**
* Your Password
*/
define("PASSWORD","mysecretpassword");
/**
* Your image location
*/
$iso = "http://releases.ubuntu.com/10.04/ubuntu-10.04-desktop-amd64.iso";
/**
* Name you want to give the disk
*/
$disk_name = 'ubuntu_lucid_desktop_iso';
/**
* The name of your Virtual Server
*/
$server_name = "Ubuntu Desktop";
/**
* The API WSDL location
*/
define("WSDL","https://api.example.com/?wsdl");
// Following defines are all ID's you can get them by doing list calls
/**
* Blank disk required to install to
*/
define('BLANK_DISK_IMAGE',66);
/**
* Virtual Server Profuct Offer 1 CPU / 1 GB RAM
*/
define('GB1_CPU1_PO',18);
/**
* Product Offer For a 20GB Disk
*/
define('GB20_PO',31);
/**
* Soap Client to connect to the API
*/
$client = new SoapClient(WSDL, array("login" => USERNAME, "password" => PASSWORD,
"trace" => 1, "exceptions" => 1));
$list_vdcs = $client->ListVDCs();
if(count($list_vdcs) == 0) {
echo("No Virtual Data Centres Found n");
exit(1);
}
$my_vdc = $list_vdcs[0]->vdc_id;
/**
* Your specified Virtual Data Centre Identifier
*/
define('MY_VDC',$my_vdc);
echo("Using vdc $my_vdc n");
$list_vlans = $client->ListVLANs(MY_VDC);
if(count($list_vlans) == 0){
$my_vlan = -1;
}else{
$my_vlan = $list_vlans[0]->vlan_id;
}
/**
* Your specified Virtual Local Area Network Identifier
*/
define('MY_VLAN',$my_vlan);
/**
* Fetch ISO to DISK
*/
$job_id = $client->fetchDisk($iso,null,null,null,$disk_name,GB20_PO,MY_VDC,false);
/**
* Disk Identifier of Downloaded ISO
*/
$new_disk_id = 0;
/**
* Wait for the Fetch Disk Job to Finish / Fail
*/
while(true) {
echo("Waiting for the job to finish n");
$job = $client->GetJob($job_id);
if($job->status == 1) {// Success
echo("Disk is successfully fetched n");
break;
} else if($job->status == 3) { //Failed
echo("Disk Fetching Failed n");
exit(1);
}
sleep(2);
}
/**
* Create blank Virtual Server
*/
$server = $client->CreateServer(MY_VDC,MY_VLAN,$server_name,
BLANK_DISK_IMAGE,GB1_CPU1_PO,array(GB20_PO));
/**
* If Virtual Server failed to be created
*/
if(!isset($server) && is_null($server)) {
echo("Server creation failed n");
exit(1);
}
/**
* List Disks for Virtual Data Centre
*/
$disks=$client->ListDisks(MY_VDC);
/**
* Find New Disk Identifier
*/
foreach($disks as $disk) {
if($disk->disk_name==$disk_name) {
$new_disk_id = $disk->disk_id;
break;
}
}
/**
* Attach new Disk to server and upon success boot
*/
echo("Attaching the disk to the server n");
if($client->AttachDisk($server->server_id, $new_disk_id) == 1) {
echo("Disk Attached Success n");
$client->StartServer($server->server_id);
echo("Echo server start is issuedn");
}
