CentOS7系 xfs ファイルシステム 40G の qcow2 イメージをノンスパース 20G に縮小する

CentOS7.5 の iso から、OS を minimal インストールしました。
どこにインストールされたかを確かめます。

```console
# virsh dumpxml centos7.5
...(snip)...
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/home/xxx/libvirt/centos7.5.qcow2'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
...(snip)...
```

以下のようになっています。

```console
/home/xxx/libvirt/centos7.5.qcow2
```

実は、容量の関係で、libvirt のパスを変更していました。
[libvirt の image 保存ディレクトリの変更](https://qiita.com/intrajp/items/13e8993a7d4bd58ed59f)
では、このパスを指定して、情報を見てみます。

```console
# qemu-img info /home/xxx/libvirt/centos7.5.qcow2
image: /home/xxx/libvirt/centos7.5.qcow2
file format: qcow2
virtual size: 40G (42949672960 bytes)
disk size: 40G
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
```

ここで、容量に注目します。以下のようになっています。

```console
virtual size: 40G
disk size: 40G
```

ls -ls コマンドでも確認します。

```console
# ls -ls /home/xxx/libvirt/centos7.5.qcow2
41949724 -rw-------. 1 root root 42956488704 2月 9 07:27 /home/xxx/libvirt/centos7.5.qcow2
# ls -lsh /home/xxx/libvirt/centos7.5.qcow2
41G -rw-------. 1 root root 41G 2月 9 07:27 /home/xxx/libvirt/centos7.5.qcow2
```

しかし、仮想マシンがこんなに容量はいらないと思います。
仮想マシンの中に入って確認したいです。
guestfishコマンドを使います。この際、仮想マシンは停止しておきます。

```console
# guestfish -a /home/xxx/libvirt/centos7.5.qcow2
# guestfish -a /home/xxx/libvirt/centos7.5.qcow2
Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.
Type: ‘help’ for help on commands
‘man’ to read the manual
‘quit’ to quit the shell
><fs> run
><fs> mount /dev/sda1 /
><fs> df-h
Filesystem Size Used Avail Use% Mounted on
/dev/root 4.0G 397M 3.4G 11% /
tmpfs 147M 104K 146M 1% /run
/dev 362M 0 362M 0% /dev
shmfs 366M 0 366M 0% /dev/shm
/dev/sda1 1014M 143M 872M 15% /sysroot
````

せいぜい、現在の容量は、多く見積もっても、1G というところでしょうか。
以下のページを参考にします。

http://libguestfs.org/guestfish.1.html

```console
# guestfish
Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.
Type: ‘help’ for help on commands
‘man’ to read the manual
‘quit’ to quit the shell
><fs> add-ro /home/fujiwara/libvirt/centos7.5.qcow2
><fs> run
><fs> list-filesystems
/dev/sda1: xfs
/dev/centos/root: xfs
/dev/centos/swap: swap
><fs> mount /dev/centos/root /
><fs> cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Fri Feb 8 22:33:46 2019
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=1c7fbd60-2126-4ca4-81bd-0d530c3b858a /boot xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
><fs> df-h
Filesystem Size Used Avail Use% Mounted on
/dev/root 4.0G 397M 3.4G 11% /
tmpfs 147M 104K 146M 1% /run
/dev 362M 0 362M 0% /dev
shmfs 366M 0 366M 0% /dev/shm
/dev/mapper/centos-root 36G 981M 35G 3% /sysroot
大体わかったので、exit します。
><fs> exit
#
```

今、virtual size と device size が等しいので、一度 sparse 化（thin-provisioning）しておきたいです。
virt-sparsify コマンドが使えそうです。
以下のサイトを参考にします。

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_deployment_and_administration_guide/virt-sparsify

まずは、仮想マシンのイメージ容量を把握します。

```console
$ ls -lh /home/xxx/libvirt/centos7.5.qcow2
-rw-------. 1 qemu qemu 41G 2月 9 08:04 /home/xxx/libvirt/centos7.5.qcow2
$ du -sh /home/xxx/libvirt/centos7.5.qcow2
41G /home/xxx/libvirt/centos7.5.qcow2
``

では、virt-sparsify コマンドで、コピーを作らずにその仮想マシン自体をスパース化します。

```console
# virt-sparsify --in-place /home/xxx/libvirt/centos7.5.qcow2
[ 2.1] Trimming /dev/centos/root
[ 3.5] Clearing Linux swap on /dev/centos/swap
[ 3.8] Trimming /dev/sda1
[ 4.1] Discard space in volgroup centos
[ 4.5] Sparsify in-place operation completed with no errors
```

では、再度仮想マシンの容量を確認します。

```console
$ ls -lh /home/xxx/libvirt/centos7.5.qcow2
-rw-------. 1 root root 41G 2月 9 09:05 /home/xxx/libvirt/centos7.5.qcow2
$ du -sh /home/xxx/libvirt/centos7.5.qcow2
1.1G /home/xxx/libvirt/centos7.5.qcow2
```

1.1G に thin-provision されたようです。
qemu-img info コマンドでも確認してみます。

```console
# qemu-img info /home/xxx/libvirt/centos7.5.qcow2
image: /home/xxx/libvirt/centos7.5.qcow2
file format: qcow2
virtual size: 40G (42949672960 bytes)
disk size: 1.1G
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
refcount bits: 16
corrupt: false
```

さて、ここから、仮想イメージをコピーして、例えば、20G の容量のイメージを作りたい、その際、スパース化しない、ということをやりたいです。
これを参考にします。

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_deployment_and_administration_guide/sect-Guest_virtual_machine_disk_access_with_offline_tools-virt_resize_resizing_guest_virtual_machines_offline

しかしながら、上記リンクには、expand する方法しか書いてありません。
次のリンクはどうでしょう。
https://blog.maideveloper.com/how-to-shrink-qcow2-file-lvm-volume-xfs-system-type/
https://www.endpoint.com/blog/2015/01/29/shrink-xfs-partition-almost-possible

少なくとも言えることは、CentOS7 のデフォルトのファイルシステムである xfs は、縮小できない、ということです。
従って、小さな領域を作成して、データを移行する必要があります。
うーん、確実にやるには、仮想マシン上で、新しいブロックデバイスを追加して、ファイルシステム作成、ボリューム作成、データコピー、
とかがいいのかもしれない。
以下のリンクを参考にします。
https://blog.dbi-services.com/how-to-reduce-the-size-a-lvm-partition-formatted-with-xfs-filesystem-on-centos7/
まず、最初のところは、別のリンクを参考にします。
https://www.cyberciti.biz/faq/how-to-add-disk-image-to-kvm-virtual-machine-with-virsh-command/
まずは、仮想マシン上のデバイス名を調べます。
以下の通りにやります。
https://blog.dbi-services.com/how-to-reduce-the-size-a-lvm-partition-formatted-with-xfs-filesystem-on-centos7/

```console
# fdisk -l | grep 'Disk /dev/vd[a-z]'
Disk /dev/vda: 42.9GB, 42949672960 bytes, 83886000 sectors
```

ということは、新たに接続するデバイス名は、vdb とします。
パーティションも知りたいです。

```console
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sr0 11:0 1 1024M 0 rom
vda 252:0 0 40G 0 disk
|-vda1 252:1 0 1G 0 part /boot
|-vda2 252:2 0 39G 0 part
|-centos-root 253:0 0 35.1G 0 lvm /
|-centos-swap 253:1 0 3.9G 0 lvm [SWAP]
```

これと同じ構成で、容量を小さくすれば良さそうです。
現在の使用状況を確認します。

```console
# df -h
/dev/mapper/centos-root 36G 982M 35G 3% /
/dev/vda1 1014M 142M 873M 15% /boot
```

構成を考えます。
上記と同様の構成で、vdb1 に /boot を 1G、vdb2 に centos-root 15G、centos-swap 3G を作ります。
もうちょっと確認します。

```console
# lvdisplay
--- Logical volume ---
LV Path /dev/centos/swap
LV Size <3.88 GiB
--- Logical volume ---
LV Path /dev/centos/root
LV Size <35.12 GiB
```

以下のコマンドを実行します。
まずは、raw フォーマットで、ディスクを作成します。

```console
$ qemu-img create -f raw xfs_srhink_test-vm-disk1-20G 20G
```

次に、現在の仮想マシン（容量40G）に作成した 20Gのディスクをアタッチします。

```console
# virsh attach-disk centos7.5 --source /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G --target vdb --persistent
```

または、

```console
# virsh attach-disk centos7.5 /home/xxx/QEMU/xfs_srhink_test-vm-disk1-20G vdb --cache none
```

では、Volume を作成したり、初期化したりしましょう。
手順です。

```console
# fdisk /dev/vdb
n p 1 Enter +1G t 83
n p 2 Enter Enter t 2 8e
w
```

カーネルに、パーティション情報の変更を教えます。

```console
# partprobe /dev/vdb
```

確認します。

```console
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sr0 11:0 1 1024M 0 rom
vda 252:0 0 40G 0 disk
|-vda1 252:1 0 1G 0 part /boot
|-vda2 252:2 0 39G 0 part
|-centos-root 253:0 0 35.1G 0 lvm /
|-centos-swap 253:1 0 3.9G 0 lvm [SWAP]
vdb 252:16 0 20G 0 disk
|-vdb1 252:17 0 1G 0 part
|-vdb2 252:18 0 19G 0 part
```

今、仮想マシンにいますが、うまくいったら、/dev/vdb から起動させたいわけです。
とにかく、vdb1 からやっていきましょう。
vdb1 が、/boot となるので、Boot Flag をつけます。

```console
# cfdisk
```

vdb1 を選択し、[Bootable] を選択して Enter、[Write]を選択して Enter、
[Quit]を選択して、Enter
これで、Boot という Flags がつきました。
ファイルシステムを作成します。

```console
# mkfs.xfs /dev/vdb1
```

grub をインストールします。

```console
# grub2-install /dev/vdb
```

では、rsync でデータを移行します。

```console
# rsync -aAxX /dev/vda1 /dev/vdb1
```

次は、/dev/vdb2 です。

```console
# pvcreate /dev/vdb2
（表示）
Physical volume "/dev/vdb2" successfully created.
# pvs
PV VG Fmt Attr PSize PFree
/dev/vda2 centos lvm2 a-- <39.88g 4.00m
/dev/vdb2 lvm2 --- <19.00g <19.00g
# vgcreate centos_ /dev/vdb2
（表示）
Volume group "centos_" successfully created
# vgdisplya centos_
（表示）
--- Volume group ---
VG Name centos_
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 1
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size <19.00 GiB
PE Size 4.00 MiB
Total PE 4863
Alloc PE / Size 0 / 0
Free PE / Size 4863 / <19.00 GiB
VG UUID xxx-xxx-xxx-xxx-xxx-xxx-xxx
# lvcreate -n root -L 15G centos_
（表示）
Logical volume "root" created.
# lvcreate -n swap -L 3G centos_
（表示）
Logical volume "swap" created.
確認します。
# lvdisplay /dev/centos_
（表示省略）
```

ファイルシステムを作成します。

```console
# mkfs.xfs /dev/centos_/root
（表示省略）
# mkswap /dev/centos_/swap
（表示省略）
```

有効にします。

```console
# swapon /dev/centos_/swap
（表示省略）
```

では、xfsdump でデータを移行します。

```console
# yum install xfsdump
```

現在のディレクトリを確認します。

```console
# ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
# xfsdump -l 0 -f /tmp/root.xfsdump /
```

ラベルは、root としました。

```console
# ls -lh /tmp/root.xfsdump
```

容量は、1.2G と表示されました。

```console
# mkdir /mnt/test
# mount /dev/mapper/centos_-root /mnt/test
# xfsrestore -f /tmp/root.xfsdump -L "root" /mnt/test
（表示）
xfsrestore: Restore Status: SUCCESS
# ls /mnt/test
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
```

boot はいらないので、消します（疑わしい）。

```console
# rm -rf /mnt/test/boot
```

一度再起動します。

```console
# shutdown -r now
```
/
dev/mapper/centos_-root を/mnt/test にマウントして確認したところ、データが入っていました。
では、もう一度再起動して、起動時に e を押下して、linux16 の行のところの centos を、centos_ にして,起動するかを試してみます。

```console
# cat /proc/mounts
/dev/mapper/centos_-root / xfs rw,seclabelrelatime,attr,inode64,noquota 0 0
```

うまくいきました。

```console
# df -h
```

/dev/mapper/centos_-root の容量が 15G で / にマウントされとなっています。
/dev/vdb1 が /boot にマウントされています。
では、一度仮想マシンを停止します。

```console
# shutdown -h now
```

ホストマシンから、virsh コマンドを使って、今迄利用していたイメージをデタッチします。

```console
# virsh list --all
Id Name State
-------------------------------
- centos7.5 シャットオフ
```

今、知りたいのは、centos7.5 ドメインが、どの qemu イメージをぶら下げているかです。

```console
# virsh dumpxml centos7.5
...(snip)...
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/home/xxx/libvirt/centos7.5.qcow2'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G'/>
<target dev='vdb' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
</disk>
```

ファイル名が、/home/xxx/libvirt/centos7.5.qcow2 でした。
では、このイメージをcentos7.5 からデタッチします。

```console
# virsh detach-disk --domain vmname /var/lib/libvirt/images/vmname-vdb.qcow2 --persistent
```

オンラインで切断する場合は、上記に --config --live を追加するとよいようです。
では、デタッチできるから、もう一度確認します。

```console
# virsh dumpxml centos7.5
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G'/>
<target dev='vdb' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
</disk>
```

起動してみます。
おっと、grubに落ちてしまいました。

```console
grub>ls
(hd0) (hd0,msdos2) (hd0,msdos1)
```

カーネルとかが/boot に入っていなかったのが原因でした。こういう場合は、以下の方法でマウントして、取り出します。そういえば、rsync でコピーした時に、ちゃんと確認していなかったなぁ。xfsdump/xfsrestore の方がいいかも。まあ、なんとかしましょう。

```console
# guestmount ...
（省略）
```

起動の印を付け忘れたら、以下の方法で直せます。
困った時は、以下をみて、イメージを /dev/nbd0 として cfdisk ができます。
[link]https://xliska.wordpress.com/2012/09/13/partitioning-qemukvm-disk-images-using-qcow2-format/
カーネルモジュールをロードします。

```console
# modprobe -av nbd
# qemu-nbd -c /dev/nbd0 <qcow2 イメージ>
# cfdisk /dev/nbd0
```

終わったら、接続を解除します。

```console
# qemu-nbd --disconnect /dev/nbd0
```

カーネルモジュールをアンロードします。

```console
# modprobe -rv nbd
# vgrename /dev/centos_ /dev/centos
```

うまく上がらないときは、grub の画面で e を押下して、linux16 のところやらを書き換えて立ち上げてから直します。
最終的に、新しい15Gのイメージで立ち上がるようになりました。
grub.cfg の書き直しは、以下になります。

```console
# grub2-mkconfig -o /boot/grub2/grub.cfg
```

では、最初の目的である、イメージの縮小はできたのでしょうか。

```console
# ls -lh /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G
-rw-r--r--. 1 root root 20G 2月 10 02:01 /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G
# qemu-img info /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G
image: /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G
file format: raw
virtual size: 20G (21474836480 bytes)
disk size: 1.3G
```

容量がほぼ半分になりました。しかし、これは、raw フォーマットになっていますね。
これを、qcow2 フォーマットに変換したいです。
また、今後の運用のオーバーヘッドを鑑みて、スパース化したくないです。
一度、普通にやってみます。

```console
# qemu-img convert -f raw -O qcow2 /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G.qcow2
# ls -lh /home/xxx/QEMU/
-rw-r--r--. 1 root root 20G 2月 10 02:01 xfs_shrink_test-vm-disk1-20G
-rw-r--r--. 1 root root 1.4G 2月 10 02:07 xfs_shrink_test-vm-disk1-20G.qcow2
```

では、スパースサイズを0にして実行してみます。-S 0

```console
# qemu-img convert -S 0 -f raw -O qcow2 /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G_non_sparse.qcow2
# ls -lh /home/xxx/QEMU
合計 23G
-rw-r--r--. 1 root root 20G 2月 10 02:01 xfs_shrink_test-vm-disk1-20G
-rw-r--r--. 1 root root 1.4G 2月 10 02:07 xfs_shrink_test-vm-disk1-20G.qcow2
-rw-r--r--. 1 root root 21G 2月 10 02:13 xfs_shrink_test-vm-disk1-20G_non_sparse.qcow2
```

qemu-img info コマンドでも確認します。

```console
# qemu-img info /home/xxx/QEMU/xfs_shrink_test-vm-disk1-20G_non_sparse.qcow2
image: /home/fujiwara/QEMU/xfs_shrink_test-vm-disk1-20G_non_sparse.qcow2
file format: qcow2
virtual size: 20G (21474836480 bytes)
disk size: 20G
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
```

できました。
xfsファイルシステム 40Gのqcow2イメージを、ノンスパースの20Gに縮小できたというところです。
xfsファイルシステムは、縮小できないので、データをダンプ/リストアする必要がありました。
/etc/fstab も、うまくいくまでは、移行前のものと移行後の行を両方書いておきました。