How to create a KVM image

Modified: 26 Jan 2023 22:12 UTC

Create a hardware virtual machine (KVM) image for use in Triton. Note: This process is for operators who need complete control over the image creation process. If possible, operators should use the SDC create-image-from-machine tooling, as it is both quicker and more concise. For more information on that process, please see How to create a Linux image using create-image-from-machine

Preparation

Create the image

The process for creating a new KVM image in Triton is to first create an empty, minimal instance into which we install our Operating System, VMTools, and any other required software.

Our working environment during this guide is the head node of your Triton installation, but a compute node will also be sufficient. 

Create an empty instance

First you must create a file that describes your minimal instance, which is a JSON file like the following. 

/var/tmp/vm.json

{
  "brand": "kvm",
  "vcpus": 1,
  "autoboot": false,
  "ram": 4096,
  "disks": [
    {
      "boot": true,
      "model": "virtio",
      "size": 40960
    }
  ],
  "nics": [
    {
      "nic_tag": "external",
      "ip": "dhcp",
      "primary": "true",
      "model": "virtio"
    }
  ]
}

With this file, create the instance with the following command. 

headnode# vmadm create -f /var/tmp/vm.json

Upon completion, you will see the following message.

Successfully created VM e9d0f9b7-e76e-474a-8951-e12e227f24d6

Take note of this UUID.

Copy in required files (OS, SDC VM tools, etc.)

Your ISOs need to be moved to the root directory of your new, empty instance. Your directory structure will need to look like the following.

headnode# ls -alh /zones/23cce230-4881-4684-a918-63d6d765f457/root
total 3676773
drwxr-xr-x  10 root     root          12 Feb 24 15:59 .
drwx------   6 root     root           7 Feb 24 15:59 ..
drwxr-xr-x  15 root     root          15 Feb 24 15:59 dev
drwxr-xr-x   3 root     root           3 Feb 24 15:59 etc
drwxr-xr-x   2 root     root           2 Feb 24 15:59 lib
drwxr-xr-x   2 root     root           2 Feb 24 15:59 sbin
drwxr-xr-x   2 root     root           2 Feb 24 15:59 smartdc
-rwxr-xr-x   1 root     root        2.5K Feb 24 15:59 startvm
drwxrwxrwt   2 root     root          10 Feb 24 16:00 tmp
drwxr-xr-x   2 root     root           2 Feb 24 15:59 usr
drwxr-xr-x   3 root     root           3 Feb 24 15:57 var
-rw-r--r--   1 root     root        1.8G Feb 24 15:59 operating-system.iso
-rw-r--r--   1 root     root        1.7M Feb 24 16:04 vmtools.iso

Boot the instance

headnode# vmadm boot e9d0f9b7-e76e-474a-8951-e12e227f24d6 \
  order=cd,once=d \
  cdrom=/operating-system.iso,ide \
  cdrom=/vmtools.iso,ide

Upon successful boot, you will see the following message.

Successfully started VM e9d0f9b7-e76e-474a-8951-e12e227f24d6

Connect to the instance

Connection to the instance is done over VNC. To get the instance's VNC details, run the following command.

headnode# vmadm info e9d0f9b7-e76e-474a-8951-e12e227f24d6 vnc 

This command will provide details such as the following.

{
  "vnc": {
    "host": "10.0.1.7",
    "port": 63380,
    "display": 57480
  }
}

Use the values of the host and port fields in your VNC application to access the console of the instance.

Prepare the instance

You will now set up the instance image. Depending on whether this is a Windows image or Linux image, you can follow the corresponding guide for that operating system.

Finalize the image

With your instance in a "stopped" state, you will now save and compress the instance's OS disk. To get the correct disk, run the following command.

headnode# vmadm get e9d0f9b7-e76e-474a-8951-e12e227f24d6 | json -aH disks

From the output of this command, use the value of zfs_filesystem to perform the following commands. 

headnode# cd /var/tmp
headnode# zfs snapshot zones/e9d0f9b7-e76e-474a-8951-e12e227f24d6-disk0@dataset
headnode# zfs send zones/e9d0f9b7-e76e-474a-8951-e12e227f24d6-disk0@dataset > my-example-1.0.0.zvol \
  && gzip my-example-1.0.0.zvol

Note: If you're working from a compute node to create your image, you will need to transfer the resulting file to the head node.

You must now create a manifest that describes your image. The following is an example manifest for a Windows Server 2008 R2 image.

/var/tmp/vm.manifest

{
  "v": "2",
  "uuid": "3d73e502-9fa6-11e3-8fa8-bb6a8876bb21",
  "owner": "9dce1460-0c4c-4417-ab8b-25ca478c5a78",
  "name": "my-example",
  "description": "my-example 1.0.0 VM image",
  "version": "1.0.0",
  "state": "active",
  "disabled": false,
  "public": true,
  "os": "windows",
  "type": "zvol",
  "files": [
    {
      "sha1": "fd3dfc22dfbc53c0aeb759a6224ef240066d59e4",
      "size": 264,
      "compression": "gzip"
    }
  ],
  "requirements": {
    "networks": [
      {
        "name": "net0",
        "description": "public"
      }
    ],
    "ssh_key": true
  },
  "generate_passwords": "true",
  "users": [
    {
      "name": "administrator"
    }
  ],
  "image_size": "40960",

  "disk_driver": "virtio",
  "nic_driver": "virtio",
  "cpu_type": "host"
}

The following fields should be replaced with the corresponding commands.

Field Command
uuid uuid
files.sha1 "sum -x sha1 /path/to/zvol.gz | cut -d' ' -f1"
files.size ls -l /path/to/zvol.gz | awk '{ print $5 }'
owner sdc-ldap s 'login=admin' | grep ^uuid | cut -d' ' -f2 on the head node (this will be the admin user's UUID)
image_size cat /var/tmp/vm.json | json -aH disks[0].size, if you've kept your /var/tmp/vm.json file, which is the size of the root disk of your image

Note: For Windows image, your instance must contain the "generate_passwords" and "users" field containing the "administrator" user (as seen in the above example).

With the manifest complete, you can proceed to the importing an image section.

Cleanup

Once your image is imported and tested, you should destroy the instance that was used to create it.

headnode# vmadm destroy e9d0f9b7-e76e-474a-8951-e12e227f24d6

You should also either destroy or move your image's zvol.gz, manifest, and any other temporary files used to create this image.