1.0. Problem Description

Up until CloudStack version 4.20.0.0 there was no support for native VM backups, only volume backups; VM backups relied on either Veeam or Dell Networker. Version 4.20.0.0 introduced the NAS backup plugin, a native backup solution for KVM full VM backups. However, the current functionality is not adequate for all use cases, namely, the following flaws are present:

Motivated by the aforementioned points and to provide a more complete native backup plugin for the KVM hypervisor, this document will present a novel virtual machine native backup solution for Apache CloudStack and KVM.

1.1. Basic Definitions

In this section we present some basic definitions that will be used throughout this spec:

To illustrate the active backup chain and backup delta definitions, let us follow the example below:

 

  1. At the start, the VM will not have an active backup chain, as no backups have been taken, this state is illustrated in figure 1:

    backup-chain1 Figure 1. Initial state.
  2. Then, during the first backup, a backup delta will be created and the volume will be consolidated and copied to the secondary storage, forming the first (full) backup. Also, the VM will have an active backup chain. This state is illustrated in figure 2:

    backup-chain2 Figure 2. After taking the first backup.
  3. During the second backup, a new backup delta will be created, the previous one will be copied to the secondary storage, forming the second (incremental) backup. Also, the active backup chain size will be increased by one. Figure 3 illustrates the state during the copy from primary to secondary of the previous backup delta. After it is copied, it will be merged into the base volume.

    backup-chain3 Figure 3. Taking a second backup.
  4. Considering the state above, after taking another backup, the number of backups on the chain will have reached the value configured for backup.chain.delta, and thus the backup chain will end, and we will not have an active backup chain, neither will we have a backup delta, this state is illustrated in figure 4:

    backup-chain4 Figure 4. After a backup chain was completed.

2.0. Proposed Changes

To address the described problems, we propose to create a new backup plugin that will use native features to support incremental VM backups with KVM and ACS when using file-based storage. Many ideas for the incremental backups will be the same as the ones in #9524. The main characteristics of the feature are outlined below, all of them will be further presented and explained in the next sections.

Allow users to download a single file from a backup. Where users inform a path inside one of the backed up volumes and ACS generates a download link for it.


2.1. Limitations

While this feature will be compatible with disk-only VM snapshots, if a VM snapshot is reverted while there is an active backup chain, the next backup may have to be a full backup. When reverting a backup, if the VM has any VM snapshots, those will need to be removed before reverting the backup. Otherwise, we would have to create a new tree for the VM's volumes, and keep many trees would add complexity to the implementation. This limitation might be addressed in the future if a significant use case is presented.


3.0. Backup Offerings

Currently, the only way to create new offerings is to import them, using an external ID listed by the backup provider. Since we are proposing a solution where ACS is the provider, we will not have an external ID to inform on the importBackupOffering. Thus, a new API called createBackupOffering will be created, which will define a new backup offering from scratch, this API will have the necessary parameters to configure the behavior of KBOSS backups.

The createBackupOffering API will have the following parameters:

ParameterDescriptionDefault ValueRequired
nameSpecifies the name of the offering-Yes
descriptionThe description of the backup offering-Yes
compressSpecifies whether the offering supports backup compressionfalseNo
validateSpecifies whether the offering supports backup validationfalseNo
validationStepsA comma-separated list of which validation steps should be performed.

screenshot

No
validationIntervalSpecifies the interval (in hours) between two validations of the same backupfalseNo
allowQuickRestoreSpecifies whether the offering supports quick restorefalseNo
allowExtractFileSpecifies whether the offering supports file extraction from backupsfalseNo
compressionlibraryCompression library, for offerings that support compression. Accepted values are zstd and zlib. If the image only supports zlib, it will be used regardless of this parameter.zstdNo
backupchainsizeBackup chain size for backups created with this offering.-No
zoneIdThe zone ID for the offering-Yes
userDrivenBackupsWhether users are allowed to create adhoc backups and backup schedulesYes
domainIdsthe ID of the containing domain(s), null for public offerings

-

No

4.0. VM Backup Creation

We will create three new DB tables and a new view, native_backup_pool_ref, native_backup_store_ref, backup_details and native_backup_view. The first will store metadata about backup deltas, which are on the primary storage; the second will store metadata about the backups, which are on the secondary storage; the third table will be a generic details table, that may be used by any backup provider, for this provider, a some information will be stored in it: Whether the backup is current, if it is the end of its chain, what is its parent and which image store it is in. Regarding the view, it is used to facilitate some processes in the code. When a backup delta is created, all three tables will be updated.

The native_backup_pool_ref table will contain the following columns:

NameTypeDescription
idbigintRow ID
backup_idbigintThe backup's ID. Foreign key that points to the backups table.
storage_pool_idbigintThe storage's ID. Foreign key that points to the storage_pool table.
volume_idbigintThe volume's ID. Foreign key that points to the volumes table.
backup_delta_pathvarchar(255)Path of the created delta
backup_parent_pathvarchar(255)Path of the created delta's parent

