[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