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

tomponline on Github lxc-bot at linuxcontainers.org
Fri Apr 3 10:09:27 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 585 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200403/2974042d/attachment.bin>
-------------- next part --------------
From bdf197028d9f5965fc602c24a22dc069cb899f29 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 3 Apr 2020 11:07:34 +0100
Subject: [PATCH] lxd/storage/drivers/driver/zfs/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_zfs_volumes.go | 49 +++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index d121254e0d..b8f1426b7e 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -1201,6 +1201,55 @@ func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWrit
 			}
 		}
 
+		// Because the generic backup method will not take a consistent backup if files are being modified
+		// as they are copied to the tarball, as ZFS 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 {
+			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)
+			}
+
+			// Create a temporary snapshot.
+			srcSnapshot := fmt.Sprintf("%s at backup-%s", d.dataset(vol, false), uuid.NewRandom().String())
+			_, err = shared.RunCommand("zfs", "snapshot", srcSnapshot)
+			if err != nil {
+				return err
+			}
+			defer shared.RunCommand("zfs", "destroy", srcSnapshot)
+			d.logger.Debug("Created backup snapshot", log.Ctx{"dev": srcSnapshot})
+
+			// Override volume's mount path with location of snapshot so genericVFSBackupVolume reads
+			// from there instead of main volume.
+			vol.customMountPath = tmpDir
+
+			// Mount the snapshot directly (not possible through ZFS tools), so that the volume is
+			// already mounted by the time genericVFSBackupVolume tries to mount it below,
+			// thus preventing it from trying to unmount it at the end, as this is a custom snapshot,
+			// the normal mount and unmount logic will fail.
+			err = TryMount(srcSnapshot, vol.MountPath(), "zfs", 0, "")
+			if err != nil {
+				return err
+			}
+			d.logger.Debug("Mounted ZFS snapshot dataset", log.Ctx{"dev": srcSnapshot, "path": vol.MountPath()})
+
+			defer func(dataset string, mountPath string) {
+				_, err := forceUnmount(mountPath)
+				if err != nil {
+					return
+				}
+
+				d.logger.Debug("Unmounted ZFS snapshot dataset", log.Ctx{"dev": dataset, "path": mountPath})
+			}(srcSnapshot, vol.MountPath())
+		}
+
 		return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op)
 	}
 


More information about the lxc-devel mailing list