The native_backup_store_ref table will contain the following columns:

NameTypeDescription
idbigintRow ID
backup_idbigintThe backup's ID. Foreign key that points to the backups table.
volume_idbigintThe volume's ID. Foreign key that points to the volumes table.
volume_sizebigintThe volume's size at the time of the backup
pathvarchar(255)Path of the backup

The backup_details will be the same as all the other details tables:

NameTypeDescription
idbigintRow ID
backup_idbigintThe backup's ID. Foreign key that points to the backups table.
namevarchar(255)The detail name
valuevarchar(1024)The detail value
displaytinyint(1) unsigned

The native_backup_view will contain the following columns:

NameTypeDescription
idbigintBackup ID
uuidvarchar(255)Backup UUID
vm_idbigintVM ID
typevarchar(255)Backup type
datedatetimeBackup creation date
statusvarchar(32)Backup status
backup_offering_idbigintBackup offering ID
image_store_idtextSecondary storage ID where the backup is stored
parent_idtextBackup parent ID
end_of_chaintextIf the backup is the end of its chain
currenttextIf the backup is current or not

Furthermore a new configuration will be created: backup.chain.size, which will determine the max size of a backup chain. The default value will be 8. If cloud admins set it to 1, all the backups will be full backups. With values lower than 1, the backup chain will be unlimited, unless it is stopped by another process. Please note that unlimited backup chains have a higher chance of getting corrupted, as new backups will be dependant on all of the older ones. In any case, if the backup has validation enabled, the validation process will catch corrupt backups.

Two new VM states will be created:

The proposed VM backup creation workflow is summarized in the following diagram. Every step is repeated for all the volumes of the VM, except the virDomainSnapshotCreateXML step and the validation step, which are done for the VM as a whole.

create-backup

In order to let users choose whether the VM will be frozen for the snapshot process, a new parameter will be added to the createBackup and createBackupSchedule APIs, called quiesceVM. The VM will only be kept frozen during the initial snapshot creation process, during the rest of the backup it will be running.

If a VM snapshot is reverted while there is an active backup chain, the next backup may have to be a full backup:

The impacts this feature will have on the disk-only VM snapshot feature will be further discussed on the VM snapshot section.

4.0.1. Backup Compression

If the backup offering supports compression, after all the backup creation steps have finished (but before validation, if supported), we will launch a asynchronous job to start the backup compression. The compression will be broken into two steps: compress the backup into new files and substitute the original backup files. The reason to break the compression into two steps is so that while the first step is being done, the backup may still be restored; the restore will only be blocked during the last step, which is the fastest. These jobs will execute in the compute plane hosts instead of the SSVMs.

The host will have the better performance, meaning that the compression will be faster. However, if left unlimited, multiple concurrent compression processes could affect the host's performance, thus the guest VMs would be affected. On the other hand, the SSVM tends to be quite limited, and would easily get overloaded. Meaning that operators might need to upgrade their compute offerings, and also multiply the number of SSVMs in the environment (which also means more public IPs being used). Also, we need the compression process to not take too long, otherwise it might not end before the next backup.

To address the concerns over the host's resource usage, a few new configurations will be created:

The compression process follows the following steps, each being its own job:

  1. Compression start: Call qemu-img convert to create a copy of the backup and compress it, informing its backing file and the -c flag. This is the slowest part.
  2. Compression finalization: Move the compressed file on top of the old uncompressed file.

In order to add more information for users regarding the backup compression, two new columns will be added to the backups table:

NameTypeDescription
compression_statusvarchar(55)Compression status, which may be Uncompressed, Compressing, FinalizingCompression, Compressed or CompressionError
uncompressed_sizebigintOld physical size of the backup, before the compression was done

As the compression is a long running job, it is prone for timeouts or interruptions, thus, the backup.compression.max.job.retries and backup.compression.retry.interval configurations will be created, which will determine the number of retries and their interval, each job will be retried a number of times equal to the value of the backup.compression.max.job.retries configuration, by default, 2.

A new global and cluster-wide configuration (backup.compression.timeout) will be created to determine the timeout of the compression. Defaults to 8 hours. Values lower than 1 will disable the timeout. If the backup compression times out, it may be retried, if all the retries fail, the compression state will go into CompressionError, any leftover files will be deleted and the backup will be left uncompressed.

To allow operators to track the compression jobs, a new API (listBackupCompressionJobs) will be added, it will allow listing the current, scheduled and executed compression jobs. It will only be available for Root Admin accounts. It will have the following parameters:

