それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

qcow2 をリサイズ(縮小)しメンテナンスやバックアップを行いやすくする。

qcow2 をリサイズ(縮小)しメンテナンスやバックアップを行いやすくする。

qcow2 を必要以上にに巨大サイズで確保したので、とっても扱いにくい。qcow2 を小さくしたい。

今回やった手法は、ダイレクトにパーティションを書き換える方法だ。

ダイレクトにパーティションやイメージファイルを「縮小」(shrink)しようとするとLVMで引っかかってすごく大変だったのでメモとして残す。

前提

作業するファイル ubuntu.qcow2

ubuntu.qcow2はEFIパーティションを含むEFIブート用のGPTディスクである。

ubuntu.qcow2 はLVMで構成されている。

LVMは、ubuntu-vg/ubuntu-lv の名前で構成している。

LVのubuntu-lvには ubuntuがインストールされていて、4GBを消費している。LVのサイズは6GBで程度ある。

PVのサイズは8.5GBを確保している。

qcow2 のサイズは10GBである。

まとめると、10GBのディスクイメージに1.5Gのブート領域が確保され、残りの8.5GBでPVが構成され、PV内に6GB程度のLVが構成されている。LVはext4で4GBを消費している。つまり無駄が多い。適当なサイズで適当に作ったディスクイメージのため中途半端な容量になっていて、コピーやバックアップで無駄な時間を浪費するのが許せない。raspiのSDカード内部で使っているディスクイメージなので、少しでもサイズを減らせば作業時間が稼げる。

お急ぎの人は

LVMの構成があるので、ディスクイメージにをダイレクトに操作するのが、本当に面倒だった。お急ぎの人は、大小ディスクイメージを2つ接続してpvmoveするべき(記事末尾に記載)

実際の作業

## qcow2 を作業用に持ってくる。
pv ubuntu.qcow2 > work.qcow2
## ループ・バックで接続する。
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
## ext4 をリサイズ(縮小)
sudo fsck.ext4 -f  /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv 5G

リサイズ(resize2fs)は時間がかかるので、この状態を保存しておく

## mapper を確認
sudo dmsetup ls 
## lvm を取り外し
sudo dmsetup remove ubuntu--vg-ubuntu--lv
## nbd ループバックを取り外し
sudo qemu-nbd  --disconnect /dev/nbd0 
## ファイルをコピー
pv work.qcow2 > work.resized.qcow2
## 以降の作業でミスった場合はリサイズ後からやり直す
## やり直すばあい
## pv work.resized.qcow2 >  work.qcow2 

## 再接続 / ループ・バックで接続する。
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

LV を操作する

## LVS をリサイズ
sudo lvresize ubuntu-vg/ubuntu-lv  --resizefs  -L 5G

--resizefs は念の為。すでにresize2fsは済ませたので時間的ロスはないので、念の為に書いただけ。

VG/LVの状態を確認する。

## LVの状態
sudo lvs ubuntu-vg/ubuntu-lv
  LV        VG        Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  ubuntu-lv ubuntu-vg -wi-a----- 5.00g
## VGの状態
sudo vgs ubuntu-vg
  VG        #PV #LV #SN Attr   VSize  VFree
  ubuntu-vg   1   1   0 wz--n- <8.50g <3.50g
## PVの状態
sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

PVのサイズを変更する

PVのサイズは、自動的に決まるはずなので、自動的に決まらないようにする必要がある。

pvの状況を確認する PEは4MiBである

sudo pvdisplay /dev/nbd0p3
  --- Physical volume ---
  PV Name               /dev/nbd0p3
  VG Name               ubuntu-vg
  PV Size               <8.50 GiB / not usable 2.00 MiB
  Allocatable           yes
  PE Size               4.00 MiB
  Total PE              2175
  Free PE               895
  Allocated PE          1280
  PV UUID               YDqRlT-iObR-A8qn-Jhd9-QHno-Ivlb-Fi1lte

メタデータの量を確認

sudo pvck /dev/nbd0p3
  Found label on /dev/nbd0p3, sector 1, type=LVM2 001
  Found text metadata area: offset=4096, size=1044480

これらから、次のことがわかる。

PEは2175PEを確保している。メタデータは1044480バイトを確保されている。

PEのサイズとバイト数

PE( Physical Extent ) は4Mib ( 4* 1024 * 1024 ) で定義されている

1PE=4Mib

確保されているPEは 2175PEなので

2175PE
⇔ 2175*4*1024**2 bytes 
⇔ 9122611200 bytes 
⇔ ≒ 8.5Gb 

これを5.4GBにリサイズしようと思うと1383PEが必要になる。

5.4GB
⇔ 5.4*1024**3 bytes
⇔ 1382.4 PE
⇔ ≒1383 PE

