В данной статье я расскажу про ситуацию, когда нужно уменьшить qcow2 образ виртуальной машины KVM в максимально быстрые сроки.
Формат блочных устройств qcow2
Но, тем не менее, эту схему можно проделать и с raw образами, достаточно сконвертировать его в qcow2. В общем-то, инструкция будет актуальна для всего, что конвертируется в qcow2.
Простой способ уменьшения диска на KVM заключается в следующих этапах:
- Сжатие блочного устройства qcow2 (диска VM)
- Создание диска меньшего размера
- Подключение gparted образа, старого и нового диска
- Подготовка к загрузке ОС
- Характеристики VM и хост машины:
Хост: CentOS 7 + QEMU 2.12 + LIBVIRT 4.5.0 + Kernel UEK5 v. 4.14
VM: CentOS 7 + 80GB HDD + Kernel std v. 3.10
В качестве донора будет выступать виртуальная машина CentOS 7 с образом жесткого диска размером 80GB, фактически занимаемыми 20GB и физически 80GB. Уменьшать будем до 40GB.
В чем разница между размером образа, фактическим и физическим объемом?
Допустим, qcow2 image создан размером 80гб и мы об этом знаем. В процессе эксплуатации образ забивался данными, какие-то данные удалялись. Если в общих словах в связи с особенностями процесса записи-удаления данных, для ОС удаленные данные как бы не существуют, а в образе остаются записанными, пока не будут перезаписаны другими данными. Соответственно хоть в ОС Вы будете видеть 20GB фактически занимаемых данных, то хост KVM покажет такую замечательную картину (используем утилиту qemu-img для получения информации):
qemu-img info qcow2_image.img
image: qcow2_image.img
file format: qcow2
virtual size: 80G (85899345920 bytes)
disk size: 80G
cluster_size: 65536
Format specific information:
compat: 0.10
refcount bits: 16
Можно заметить что virtual size = disk size, а так же du -sh образа покажет что он занимает реальные 80G:
du -sh qcow2_image.img
80G qcow2_image.img
И так как нам нужно уменьшить размер образа до 40GB, приступаем к процессу.
Этап 1 Сжатие блочного устройства (образа)
Перед началом процедуры уменьшения диска надо убедиться, что занимаемое пространство внутри ОС меньше объема, до которого диск будет так или иначе уменьшен.
df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda2 80G 18G 63G 22% /
Как мы видим, занято 18GB, что меньше 40GB. Выключаем VM командой
shutdown -h now
И переходим на хост машину, проверяем:
- сколько занимает образ физически
- сколько фактически
# qemu-img info qcow_shrink
image: qcow_shrink
file format: qcow2
virtual size: 80G (85899345920 bytes)
disk size: 80G
cluster_size: 65536
Format specific information:
compat: 0.10
refcount bits: 16
# virt-df -h qcow_shrink
du -sh Filesystem Size Used Available Use%
qcow_shrink:/dev/sda1 488M 101M 351M 21%
qcow_shrink:/dev/sda2 79G 17G 62G 22%
# du -sh qcow_shrink
80G qcow_shrink
Для сжатия образа нам понадобится простая утилита virt-sparsify. Убедимся что VM не работает и выполним команду в директории вместе с образом диска (Важное замечание: перед началом virt-sparsify, убедитесь, что в /tmp и в хранилище образа достаточно свободного пространства для выполнения операции)
virt-sparsify qcow_shrink qcow_shrink-new
Результатом успешного завершения операции будет следующий вывод:
[ 0.0] Create overlay file in /tmp to protect source disk
[ 0.1] Examine source disk
[ 1.2] Fill free space in /dev/sda1 with zero
[ 1.5] Fill free space in /dev/sda2 with zero
100% 00:00
[ 72.5] Copy to destination and make sparse
[ 81.9] Sparsify operation completed with no errors.
virt-sparsify: Before deleting the old disk, carefully check that
the
target disk boots and works correctly.
Затем делаем подмену диска (перемещаем qcow_shrink куда-нибудь в сторону, например qcow_shrink-old, а qcow_shrink-new на его место qcow_shrink)
mv qcow_shrink qcow_shrink-old && mv qcow_shrink-new
qcow_shrink
Запускаем VM. Если всё запустилось, гасим VM и продолжаем работы.
Этап 2 Создание диска меньшего размера
Простая процедура включающая в себя всего одну команду:
qemu-img create -f qcow2 -o preallocation=metadata
qcow_shrinked 40G
qcow_shrinked имя нового образа
40G новый размер
Этап 3 подключение gparted
Так как иногда админы предпочитают более легкие пути решения вопроса, бубен откладывается в сторону (kpartx) и на его место приходит ISO и VNC. К счастью, в KVM подключить его не очень сложно.
Что мы делаем:
- Подключаем образ ISO GParted
- Подключаем qcow2_shrinked к VM
- Запускаем VM, загружаемся с ISO
Как это делать, я опущу из данной статьи, так как подразумевается что выполняющий всё это уже знает, как это происходит, но результатом будет следующее:
Запускаем VM и видим загрузочный экран GParted:
Выбираем первый пункт и следуем инструкциям на экране. Я обычно нажимаю enter до конца.
Увидев сам GParted, приступаем к действу. Быстренько проверяем какая у /dev/vda таблица разделов msdos или gpt. Это важно:
Переключаемся на второй диск /dev/vdb и создадим таблицу разделов:
При создании таблицы выбираем тип msdos как мы узнали ранее.
Затем переключаемся обратно на /dev/vda и последовательно, с первых дисков начинаем копировать разделы переключаясь между vda и vdb:
Конечным результатом будет:
Нажимаем Apply и ждем завершения результата:
В результате:
Что уже похоже на правду. Но так как мы сделали некоторые манипуляции, которые приведут к изменению UUID дисков, мы потенциально не загрузимся в ОС. Почему? CentOS 7 использует в fstab UUID дисков, Grub2 использует UUID дисков, поэтому прыгаем в консоль и занимаемся черной магией.
Gparted работает изначально под пользователем, поэтому прыгаем под root командой sudo su root:
Сделаем blkid чтобы убедится, что UUID разделов поменялся
Видно, что UUID vda1 = vdb1, а у vdb2 он поменялся. Ничего страшного жить с этим можно.
Монтируем vdb полностью, вместе с /boot разделом, а также смонтируем некоторые разделы для нашего удобства.
mkdir vdb2
mount /dev/vdb2 vdb2
mount /dev/vdb1 vdb2/boot
cd vdb2
mount --bind /dev dev
mount --bind /sys sys
mount --bind /proc proc
chroot .
Начнем с fstab так как в VNC набирать UUID не очень удобно, заменим его на привычное название устройства.
Заменяем строку с UUID= на, внимание:
указываем /dev/vdb2, если старый диск не планируется отключать
указываем /dev/vda2, если старый диск будет отключен
Так как мы старый диск отключаем перед загрузкой ОС, то пишем /dev/vda2
Далее изменим загрузчик, приведем его в порядок. Предположим, что всё лежит штатно в /boot/grub2, grub.cfg там же, а efi нет (msdos table, какой efi :) ):
grub2-install /dev/vdb
cd /boot/grub2
grub2-mkconfig -o grub.cfg
На этом можно порадоваться за себя и отключив gparted, загрузиться в ОС.
Этап 4 загрузка ОС
Перед загрузкой ОС я всё же рекомендую отключить старый диск от сервера. Поэтому на предыдущем этапе в fstab нужно было прописать vda2, но если Вы внимательный пользователь ПК и ничего не отключали, то проблем возникнуть не должно. Со старым диском велика вероятность загрузиться именно с него.
В процессе загрузки никаких проблем не возникло, сервер загрузился как положено. Проверим это:
[root@shrink ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 484M 0 484M 0% /dev
tmpfs 496M 0 496M 0% /dev/shm
tmpfs 496M 6.7M 489M 2% /run
tmpfs 496M 0 496M 0% /sys/fs/cgroup
/dev/vdb2 40G 18G 23G 44% /
/dev/vdb1 488M 101M 352M 23% /boot
tmpfs 100M 0 100M 0% /run/user/0
[root@shrink ~]# blkid
/dev/vda1: UUID="ea505196-32fb-4df6-8bed-0a0ab2d0b726"
TYPE="ext4"
/dev/vda2: UUID="30ec1bc6-658f-4611-8708-5e3b7ebaa467"
TYPE="xfs"
/dev/vdb1: UUID="ea505196-32fb-4df6-8bed-0a0ab2d0b726"
TYPE="ext4"
/dev/vdb2: UUID="c8548834-272b-4331-a9bf-aa99fb41a434"
TYPE="xfs"
/dev/sr0: UUID="2019-03-21-13-42-32-00" LABEL="GParted-live"
TYPE="iso9660" PTTYPE="dos"
Мы видим, что /boot и / нужные, размер 40GB, ОС работает. Счастье, не иначе!
Бонус
То, с чем придется столкнуться в некоторых ситуациях.
- Если VM на Windows, то вместо virt-sparsify можно использовать сжатие тома внутри ОС. Технически весь процесс одинаковый, однако известно, что метки тома меняются (мы это видели ранее в blkid), а значит Windows надо знать, что есть что в новой реальности. Для этого загружаемся в режим восстановления (после всех операций) и делаем fixmbr + rebuildbcd. Что именно и как да поможет вам man
- Великая проблема с xfs напороться на Superblock has unknown read-only compatible features (0x4) enabled. После загрузки ОС она будет работать в режиме read-only, а всё будет смонтировано как надо. Решение до безумия простое:
Скорее всего, когда всё делалось в gparted или ином окружении, версия ядра этого окружения была слишком новой, в котором xfs немного изменен, а именно отличаются метаданные и их версия. В результате xfs сделанный в новом ядре, превращается в тыкву на старом. Что делаем загружаемся обратно в rescue gparted, поднимаем в этом rescue окружении сеть и ставим максимально свежее ядро в ОС. Я ставил 5.х на CentOS 7, возможно подойдет и 4.х, не проверял, но в итоге всё работало. Причем, без особых проблем.
На этом всё!
Как видите, ничего сложного в этом нет. Конечно, можно использовать LVM, resize2fs и прочие штуки, но всё-таки qcow2 где-то ещё используется и кому-то даже нужен.
Если вы знаете способ ещё более простой напишите о нём в комментариях.