ParameterDescription
idList only the job with the specified ID
backupidList jobs associated with the specified backup
hostidList jobs associated with the specified host. When this parameter is provided, the executing parameter is implicit
zoneidList jobs associated with the specified zone
typeList jobs of the specified type. Accepts Starting or Finalizing
executingList jobs that are currently executing
scheduledList jobs scheduled to run in the future

To allow operators to pause the compression processes, the backup.compression.task.enabled account/domain/global setting will be created, when set to false in the global scope, no compression jobs will be executed, regardless of the value in other scopes. When true in the global scope, you may disable the compression task for certain domains or accounts by setting the value to false in the domain/account.

4.1. Backup Restoration

As with the backup creation, this process will be queued with the other VM jobs and serialized, so we will not have to worry about the VM starting during the restoration process. Furthermore, the current backup chain will be severed.

It is important to note that, when restoring a backup, the size of the VM's volumes will be restored to what it was when the backup was taken. This happens because each QCOW2 has its size defined within themselves, thus, when copying a backup from the secondary storage to substitute the VM's volumes, the size of the volumes will be updated as well.

4.1.1 Standard Restoration

The proposed standard VM backup restoration workflow is summarized in the following diagram. Every step, except the two initial verifications, is repeated for all the volumes of the VM.

standard-reversion

After a backup restoration, all of the restored volumes will be full clone, no longer depending on a template.

4.1.2 Volume Restoration

We will also allow the user to restore a single volume that was part of the backup. A new volume will be created and attached to the target VM. The proposed volume backup restoration workflow is summarized in the following diagram.

volume-reversion

Concept-wise the implementation is simple, but a new workflow must be created so that ACS allocates a volume to a primary storage before attaching it to the VM, so that we have a target for step 2.

4.1.3 Quick Restoration

The quick restoration process is similar to the two processes above, however, instead of converting the volume from the secondary storage, for each volume being reverted, we create a delta on the primary storage that has the backup in the secondary storage as a backing file. Then, we start the VM (if it is not running when quick restoring a single volume), and use Libvirt's virDomainBlockPull to consolidate the volumes while the VM runs. The diagram below describes the process for quick reverting a VM. The process for a single volume will be similar, except for the VM start, which will only happen if the VM is not running already.

quick-reversion-workflow

Since the process above involves a lot of communication between different components. The following (simplified) sequence diagram details a bit more the process:

quick-reversion-sequence

The advantage of using the quick restore over the standard one is that the user will not have to wait for the backup copy into the primary storage to start using the VM. However, during the consolidation process, the volume performance might be affected, as any reads to old data must fetch the data from the secondary storage.

4.1.4 Create VM from backup

This feature will be compatible with KBOSS, as we mostly just need to restore the backup to the given VM.


4.2. Backup Deletion