ただし、メタデータが含まれるため、メタデータ分だけ容量は減る。metadata の容量はpvck で確認した値である。

sudo pvck /dev/nbd0p3
  Found label on /dev/nbd0p3, sector 1, type=LVM2 001
  Found text metadata area: offset=4096, size=1044480

新しいPVのサイズを計算する。

PVサイズはディスクのパーティションのサイズですから、パーティションのサイズはセクタ単位で計算されるので、セクタ数を調べる。

sudo sgdisk -p /dev/nbd0

出力から、512 bytes=1セクタであるとわかる。

Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): BDA3DC0C-F094-455B-965F-6546444E422C
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 20971486
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        20969471   8.5 GiB     8300

5.4G程度確保しようと思うと次のような計算になった

⇔ 確保したい容量+メタデータ量
⇔ ((5*1024**3+410*1024**2)+1044480) bytes
⇔ 5799669760
⇔ 11327480 sector ( 1 sector = 512 bytes )

この計算に沿ってgdisk で容量を変更する

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo gdisk  /dev/nbd0

パーティション+11327480セクタで作り直し

Command (? for help): p
## 確認
Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
  Number  Start (sector)    End (sector)  Size       Code  Name
  1            2048         1050623   512.0 MiB   EF00
  2         1050624         3147775   1024.0 MiB  8300
  3         3147776        20969471   8.5 GiB     8300
## 削除
Command (? for help): d
Partition number (1-3): 3
## 作り直し
Command (? for help): n
Partition number (3-128, default 3):
First sector (34-20971486, default = 3147776) or {+-}size{KMGTP}:
Last sector (3147776-20971486, default = 20971486) or {+-}size{KMGTP}: +11327480
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
## 確認
Command (? for help): p

  Number  Start (sector)    End (sector)  Size       Code  Name
  1         2048          1050623   512.0 MiB   EF00
  2         1050624       3147775   1024.0 MiB  8300
  3         3147776      14475255   5.4 GiB     8300  Linux filesystem
## 書き込み
Command (? for help): w

pvの設定サイズより小さくなったので、PEを読めなくてエラーになる。

sudo pvs /dev/nbd0p3
  WARNING: Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors. Was device resized?
  WARNING: One or more devices used as PVs in VG ubuntu-vg have changed sizes.
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

LVM の構造はキャッシュされるので一旦削除

ubuntu-vg という名前で /etc/lvm/{backup,archive} されているので削除する。

## 切断する
sudo qemu-nbd  --disconnect /dev/nbd0
## LVMの既知レイアウトのキャッシュ削除
sudo grep ubuntu-vg /etc/lvm -rl | xargs sudo rm
### 再接続する
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

これでもまだエラーになる場合

sudo pvs  /dev/nbd0p3
  WARNING: Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors. Was device resized?
  WARNING: One or more devices used as PVs in VG ubuntu-vg have changed sizes.
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

PV のサイズを強制的に変更する

エラーメッセージから現在のPVサイズが分かる

Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors.

現在のPVは11327480 sectors であることがわかる。

セクタをバイト数に変換する。

11327480 sectrors 
⇔ 11327480*512 bytes
⇔ 5799669760 bytes

バイト数で PVの設定を作り直し

sudo pvresize --setphysicalvolumesize=5799669760b  /dev/nbd0p3

作り直したら読み込みし直す

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

pv を確認する。

sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <5.40g 408.00m

細かい計算ミスをresize max で補う

LVとext4 は目標とするpvサイズより少量で確保したので、PVサイズに合わせてリサイズしておく。 現在のLVとPV

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0    10G  0 disk
├─nbd0p1                   43:1    0   512M  0 part
├─nbd0p2                   43:2    0     1G  0 part
└─nbd0p3                   43:3    0   5.4G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0     5G  0 lvm

最初に若干小さめにLVをリサイズしておいたので、PVよりLVが小さくなっており、500MB弱余っている。(512bytes=1sector 単位かつ、4Mib=1PEで整数倍のバイト数に揃える必要があり、計算が面倒になった。メタデータのこともあるので最初にLVを少なめにリサイズした。)

sudo lvresize -l +100%FREE ubuntu-vg/ubuntu-lv
sudo fsck.ext4 -f  /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv

計算ミスを追いかけるのも面倒だし、メタデータのサイズも面倒なので、LV/ext4を小さくとって、PVを正しく設定した、PVはちゃんとLVを包含できたのを確認して、PVのサイズのMAXまでresize max して、無事に完了

PVチェック

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0   10G  0 disk
├─nbd0p1                   43:1    0  512M  0 part
├─nbd0p2                   43:2    0    1G  0 part
└─nbd0p3                   43:3    0  5.4G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0  5.4G  0 lvm

これで、PVは5.4Gに切りそろえることができた。

