О bcache

2015-01-10

Решил я переустановить Убунту. Да. Иногда случается так, что линуксы тоже надо переустанавливать. Причин для этого было у меня две.

Во-первых, пришла пора переходить на 64-битную систему. 32-битная у меня жила и обновлялась (на этом железе) года два. Потому что всякая проприетарная фигня, например для организации митингов, если и существует под Линукс, то только в 32-битном варианте. Ну а теперь оказалось, что и Docker, который пришла пора потыкать, существует только в 64-битном варианте. И Android Emulator в 32-битном варианте объявлен устаревшим.

Ну а последние Убунты, вслед за Дебианом, наконец-то стали multiarch. Каким давным давно являются Редхаты да Центосы. Теперь в системе спокойно могут сосуществовать 64-битные и 32-битные библиотеки и приложения. Значит, можно ставить 64-битную систему и иметь полную поддержку 32-бит. Кстати, это наш Скайп остается 32-битным, и прекрасно работает в 64-битной Убунте (впрочем, чуток подшаманить ради красоты нужно).

Во-вторых, у моего любимого ультрабука очень интересная ситуация с дисками. Есть 24 гига SSD. И есть 500 гигов HDD. В случае Windows все понятно, SSD диск используется как кэш и все хорошо. В случае Убунты два года назад я решил поступить проще: засунуть на SSD систему и свап, а на HDD поместить /home. Такой простейший подход себя не оправдал. Система-то грузится махом. Но вот вход в иксы, да запуск какого-нибудь браузера — не быстры. Совсем не быстры. Ибо нужно читать кучу мелких файлов с HDD.

Самый радикальный способ, конечно, — это выкинуть HDD и воткнуть на его место SSD. Но это сильно дорого получается, чтобы остаться с пятьюстами гигами. К тому же эти 24 гига встроенного SSD все-равно никуда не выкинешь, они на плате распаяны.

В общем, нужен кэш на SSD для кэширования доступа к HDD. В Линуксе для этого есть два с половиной способа (из поддерживаемых Ядром из коробки). Но ни один из них не настраивается из коробки штатными инсталляторами (Убунты). А еще захотелось расстаться с, внезапно, устаревшим MBR. И поиграться с более современным и надежным GPT. Где нет дурацких веселий про четыре основных раздела плюс логические разделы :) И где-то рядом стоит UEFI, который новая майкро молодежная замена BIOS. Но он скорее мешался, чем что-то дал.

Ubuntu

Итак, кэши.

Самый старый — dm-cache. В составе Ядра с версии 3.9, с апреля 2013. Является модулем device mapper, т.е. довольно штатным способом расширения функционала Ядра. В dm-cache пугает сложность настройки: кроме самого диска, который кэшируем, нужно еще два отдельных устройства для самого кэша и метаданных кэша. Причем размер метаданных нужно еще заранее правильно подсчитать.

Для любителей LVM dm-cache впилили в него. Возможно, это самый правильный способ нарисовать нужный мне кэш. Но я не хотел заморачиваться еще и с LVM. Все же это ноутбук, и шанс добавить в систему новый диск с тем, чтобы расширить имеющиеся разделы на него, для чего, в моем понимании, и нужен LVM, стремится к нулю. А если уж менять диск, то от наличия LVM профита нет.

И, наконец, есть bcache. Он вошел в Ядро версии 3.10, в июле 2013. Его проще настраивать: нет нужды в отдельном разделе для метаданных. Он точно умеет кэшировать несколько HDD в один SSD, а именно такого мне хотелось: иметь один кэш для двух разделов — корня ФС и /home.

Кто быстрее, dm-cache, bcache или их еще не вошедшие в Ядро аналоги вроде Flashcache и EnhanceIO — непонятно. Каждый тянет одеяло на себя. Но bcache вроде более явно высказывается об особенностях своих алгоритмов и бережном отношении к SSD.

В общем, решил попробовать поднять bcache. И, если заведется, на нем и остановиться.

У bcache есть не очень приятная багофича. Его нельзя поднять поверх существующих разделов. Он требует явного переформатирования разделов с потерей содержащихся на них данных. Он создает свой суперблок. Делается это для того, чтобы нельзя было получить доступ к этим разделам, минуя bcache. Чтобы избежать возможного нарушения целостности кэша. Ничего особо страшного тут нет, ибо bcache уже есть в ядре, а работоспособность он сохраняет и при отсутствии доступа к собственно кэшу, тупо переходит в режим прямой работы с кэшируемым устройством.

