The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7122
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === As tarball creation will not create consistent backup when instance is running and files are being modified during backup, we avoid this by creating a read-only snapshot of the main volume. This is possible because BTRFS allows us to create a snapshot quickly without freezing the main volume.
From 6be11c50f62d110b63fed72b9fbaddae6aa2aa2e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 2 Apr 2020 15:51:53 +0100 Subject: [PATCH 1/5] lxd/storage/drivers/driver/zfs/volumes: Comment clarification Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_zfs_volumes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 143c660e20..d121254e0d 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -1186,7 +1186,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error { // Handle the non-optimized tarballs through the generic packer. if !optimized { - // For block volumes that are exporting snapshots, we need to mount parent volume first so that + // For block volumes that are exporting snapshots, we need to activate parent volume first so that // the snapshot volumes can have their devices accessible. if vol.contentType == ContentTypeBlock && snapshots { parent, _, _ := shared.InstanceGetParentAndSnapshotName(vol.Name()) From 77d6616b4be115bb8b23349be0014ae6db575e77 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 2 Apr 2020 17:34:58 +0100 Subject: [PATCH 2/5] lxd/storage/drivers/volume: Adds support for setting custom mount path Can be used when creating snapshots at a custom location that needs to be reflected in Volume.MountPath(). Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/volume.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go index f612b5f2e0..e52f7ad685 100644 --- a/lxd/storage/drivers/volume.go +++ b/lxd/storage/drivers/volume.go @@ -61,14 +61,15 @@ var BaseDirectories = map[VolumeType][]string{ // Volume represents a storage volume, and provides functions to mount and unmount it. type Volume struct { - name string - pool string - poolConfig map[string]string - volType VolumeType - contentType ContentType - config map[string]string - driver Driver - keepDevice bool + name string + pool string + poolConfig map[string]string + volType VolumeType + contentType ContentType + config map[string]string + driver Driver + keepDevice bool + customMountPath string } // NewVolume instantiates a new Volume struct. @@ -121,6 +122,10 @@ func (v Volume) IsSnapshot() bool { // MountPath returns the path where the volume will be mounted. func (v Volume) MountPath() string { + if v.customMountPath != "" { + return v.customMountPath + } + return GetVolumeMountPath(v.pool, v.volType, v.name) } From 6c48834fb7ebb3a84d4cbfe44e8a969592b0ba33 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 2 Apr 2020 17:36:37 +0100 Subject: [PATCH 3/5] lxd/storage/drivers/driver/btrfs/volumes: Create temporary snapshot in BackupVolume() Ensures consistent backups when instance files are being modified during backup. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 6a592f1af9..656cf44b23 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -650,6 +650,37 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error { // Handle the non-optimized tarballs through the generic packer. if !optimized { + // Because the generic backup method will not take a consistent backup if files are being modified + // as they are copied to the tarball, as BTRFS allows us to take a quick snapshot without impacting + // the parent volume we do so here to ensure the backup taken is consistent. + if vol.contentType == ContentTypeFS { + sourcePath := vol.MountPath() + poolPath := GetPoolMountPath(d.name) + tmpDir, err := ioutil.TempDir(poolPath, "backup.") + if err != nil { + return errors.Wrapf(err, "Failed to create temporary directory under %q", poolPath) + } + defer os.RemoveAll(tmpDir) + + err = os.Chmod(tmpDir, 0100) + if err != nil { + return errors.Wrapf(err, "Failed to chmod %q", tmpDir) + } + + // Override volume's mount path with location of snapshot so genericVFSBackupVolume reads + // from there instead of main volume. + vol.customMountPath = filepath.Join(tmpDir, vol.name) + + // Create the read-only snapshot. + mountPath := vol.MountPath() + err = d.snapshotSubvolume(sourcePath, mountPath, true, true) + if err != nil { + return err + } + d.logger.Debug("Created read-only backup snapshot", log.Ctx{"sourcePath": sourcePath, "path": mountPath}) + defer d.deleteSubvolume(mountPath, true) + } + return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op) } From a86bed47ad14d379261f1a227cb3393a48176353 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 2 Apr 2020 17:37:55 +0100 Subject: [PATCH 4/5] lxd/storage/drivers/driver/btrfs/volumes: Renames container vars to instance Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 656cf44b23..1c0f5af81b 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -763,23 +763,23 @@ func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWr } } - // Make a temporary copy of the container. + // Make a temporary copy of the instance. sourceVolume := vol.MountPath() - containersPath := GetVolumeMountPath(d.name, vol.volType, "") + instancesPath := GetVolumeMountPath(d.name, vol.volType, "") - tmpContainerMntPoint, err := ioutil.TempDir(containersPath, "backup.") + tmpInstanceMntPoint, err := ioutil.TempDir(instancesPath, "backup.") if err != nil { - return errors.Wrapf(err, "Failed to create temporary directory under '%s'", containersPath) + return errors.Wrapf(err, "Failed to create temporary directory under '%s'", instancesPath) } - defer os.RemoveAll(tmpContainerMntPoint) + defer os.RemoveAll(tmpInstanceMntPoint) - err = os.Chmod(tmpContainerMntPoint, 0100) + err = os.Chmod(tmpInstanceMntPoint, 0100) if err != nil { - return errors.Wrapf(err, "Failed to chmod '%s'", tmpContainerMntPoint) + return errors.Wrapf(err, "Failed to chmod '%s'", tmpInstanceMntPoint) } // Create the read-only snapshot. - targetVolume := fmt.Sprintf("%s/.backup", tmpContainerMntPoint) + targetVolume := fmt.Sprintf("%s/.backup", tmpInstanceMntPoint) err = d.snapshotSubvolume(sourceVolume, targetVolume, true, true) if err != nil { return err From b13c5a27a80f19f7b0a45b22423df2efb4790138 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 2 Apr 2020 17:40:34 +0100 Subject: [PATCH 5/5] lxd/storage/drivers/driver/btrfs/volumes: Consistent quoting of error message variables Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 1c0f5af81b..4841be48e3 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -197,13 +197,13 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData i // Create a temporary directory to unpack the backup into. unpackDir, err := ioutil.TempDir(GetVolumeMountPath(d.name, vol.volType, ""), "backup.") if err != nil { - return nil, nil, errors.Wrapf(err, "Failed to create temporary directory under '%s'", GetVolumeMountPath(d.name, vol.volType, "")) + return nil, nil, errors.Wrapf(err, "Failed to create temporary directory under %q", GetVolumeMountPath(d.name, vol.volType, "")) } defer os.RemoveAll(unpackDir) err = os.Chmod(unpackDir, 0100) if err != nil { - return nil, nil, errors.Wrapf(err, "Failed to chmod '%s'", unpackDir) + return nil, nil, errors.Wrapf(err, "Failed to chmod %q", unpackDir) } // Extract main volume. @@ -325,13 +325,13 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v // Create a temporary directory which will act as the parent directory of the received ro snapshot. tmpVolumesMountPoint, err := ioutil.TempDir(instancesPath, "migration.") if err != nil { - return errors.Wrapf(err, "Failed to create temporary directory under '%s'", instancesPath) + return errors.Wrapf(err, "Failed to create temporary directory under %q", instancesPath) } defer os.RemoveAll(tmpVolumesMountPoint) err = os.Chmod(tmpVolumesMountPoint, 0100) if err != nil { - return errors.Wrapf(err, "Failed to chmod '%s'", tmpVolumesMountPoint) + return errors.Wrapf(err, "Failed to chmod %q", tmpVolumesMountPoint) } wrapper := migration.ProgressWriter(op, "fs_progress", vol.name) @@ -507,7 +507,7 @@ func (d *btrfs) SetVolumeQuota(vol Volume, size string, op *operations.Operation } if id == "" { - return fmt.Errorf("Failed to find subvolume id for %s", volPath) + return fmt.Errorf("Failed to find subvolume id for %q", volPath) } // Create a qgroup. @@ -607,13 +607,13 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m // Create a temporary directory which will act as the parent directory of the read-only snapshot. tmpVolumesMountPoint, err := ioutil.TempDir(instancesPath, "migration.") if err != nil { - return errors.Wrapf(err, "Failed to create temporary directory under '%s'", instancesPath) + return errors.Wrapf(err, "Failed to create temporary directory under %q", instancesPath) } defer os.RemoveAll(tmpVolumesMountPoint) err = os.Chmod(tmpVolumesMountPoint, 0100) if err != nil { - return errors.Wrapf(err, "Failed to chmod '%s'", tmpVolumesMountPoint) + return errors.Wrapf(err, "Failed to chmod %q", tmpVolumesMountPoint) } // Make read-only snapshot of the subvolume as writable subvolumes cannot be sent. @@ -769,13 +769,13 @@ func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWr tmpInstanceMntPoint, err := ioutil.TempDir(instancesPath, "backup.") if err != nil { - return errors.Wrapf(err, "Failed to create temporary directory under '%s'", instancesPath) + return errors.Wrapf(err, "Failed to create temporary directory under %q", instancesPath) } defer os.RemoveAll(tmpInstanceMntPoint) err = os.Chmod(tmpInstanceMntPoint, 0100) if err != nil { - return errors.Wrapf(err, "Failed to chmod '%s'", tmpInstanceMntPoint) + return errors.Wrapf(err, "Failed to chmod %q", tmpInstanceMntPoint) } // Create the read-only snapshot. @@ -864,7 +864,7 @@ func (d *btrfs) RestoreVolume(vol Volume, snapshotName string, op *operations.Op backupSubvolume := fmt.Sprintf("%s%s", vol.MountPath(), tmpVolSuffix) err := os.Rename(vol.MountPath(), backupSubvolume) if err != nil { - return errors.Wrapf(err, "Failed to rename '%s' to '%s'", vol.MountPath(), backupSubvolume) + return errors.Wrapf(err, "Failed to rename %q to %q", vol.MountPath(), backupSubvolume) } // Setup revert logic.
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel