[lxc-devel] [lxd/master] Storage: Take snapshot when backing up BTRFS volumes

tomponline on Github lxc-bot at linuxcontainers.org
Thu Apr 2 16:43:21 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 597 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200402/e38d7db0/attachment-0001.bin>
-------------- next part --------------
From 6be11c50f62d110b63fed72b9fbaddae6aa2aa2e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.


More information about the lxc-devel mailing list