On 2019-06-10 8:19 a.m., Stefan Hajnoczi wrote:
On Sat, Jun 01, 2019 at 08:12:01PM -0400, Gary Dale wrote:
A while back I converted a raw disk image to qcow2 to be able to use
snapshots. However I realize that I may not really understand exactly how
snapshots work. In this particular case, I'm only talking about internal
snapshots currently as there seems to be some differences of opinion as to
whether internal or external are safer/more reliable. I'm also only talking
about shutdown state snapshots, so it should just be the disk that is
snapshotted.
As I understand it, the first snapshot freezes the base image and subsequent
changes in the virtual machine's disk are stored elsewhere in the qcow2 file
(remember, only internal snapshots). If I take a second snapshot, that
freezes the first one, and subsequent changes are now in third location.
Each new snapshot is incremental to the one that preceded it rather than
differential to the base image. Each new snapshot is a child of the previous
one.
Internal snapshots are not incremental or differential at the qcow2
level, they are simply a separate L1/L2 table pointing to data clusters.
In other words, they are an independent set of metadata showing the full
state of the image at the point of the snapshot. qcow2 does not track
relationships between snapshots and parents/children.
Which sounds to me like they are incremental. Each snapshot starts a new
L1/L2 table so that the state of the previous one is preserved.
One explanation I've seen of the process is if I delete a snapshot, the
changes it contains are merged with its immediate child.
Nope. Deleting a snapshot decrements the reference count on all its
data clusters. If a data cluster's reference count reaches zero it will
be freed. That's all, there is no additional data movement or
reorganization aside from this.
Perhaps not physically but logically it would appear that the data
clusters were merged.
So if I deleted the
first snapshot, the base image stays the same but any data that has changed
since the base image is now in the second snapshot's location. The merge
with children explanation also implies that the base image is never touched
even if the first snapshot is deleted.
But if I delete a snapshot that has no children, is that essentially the
same as reverting to the point that snapshot was created and all subsequent
disk changes are lost? Or does it merge down to the parent snapshot? If I
delete all snapshots, would that revert to the base image?
No. qcow2 has the concept of the current disk state of the running VM -
what you get when you boot the guest - and the snapshots - they are
read-only.
When you delete snapshots the current disk state (running VM) is
unaffected.
When you apply a snapshot this throws away the current disk state and
uses the snapshot as the new current disk state. The read-only snapshot
itself is not modified in any way and you can apply the same snapshot
again as many times as you wish later.
So in essence the current state is a pointer to the latest data cluster,
which is the only data cluster that can be modified.
I've seen it explained that a snapshot is very much like a timestamp so
deleting a timestamp removes the dividing line between writes that occurred
before and after that time, so that data is really only removed if I revert
to some time stamp - all writes after that point are discarded. In this
explanation, deleting the oldest timestamp is essentially updating the base
image. Deleting all snapshots would leave me with the base image fully
updated.
Frankly, the second explanation sounds more reasonable to me, without having
to figure out how copy-on-write works, But I'm dealing with important data
here and I don't want to mess it up by mishandling the snapshots.
Can some provide a little clarity on this? Thanks!
If you want an analogy then git(1) is a pretty good one. qcow2 internal
snapshots are like git tags. Unlike branches, tags are immutable. In
qcow2 you only have a master branch (the current disk state) from which
you can create a new tag or you can use git-checkout(1) to apply a
snapshot (discarding whatever your current disk state is).
Stefan
That's just making things less clear - I've never tried to understand
git either. Thanks for the attempt though.
If I've gotten things correct, once the base image is established, there
is a current disk state that points to a table containing all the writes
since the base image. Creating a snapshot essentially takes that pointer
and gives it the snapshot name, while creating a new current disk state
pointer and data table where subsequent writes are recorded.
Deleting snapshots removes your ability to refer to a data table by
name, but the table itself still exists anonymously as part of a chain
of data tables between the base image and the current state.
This leaves a problem. The chain will very quickly get quite long which
will impact performance. To combat this, you can use blockcommit to
merge a child with its parent or blockpull to merge a parent with its child.
In my situation, I want to keep a week of daily snapshots in case
something goes horribly wrong with the VM (I recently had a database
file become corrupt, and reverting to the previous working day's image
would have been a quick and easy solution, faster than recovering all
the data tables from the prefious day). I've been shutting down the VM,
deleting the oldest snapshot and creating a new one before restarting
the VM.
While your explanation confirms that this is safe, it also implies that
I need to manage the data table chains. My first instinct is to use
blockcommit before deleting the oldest snapshot, such as:
virsh blockcommit <vm name> <qcow2 file path> --top <oldest
snapshot> --delete --wait
virsh snapshot-delete --domain <vm name> --snapshotname <oldest
snapshot>
so that the base image contains the state as of one week earlier and the
snapshot chains are limited to 7 links.
1) does this sound reasonable?
2) I note that the syntax in virsh man page is different from the syntax
at
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_deployment_and_administration_guide/sect-backing-chain
(RedHat uses --top and --base while the man page just has optional base
and top names). I believe the RedHat guide is correct because the man
page doesn't allow distinguishing between the base and the top for a commit.
However the need for specifying the path isn't obvious to me. Isn't the
path contained in the VM definition?
Since blockcommit would make it impossible for me to revert to an
earlier state (because I'm committing the oldest snapshot, if it screws
up, I can't undo within virsh), I need to make sure this command is correct.