肥大化したKVMイメージをvirt-sparsifyコマンドで適正サイズに縮小する方法

# はじめに

OpenStackでインスタンスのスナップショットを作成した際、スナップショットのサイズがインスタンスで使用されているディスクサイズよりも大きくなってしまった。

スナップショットの実体はKVMのイメージファイルである。
KVMイメージはスパースファイルであり、ファイル内で0が連続する箇所についてはブロックを割り当てないことで、ディスクを節約することができる。
しかし、ファイルを`rm`コマンド等で削除しただけでは使用されていたブロックが開放されるだけでデータは残ったままになるので、残ったデータはスナップショットのサイズに反映されてしまう。

（参考：<https://milestone-of-se.nesuke.com/sv-basic/linux-basic/resize-kvm-image-sparse-file/>）

本記事では、`virt-sparsify`コマンドを使ってスナップショット（KVMイメージファイル）をスパースすることで肥大化したファイルを適正サイズに縮小する方法を試します。

# 環境

* OS
    * ホスト：ubuntu 16.04.6
    * ゲスト：ubuntu 20.04.3
* OpenStack Queens

# 作業ログ

## 肥大化したスナップショットの作成

OpenStackでインスタンスを作成する。

使用したイメージは以下の通り。

```
root@ctl01:~# openstack image show fd79fd9f-9326-48f5-a365-b7266a5f578e
+------------------+------------------------------------------------------+
| Field            | Value                                                |
+------------------+------------------------------------------------------+
| checksum         | ea59a14b7fa76307007c58e7b4b7e2d0                     |
| container_format | bare                                                 |
| created_at       | 2021-08-26T06:50:29Z                                 |
| disk_format      | qcow2                                                |
| file             | /v2/images/fd79fd9f-9326-48f5-a365-b7266a5f578e/file |
| id               | fd79fd9f-9326-48f5-a365-b7266a5f578e                 |
| min_disk         | 0                                                    |
| min_ram          | 0                                                    |
| name             | ubuntu_20.04.3                                       |
| owner            | 67ad2f1f40574706ab8aca7f7388ba33                     |
| protected        | False                                                |
| schema           | /v2/schemas/image                                    |
| size             | 563019776                                            |
| status           | active                                               |
| tags             |                                                      |
| updated_at       | 2021-08-26T06:50:45Z                                 |
| virtual_size     | None                                                 |
| visibility       | public                                               |
+------------------+------------------------------------------------------+
```

イメージファイルのサイズは、536.94 MB。

インスタンス作成直後のディスク使用量は以下の通り。

```
ubuntu@base-machine:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            977M     0  977M   0% /dev
tmpfs           199M  980K  198M   1% /run
/dev/vda1        20G  1.3G   18G   7% /
tmpfs           994M     0  994M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           994M     0  994M   0% /sys/fs/cgroup
/dev/loop0       56M   56M     0 100% /snap/core18/2128
/dev/loop1       33M   33M     0 100% /snap/snapd/12704
/dev/vda15      105M  5.2M  100M   5% /boot/efi
/dev/loop2       71M   71M     0 100% /snap/lxd/21029
tmpfs           199M     0  199M   0% /run/user/1000
```

ファイルを作成して、`/`をフルにする。

```
ubuntu@base-machine:~$ dd if=/dev/urandom of=dummy bs=1G count=1000
dd: warning: partial read (33554431 bytes); suggest iflag=fullblock
dd: error writing 'dummy': No space left on device
0+574 records in
0+573 records out
19234553856 bytes (19 GB, 18 GiB) copied, 126.173 s, 152 MB/s
ubuntu@base-machine:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        20G   20G     0 100% /
```

作成したファイルを削除する。

```
ubuntu@base-machine:~$ rm dummy
ubuntu@base-machine:~$ df -h / 
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        20G  1.3G   18G   7% /
```

次に作成したインスタンスのスナップショットを作成する。

```
root@ctl01:~# openstack server image create --name large-snapshot 13c6d40b-a1b6-41b4-8b87-e57c6a35ec4a

... snip ...

root@ctl01:~# openstack image show cdc518f4-1ea4-4fca-adf9-20b57f827c1c |grep size
| size             | 20685586432                                                                                                                                                                                                                                                                                                                                              |
| virtual_size     | None                                                                                                                                                                                                                                                                                                                                                     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
```

サイズは19.26 GBと`/`の使用量よりも大きくなった。

## スナップショットのイメージファイルをスパース

スナップショットのダウンロード

```
root@ctl01:~# openstack image save cdc518f4-1ea4-4fca-adf9-20b57f827c1c --file large-snapshot.img
root@ctl01:~# ll large-snapshot.img 
-rw-r----- 1 root root 20685586432 Aug 26 10:21 large-snapshot.img
```

virt-sparsifyコマンドでスナップショットをスパース

```
$ sudo virt-sparsify large-snapshot.img large-snapshot_resized.img 
[   0.1] Create overlay file in /tmp to protect source disk
[   0.1] Examine source disk
[   2.4] Fill free space in /dev/sda1 with zero
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
[  23.2] Fill free space in /dev/sda15 with zero
[  23.6] Copy to destination and make sparse
[  26.1] Sparsify operation completed with no errors.
virt-sparsify: Before deleting the old disk, carefully check that the 
target disk boots and works correctly.
$ du -h large-snapshot*.img
20G     large-snapshot.img
1.4G    large-snapshot_resized.img
```

大幅に縮小することができた！