[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