この状態を保存しておく

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
pv work.qcow2 > work.PVresized.qcow2
## 戻すときは
# pv work.PVresized.qcow2 > work.qcow2 

qcow2 を縮小する

新しいディスクに必要なサイズを計算する

sgdisk -p /dev/nbd0

一番うしろのパーティションのセクタ位置を確認する

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        14475255   5.4 GiB     8300  Linux filesystem

最終セクタまでのバイト数を見る

14475255 セクタ
⇔ 14475255 * 512 バイト
⇔ 7411330560.0 bytes

つまり、7411330560バイトのサイズにqcow2をリサイズすればいいとわかる。 ただ、バックアップパーティションテーブルも必要なので少し余裕をもたせておく。 7411330560は6.9GBなので、7GB(=7516192768b)確保することにする。

qemu-img resize --shrink work.qcow2  7516192768b
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
sudo sgdisk -p /dev/nbd0
Warning! Disk size is smaller than the main header indicates! Loading
secondary header from the last sector of the disk! You should use 'v' to
verify disk integrity, and perhaps options on the experts' menu to repair
the disk.
Caution: invalid backup GPT header, but valid main header; regenerating
backup header from main header.

Warning! One or more CRCs don't match. You should repair the disk!
Main header: OK
Backup header: ERROR
Main partition table: OK
Backup partition table: ERROR

****************************************************************************
Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk
verification and recovery are STRONGLY recommended.
****************************************************************************
Disk /dev/nbd0: 14680064 sectors, 7.0 GiB

パーティションテーブルを書き換える

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/nbd0.
The operation has completed successfully.

書き換えたパーティションテーブルの値に沿ってPVを最終調整

sudo sgdisk -p /dev/nbd0
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        14680030   5.5 GiB     8300  Linux filesystem

パーティションのサイズからバイト数を計算

14680030-3147776
⇔ 11532254 セクタ
⇔ 11532254 *  512 バイト
⇔ 5904514048 バイト
### リサイズ
sudo pvresize --setphysicalvolumesize=5904514048b  /dev/nbd0p3
## 再接続
sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
## リサイズの結果を確認
sudo pvs

リサイズの結果、qemu-img resize で 100Mの余分が出ていた

  PV                       VG        Fmt  Attr PSize    PFree
  /dev/mapper/crypted-root vg.main   lvm2 a--  <111.27g <46.27g
  /dev/nbd0p3              ubuntu-vg lvm2 a--    <5.50g 100.00m

resize max して反映する

sudo lvresize -l +100%free ubuntu-vg/ubuntu-lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv

最終チェック

ブロックデバイスの状況を見る。

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0    7G  0 disk
├─nbd0p1                   43:1    0  512M  0 part
├─nbd0p2                   43:2    0    1G  0 part
└─nbd0p3                   43:3    0  5.5G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0  5.5G  0 lvm

LVMのPV / VG / LV を確認する。

$ sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <5.50g    0
$ sudo vgs ubuntu-vg
  VG        #PV #LV #SN Attr   VSize  VFree
  ubuntu-vg   1   1   0 wz--n- <5.50g    0
$ sudo lvs ubuntu-vg
  LV        VG        Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  ubuntu-lv ubuntu-vg -wi-a----- <5.50g

マウントしてみる。

$ sudo mount /dev/mapper/ubuntu--vg-ubuntu--lv /mnt
$ df -h /mnt
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  5.4G  4.0G  1.1G  79% /mnt
$ sudo umount /mnt

サイズもマウントも、OKだった。

やってみて

正確に計算すると、ちゃんと確実に終わるんだろうけど、PEサイズやセクタサイズなど気にするところがあって、正確な計算やりきりるのは面倒だった。やりながら考えていると、計算が合わなくなったり、メタデータサイズに気づかなかったりパーティションのサイズとかでミスが頻発して心が折れそうになった。

なれないことは、やるもんじゃない。

別解

pvmove という便利なコマンドがある。

2つのディスクにまたがったシンプルボリュームをLVMで構成することができる。

2つのディスクを接続し、VGを構成しておけば、あとは pvmove で古いディスクを取り外すことができる。

これをつかえば、バイトやセクタのサイズを気にすることなく、LVを別ディスクに移動することができる。PVのディスクの縮小ではpvmoveを使うべき。

pvcreate /dev/nbd1p3 #新しいディスクをPVへ追加
pvmove /dev/nbd1p3 # 新しいディスクへデータを移動
vgreduce vg-name /dev/nbd0p3 # 古いディスクを除去
pvremove /dev/nbd0p3 # 古いディスクを除去
resize2fs /dev/mapper/vg-name-lvname 

他に言えること。バックアップや移動がめんどくさいと思うようになったら、qcow2+kvm を諦めて、lxcに変えるべきなのかもしれない。lvmが面倒の原因なのでbtrfs にしてしまうのもありかもしれない。