Так что пришлось два дня аккуратно упаковывать родной /home в стопочку архивов и складывать на внешний диск. Оказалось, несколько десятков гигабайт бесценного барахла накопилось. Самая сложная часть операции :)

Есть, конечно, способ сконвертировать раздел в bcache. Но не так-то просто оказалось эти скрипты запустить в Убунте 14.04, давно они не обновлялись. К тому же тут нет никаких гарантий, а значит, бэкапы все равно нужны.

А еще GRUB не умеет bcache, поэтому нужно создавать отдельный раздел для /boot. И вообще, установщики ОС тоже не умеют bcache. Поэтому пришлось сделать финт ушами. Сначала поставить Убунту в раздел, который потом станет свапом. Затем в установленной системе настроить bcache и перенести систему (и /home) на bcache разделы. И напоследок, после второй перезагрузки, засунуть свап в тот самый раздел, где он должен быть.

С загрузкой пришлось дополнительно помучаться. С одной стороны, я собрался делать GPT разметку дисков. С другой стороны, у нас имеется два варианта загрузки: классический BIOS и новый UEFI. Тестировал я установку bcache в VirtualBox, где поддержка UEFI экспериментальна и не сработала. Поэтому пришлось грузиться в GPT раздел из BIOS. GRUBу в этом случае нужен дополнительный раздел с флагом bios_grub, куда он помещает свое тельце. (В случае MBR он размещается в первых секторах, которые по стандарту вроде как не заняты, а в GPT такой роскоши нет). Однако ультрабук отказался грузить GPT диск в BIOS режиме. Пришлось грузиться в UEFI. GRUBу тогда нужен fat32(!) раздел для EFI. Получилась кучка вспомогательных разделов, которые я разместил на SSD, но также создал их и на HDD, чтобы в случае извлечения этого HDD из ультрабука, его тоже можно было бы сделать потом загрузочным.

Select boot device

Итак, бэкапы мы сделали. Теперь берем загрузочную флешку с Ubuntu 14.04 LTS 64bit и грузимся с нее в режиме UEFI. Инсталлятор пока не запускаем, а балуемся с GParted. Создаем на каждом диске новую GPT таблицу разделов. Как-то так:

  • sdb, SSD диск
    • sdb1, BIOS boot раздел, 1MB, без файловой системы, поставить флаг bios_grub
    • sdb2, UEFI boot раздел, 200MB, fat32, поставить флаг boot
    • sdb3, /boot, 250MB, ext4
    • sdb4, будет свап, но сначала сюда поставим систему, 6GB, отформатировать в ext4
    • sdb5, кэш bcache, оставшиеся 17GB SSD диска, без файловой системы
  • sda, HDD диск
    • sda1, BIOS boot раздел, 1MB, без файловой системы, резерв на случай изъятия диска
    • sda2, UEFI boot раздел, 200MB, fat32, не форматировать, резерв на случай изъятия диска
    • sda3, резерв для /boot, 250 MB, не форматировать, резерв на случай изъятия диска
    • sda4, /, 20GB, ext4, сейчас оставить без форматирования
    • sda5, /home, оставшиеся 478GB HDD диска, ext4, сейчас оставить без форматирования

Ставим Убунту в тот самый раздел, который потом сделаем свапом.

  • sdb3 — /boot
  • sdb4 — /

Загрузчик поставить в sdb. Проигнорировать предупреждения об отсутствии свапа и наличия файловой системы в /boot.

Перезагружаемся, работаем в установленной системе (которая сейчас занимает один раздел на SSD диске).

Ставим bcache-tools (далее все команды — из-под рута):

# add-apt-repository ppa:g2p/storage
# apt-get update
# apt-get install bcache-tools

Создать bcache не просто, а очень просто:

# make-bcache -C /dev/sdb5 -B /dev/sda4 /dev/sda5

Флагом -C указывается устройство-кэш. Флагом -B указываются кэшируемые (backed) устройства. Если все сделать одной командой, то сразу получится то, что нам нужно: два раздела на HDD и один кэш для них на SSD.

Если на этих разделах имеется какая-то файловая система (ну например, вы все же отформатировали что-то лишнее при разметке диска), make-bcache не будет её удалять. Придется почистить как-то так: wipefs -a /dev/sda4. А потом связывать устройства-кэши с кэшируемыми устройствами, как описано в документации. Главное тут понять, что на каждое кэшируемое устройство (раздел HDD), появляется свое устройство /dev/bcacheX. И, так называемый CSET-UUID кэша нужно приаттачить к каждому из них. Как-то так:

# ls /sys/fs/bcache/
4ed92e55-683a-49b2-8043-39d5bde433f9  register  register_quiet
# echo 4ed92e55-683a-49b2-8043-39d5bde433f9 > /sys/block/bcache0/bcache/attach
# echo 4ed92e55-683a-49b2-8043-39d5bde433f9 > /sys/block/bcache1/bcache/attach

Создаем файловые системы:

# mkfs.ext4 /dev/bcache0
# mkfs.ext4 /dev/bcache1

Копируем /home и / на новые места на bcache устройствах. Тут главное не запутаться с номерами этих устройств. Порядок нумерации может быть произвольным (но постоянным). Смотрите на размер устройств, или же смотрите, например, ls /sys/block/bcache0/slaves.

# mkdir OLD NEW HOME
# mount /dev/sdb4 OLD
# mount /dev/bcache0 NEW      #должен быть новый кэшированный рут
# rsync -a OLD/ NEW/
# mount /dev/bcache1 HOME
# rsync -a /home/ HOME/

Готовимся обновлять GRUB:

# mount /dev/sdb3 NEW/boot
# mount /dev/sdb2 NEW/boot/efi
# mount -o bind /dev NEW/dev
# mount -t proc none NEW/proc
# mount -t sysfs none NEW/sys
# chroot NEW

Нужно найти UUIDы наших bcache устройств, а также UUID старого корня (который станет свапом):

# ls -l /dev/disk/by-uuid/ | grep bcache
lrwxrwxrwx 1 root root 13 Jan  5 21:48 6538fd23-8f53-4651-adc7-43c494e75f67 -> ../../bcache1
lrwxrwxrwx 1 root root 13 Jan  5 21:48 dfc55722-a89c-4026-b1d2-758ec336017a -> ../../bcache0
# ls -l /dev/disk/by-uuid/ | grep sdb4
lrwxrwxrwx 1 root root 10 Jan  5 21:48 b0b3a280-329e-42a9-91fd-09fde7e8977f -> ../../sdb4

Любимым текстовым редактором правим /etc/fstab:

  • меняем UUID корневого раздела, со старого sdb4 на новый bcache0 (вот для чего мы их находили)
  • добавляем запись для /home

Получается что-то вроде:

UUID=dfc55722-a89c-4026-b1d2-758ec336017a /               ext4    errors=remount-ro 0       1
UUID=6538fd23-8f53-4651-adc7-43c494e75f67 /home           ext4    defaults          0       1

Любимым текстовым редактором правим /boot/grub/grub.cfg. Нужно тщательно и аккуратно заменить вхождения UUID старого корня (на sdb4) на UUID нового корня (на bcache0).

Ставим GRUB:

# grub-install /dev/sdb

Если все ок, то все ок. Перезагружаемся. Превращаем старый рут в свап:

# mkswap /dev/sdb4
# swapon /dev/sdb4

И добавляем свап в /etc/fstab:

UUID=b0b3a280-329e-42a9-91fd-09fde7e8977f none            swap    defaults        0       0

Ну и все. Можно восстанавливать бэкапы взад.

Проследить за работой bcache можно через sysfs:

% cat /sys/block/bcache*/bcache/state
clean
clean
% ls /sys/block/bcache0/bcache/stats* -d -1
/sys/block/bcache0/bcache/stats_day
/sys/block/bcache0/bcache/stats_five_minute
/sys/block/bcache0/bcache/stats_hour
/sys/block/bcache0/bcache/stats_total
% ls /sys/block/bcache0/bcache/stats_day/ -1
bypassed
cache_bypass_hits
cache_bypass_misses
cache_hit_ratio
cache_hits
cache_miss_collisions
cache_misses
cache_readaheads
% cat /sys/block/bcache*/bcache/stats_day/cache_hit_ratio
78
68

Если, кроме оптимизации чтения, хочется покэшировать и запись, можно поиграть с режимами:

% cat /sys/block/bcache0/bcache/cache_mode
[writethrough] writeback writearound none

Итак, раньше на SSD был корень, а /home был на HDD. Теперь и то и другое — на HDD, но кэшировано через SSD. Причем кэшируются только операции чтения, а запись идет сразу и в кэш и на жесткий (writethrough). По субъективным ощущениям система грузится с прежней скоростью, а вот иксы, браузер, Идея стартуют быстрее. Пакеты ставятся медленнее (потому что запись в корень стала медленнее). Прошла неделя, полет нормальный.

HDD+SSD

P.S. А еще есть btier.