The backup deletion process is somewhat similar to the KVM incremental snapshot deletion process (see #8907). It will also be added to the VM job queue, since it might have to merge the top delta file of the VM's volumes. The process is summarized on the diagram below.

backup-deletion

5.0. Backup Validation

The backup validation process will consist of three steps: wait for the VM to boot; execute a user-defined command; take a screenshot of the console of the VM. The first two steps can only be performed if the VM has the QEMU guest agent installed and it is configured to start on system startup. The main idea of the backup validation process is to verify that the VM is able to boot using the backup being validated. The validation steps that will be executed are defined in the native backup offering through the validationSteps parameter, by default, only the screenshot step is executed.

The backup validation will be executed at the end of the backup creation process, as an asynchronous job. To allow operators to pause the validation processes, the backup.validation.task.enabled account/domain/global setting will be created, when set to false in the global scope, no validation jobs will be executed, regardless of the value in other scopes. When true in the global scope, you may disable the validation task for certain domains or accounts by setting the value to false in the domain/account.

Furthermore, operators will be able to configure periodic validation of already validated backups. At the end of the validation process, if the backup is deemed valid, a hash of the backup will be stored. The next validations of that backups will only take the hash and compare it to the original. Since the backup was already marked as valid, as long as the hash of the backup does not change, it should remain valid. To configure the periodic backup validation process, operators may use the validationInterval parameter of the native backup offerings. Furthermore, a new VM setting with the same name will be created that overrides the configuration set in the backup offering. By default, only operators will be able to see this VM setting, as it will be added to the user.vm.denied.details list of settings.

The backup.validation.max.concurrent.operations configuration will be created to determine the max number of concurrent validations per host. The idea is to limit the concurrent load on the hosts. Defaults to 1. Values lower than 1 remove the limit, meaning that as many validations as necessary will be done at the same time. This configuration will be at the cluster and global level. A new property on agent.properties will be created with the same name that when set will override the cluster/global level.

Regarding the wait for boot step, the backup.validation.boot.default.timeout configuration will be created to specify the default time to wait for a validation VM to boot. By default it is set to 240 seconds. It will be at the account/domain/global scope. Furthermore, a VM setting called backupValidationBootTimeout will also be created for the same purpose, when set, it overrides the value on the backup.validation.boot.default.timeout configuration. By default, the VM setting will only be writable for operators.

Regarding the command execution step, 4 new VM settings will be created to configure how this step will be executed:

By default, backupValidationCommand, backupValidationCommandArguments and backupValidationCommandExpectedResult will be available for end-users to edit. While backupValidationCommandTimeout will be visible to end-users, but not editable.

As backupValidationCommand will only accept a single command or path to an executable script that must exist inside the user's VM, users are encouraged to create scripts inside their VM that validate what is needed and inform the script's fully qualified path on the setting.

Regarding the screenshot step, the backup.validation.screenshot.default.timeout configuration will be created to specify the default time to wait before taking a screenshot of the VM's console. By default it is set to 60 seconds. It will be at the account/domain/global scope. Furthermore, a VM setting called backupValidationScreenshotWait will also be created for the same purpose, when set, it overrides the value on the backup.validation.screenshot.default.timeout configuration. By default, the VM setting will only be writable for operators.

Moreover, to recover the screenshots, a new API will be created called downloadValidationScreenshot, this API will have a single parameter, backupId, which is the ID of the backup that should have its screenshot downloaded. Each backup can have at most a single validation screenshot.

The following diagram describes the backup validation process.

backup-validation

6.0. File Extraction

Sometimes, users do not need to restore a complete backup of their volumes. Instead, they simply want a single file from the backup. To allow users to extract a single file from created backups, a new API extractFileFromBackup will be created, which, when called, will return a download link for the Zipped file. This API will have the following parameters:

NameDescriptionTypeRequired
backupIdthe ID of the backupLongYes
volumeIdthe ID of the volumeLongYes
filePaththe path to the file/directoryLongYes
partitionWhich partition is the file located. If not informed, every non boot partition will be searchedStringNo
fileSystemWhich FS is being used in the given partition. If not informed, ACS will try to derive itStringNo

The API workflow is summarized below:

file-extraction

Please note that the file extraction feature is not compatible with encrypted volumes.


7.0. Interactions with other features

This session will go over changes and/or interaction with other features.

7.1. Changes to the Disk-Only VM Snapshot Feature

During the disk-only VM snapshot revert, we must check if there was a backup taken after the snapshot being reverted. If there was, we must merge the backup delta with its parent. The procedure will be the same used in the backup creation process, when the backup being taken is not the first one. Namely this part:

However, if the backup delta is also the current volume's path, we do not need to go through the above procedure. We must only remove the current delta and its parent (which will be another delta).

In any case, if there was a backup after the snapshot being reverted, we must also mark its chain as ended.

Furthermore, all of the file-based KVM disk-only VM snapshot processes that merge deltas will also have to be tweaked to take into account that there might be a backup delta between two VM snapshot deltas.

7.2. Volume Snapshot Restoration

When reverting a volume snapshot, if the VM has an active backup chain, the next backup will be a full backup for the volume that was reverted. Furthermore, the old backup delta for that volume will be removed.

7.3. Volume Attach/Detach

7.3.1 Volume Attach

If a volume is attached after the VM already has backups, it will start to be contemplated on the next backups, even if the next backup will be incremental for all the other volumes, for the newly attached volume, it will be a full backup. When restoring the older backups, if the volume has any backup delta on its backing chain, it will be merged with the rest of the volume, to keep it clean, so that the next backup will be a full one (as the rest of the volumes will be as well).

Even if a VM is restored to a point where the volume was not attached, it will still stay attached and will not be affected by the restore operation.

7.3.2 Volume Detach

If a volume is detached after the VM already has backups and this volume has any backup delta on its backing chain, it will be merged with the rest of the volume, to keep it clean. When restoring the old backups, from when the VM still had that volume, a duplicate volume will be created and attached to the VM.

7.4. Migration

7.4.1 VM Migration

VM migration will not be affected.

7.4.2 Live Volume Migration

During the volume migration process, we will check if the volume is attached to a VM which has a ongoing backup chain. If it is, we will merge the backup delta with its parent, so that the volume is returned to only having either a single file (if full clone) or a top file with a template as a backing file (if linked clone). We do this because we cannot guarantee that a host will have access to the target primary storage to copy the backup delta. For example, for migration between two local storages, the origin host will not have direct access to the target storage, and thus cannot copy the delta by itself.

The next backup for that volume will be a full backup.

7.4.3 Cold Volume Migration

There will be no changes to the cold volume migration process. However, as it consolidates the volume, the next backup for that volume will be a full backup.

7.5. Secondary storage migration

The secondary storage migration process will be executed by the KVM compute plane hosts. The process will be as follows:

Processing each backup individually enables a safe procedure, lowering the chances of a timeout, and diminishing the possible problems with an interruption on the copy process.

8.0. Future works