- PVSM.RU - https://www.pvsm.ru -

Меня зовут Юрий, я руководитель группы системного администрирования в Ситимобил. Сегодня поделюсь опытом работы с технологией тонкого резервирования (thin provisioning) файловых систем Linux и расскажу, как ее можно применять в технологических CI/CD-процессах компании. Мы разберем ситуацию, когда для автоматического тестирования кода при доставке его в production нам как можно быстрее необходимы копии БД MySQL, максимально приближенные к «боевой» версии, доступные на чтение и на запись.
Логичный вопрос, ведь есть отработанные механизмы миграций схем БД в тестовые среды. Зачем вообще доводить основную нешардированную СУБД до таких объемов? Да и для тестирования нужны не все данные. Постараюсь объяснить.
Примерно год назад на фоне активного роста нашего агрегатора такси (за 2018 год выросли по завершенным поездкам примерно в 15 раз), выросли объемы данных, нагрузка на серверы, частота выкаток. Мы оказались в следующей ситуации:
Последнюю проблему хотелось решить максимально быстро. Были уже написаны тесты Postman для проверки основного PHP-монолита, но не хватало актуальной базы данных. При этом мы не могли создавать ночью реплику, делать ее мастером и отдавать на растерзание днем: очень большое количество выкаток и изменений, в том числе в данных и схеме БД, сделали бы стенд неработоспособным уже к середине дня. Да и ограничивать выкатки всего лишь рабочим днем было бы неэффективно.
Тем не менее, задача была выполнена: первый рабочий стенд мы получили уже через две недели. За прошедший год он претерпел много изменений и продолжает использоваться.
Далее я подробно опишу все шаги и этапы развития нашего решения. Вы убедитесь, что этот метод заслуживает право на существование.
Что такое «тонкое резервирование»?
Это аппаратная или программная технологий (другое название — sparse volumes), позволяющая выделять большее количество требуемого ресурса, чем есть в наличии. При этом выделяемый объем должен соответствовать критериям just-enough (столько, сколько нужно) и just-in-time (за необходимое время). В основном тонкое резервирование применяется в различных СХД, чтобы предоставлять дисковое пространство в необходимых объемах, превышающих фактически доступные. Технология поддерживается различными файловыми системами, например, LVM2, ZFS, BTRFS. Она широко используется в гипервизорах виртуализации. Нам тонкое резервирование позволяло быстро создать из снапшотов основного раздела с данными столько копий этого раздела, сколько нам нужно (data-директория СУБД MySQL).
Это главу можно ещё назвать «Как сделать максимально быстрые снапшоты больших объемов данных с помощью Thin LVM [1], уменьшив стабильность файловой системы и СУБД MySQL до неприличных показателей».
Поскольку мы уже использовали LVM для построения основных разделов ОС, то решили начать именно с нее. Для начала нам потребовалась отдельная физическая машина — реплика нашей основной базы MySQL, на которой мы могли бы по запросу создавать снапшот реплики и поднимать его рядом отдельным экземпляром MySQL. На время тестирования мы разрешили применять на этом экземпляре изменяющие операции, и по завершении тестов благополучно его удаляли. Конфигурация сервера была такой:
На тему выбора между RAID-контроллером и программным RAID MD можно писать отдельную статью. Скажу лишь, что наш выбор повлияли два фактора:
Мы разделили получившийся RAID-10: сделали единый Volume Group (VG) на весь объем (с накладными расходами примерно 6,7 Гб) и создали логический раздел (Logical Volume, LV) под систему на 50 Гб. В обычной ситуации все остальное место мы определяем под раздел c MySQL. Но нам нужно было тонкое резервирование, поэтому сначала мы создали так называемый pool, внутри которого создали раздел под /var/lib/mysql на 3,5 Тб (исходя из предполагаемых объемов БД):
lvcreate -l 100%FREE -T vga/thin
lvcreate -V 3.5T -T vga/thin -n mysql
Отформатировали раздел в ext4, примонтировали его, записали реплику и получили исходный стенд. Затем сделали обвязку в виде API, который должен создавать снапшоты, поднимать экземпляр БД MySQL на заданном порте и удалять созданный экземпляр. Поскольку при этом используются исключительно системные вызовы, в качестве языка написания скриптов мы выбрали обычный bash, а в качестве провязки API HTTP → bash развернули open source-решение goexpose [2], написанное на Go.
Когда-нибудь мы выложим наши bash-скрипты в open source, а пока я просто опишу основной алгоритм:
Создание основного снапшота snapmain:
Создание БД на произвольном порте из snapmain:
Удаление БД на произвольном порте:
Пример команд для клонирования разделов нового экземпляра БД:
lvcreate -n stage_3307 -s vga/snapmain
lvchange -ay -K vga/stage_3307
mount -o noatime,nodiratime,data=writeback /dev/mapper/vga-stage_3307 /mnt/stage_3307
Теперь расскажу про основную проблему, с которой мы столкнулись при использовании тонкого резервирования. Мы уперлись в производительность SSD-дисков. Произошло это из-за особенностей Thin LVM: она в своей основе оперирует на уровне устройства низкоуровневыми чанками размером по умолчанию в 4 Мб. Как это выглядело:
Как мы с этим боролись, чтобы получить более вменяемый результат (основные моменты):
RAID-контроллер:
Файловая система:
MySQL:
Это далеко не полный список, особенно по MySQL. Впрочем, остальные изменения минорны и зачастую применимы не всегда и не точно. Например, в попытке разгрузить диски мы даже унесли innodb_parallel_doublewrite_path в /dev/shm, что в некоторых случаях при старте некорректно завершенного экземпляра экономило нам до 5 секунд.
Почему мы останавливаем MySQL перед тем, как делать снапшот? Ведь мы можем снять его с работающей реплики. Все верно, вот только новый экземпляр БД на этом снапшоте будет по умолчанию считаться поврежденным и потребует полного сканирования при запуске. Останавливать реплику определенно быстрее, хоть это в итоге и является самой длительной операцией во всем процессе.
В результате мы получили более приемлемые тайминги и готовый к работе стенд. Хотя, как видно по наиболее красноречивому графику отставания репликации основной реплики, ситуация все еще далека от идеала:

Из других недостатков стоит отметить практическую невозможность мониторинга пула Thin LVM: помимо системных стандартных функций iostat, понять, например, какой элемент пула сейчас производит наибольшую нагрузку на файловую систему невозможно.
Отдельно стоит отметить один большой недостаток, связанный с описанной выше оптимизацией: мы получили YOLO-стенд. Примерно раз в один-два месяца ext4 не выдерживала подобных надругательств над собой и безвозвратно ломалась, требуя переформатирования и перезаливки реплики. Выиграв в скорости, мы безнадежно угробили стабильность.
За какими метриками стоит следить в процессе эксплуатации Thin LVM:
Если закончившееся место под данные наш стенд переживет (достаточно почистить диски), то закончившееся место под метаданные приведет к полному краху пула и необходимости его пересоздания с нуля.
Файловая система внутри пула со временем очень сильно фрагментируется. Рекомендую раз в сутки запускать по крону команду fstrim -v /var/lib/mysql.
Промежуточные итоги:
Когда-то давно я имел дело с файловой системой ZFS, но тогда достоверно хорошо ZFS работала на своем родном семействе ОС Solaris. Существовала портированная на FreeBSD версия с достаточно хорошим уровнем реализации. Был также недоделанный порт на Linux, который мало кто применял. Из-за структуры хранения данных B-tree (кстати, такая же структура хранения и у InnoDB MySQL) ZFS плохо себя показывала на инсталляциях с очень большим количеством файлов. Всё это в совокупности с необходимостью учить матчасть перед использованием надолго вычеркнуло из моей практики эту файловую систему. Появились ext4 и xfs, которые превратились в стандарт. Но учитывая, что под нашу задачу ZFS более чем подходит, да и Linux-версия, судя по отзывам, выросла во вполне вменяемый продукт (хоть и не на полной поддержке, из-за чего поставить систему на ZFS полностью с нуля можно только при помощи различных шаманств), мы решили её попробовать.
По понятным причинам стенд выбрали аналогичной конфигурации (за исключением RAID-контроллера). Установили восемь SSD-дисков по 1920 Гб. Не было желания писать свой сетевой образ для заливки сервера на голую ZFS, поэтому мы откусили от всех дисков по 50 Гб и сделали на них MD RAID-10 под систему. Оставшиеся 1950 Гб на каждом диске объединили в ZFS-аналог RAID-10:
zpool create zpool mirror /dev/sda2 /dev/sdb2 mirror /dev/sdc2 /dev/sdd2 mirror /dev/sde2 /dev/sdf2 mirror /dev/sdg2 /dev/sdh2
Сделали разделы под MySQL:
zfs create zpool/mysql
zfs set compression=gzip zpool/mysql
zfs set recordsize=128k zpool/mysql
zfs set atime=off zpool/mysql
zfs create zpool/mysql/data
zfs set recordsize=16k zpool/mysql/data
zfs set primarycache=metadata zpool/mysql/data
zfs set mountpoint=/var/lib/mysql zpool/mysql/data
Обратите внимание, что мы включили штатное сжатие данных gzip. Процессорных ресурсов у нас на сервере много и они не полностью используются В результате 3 Тб нашей БД превратились в 1,6 Тб, и поскольку слабым звеном, как и в прошлом случае, является максимальная производительность дисков, то чем меньше данных — тем лучше, мы с самого начала получаем отличный бонус от ZFS! В час пик на полной нагрузке на поддержание работы gzip уходит до 4 ядер, но нам и не жалко.
Дальше внедрение пошло быстрее. Под копирку перенесли c LVM-стенда настройки реплики MySQL. Пришлось потратить некоторое время на переписывание скриптов на команды ZFS, но в целом алгоритмы остались прежними. Пример создания снапшота:
zfs set snapdir=visible zpool/mysql/data
zfs create zpool/stage_3307
zfs clone zpool/mysql/data@snapmain zpool/stage_3307/data
zfs set mountpoint=/mnt/stage_3307 zpool/stage_3307/data
Из дополнительного тюнинга: вынесли в память ZFS-разделы с метаданными и логами l2arc и zil. Для нашей задачи, как оказалось в дальнейшем, это было избыточно, но пока что мы оставили эту оптимизацию, поменять при случае несложно. Из негативных эффектов — приходится после перезагрузки сервера пересоздавать соответствующие области памяти. Данные при этом не теряются. Вырезка zpool status:
logs
/dev/shm/zil_slog.img ONLINE 0 0 0
cache
/dev/shm/l2arc.img ONLINE 0 0 0
В такой конфигурации мы начали тестировать стенд и получили прекрасные результаты: с двумя одновременно работающими экземплярами БД (и активной основной репликой) на снапшотах мы получили загруженность дисков на 50-60 %.
Мы избавились от нашей основной проблемы, что видно на графике отставания репликации (сравните с предыдущим графиком в разделе Thin LVM):

Помимо и благодаря этому мы сильно ускорились во всех операциях: полное создание снапшота с остановкой и запуском реплики занимает до 40 секунд, развёртывание из снапшота нового экземпляра MySQL занимает до 20 секунд. Что более чем устраивает как нас, так и наши тесты программного кода.
Промежуточные итоги:
В рамках стенда делать больше ничего, результат нас устраивает. Возможно, в дальнейшем добавим в настройку репликации стенда исключения таблиц, не нужных для тестирования, это еще больше сократит объем БД. Мы не тестировали систему BTRFS и ее реализацию технологии тонкого резервирования. Впрочем, такой задачи уже не стоит, поскольку основная цель достигнута. В целом, конечно же, хочется уйти от вышеописанного подхода — реализовать рабочие миграции БД в тестовую среду, создать отдельный тестовый контур БД, заняться шардированием основной базы. Многое из этого мы уже воплощаем в жизнь, о чем обязательно расскажем в будущих статьях.
Исходная задача была решена, хоть и необычным способом. В промежуточных выводах были описаны достоинства и недостатки каждой из примененных технологий, поэтому давайте решим, какую технологию и когда можно использовать:
На более высоком уровне представления эта статья — не просто сравнение технологии двух файловых систем. Основная идея, которую я хотел бы передать и закрепить, заключается в том, что не стоит бояться мыслить нестандартно в ситуациях, критичных для бизнеса, и брать только готовые рецепты. Когда-то мы могли всем техническим департаментом покачать головой и сказать, что задача создания трехтерабайтных копий БД меньше чем за минуту невыполнима, и нам не нужны рискованные технологии, давайте сделаем как надо. Это было возможно, но мы потеряли бы примерно полгода-год и много поездок клиентов (поездки — наш основной бизнес-показатель) без тестов и во время внедрения. Поступив нестандартно, мы потеряли не так много времени на внедрение, получили опыт в новых и забытых старых технологиях, и предоставили тестирование именно в тот момент, когда мы в нем очень нуждались. Несомненно, это положительно сказалось на всех наших показателях. Выбор всегда за вами, а мы со своей стороны продолжим рассказывать в нашем блоге про интересные текущие и будущие достижения.
Автор: Yuri Saulin
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mysql/349438
Ссылки в тексте:
[1] Thin LVM: http://man7.org/linux/man-pages/man7/lvmthin.7.html
[2] goexpose: https://github.com/phonkee/goexpose
[3] Источник: https://habr.com/ru/post/492172/?utm_source=habrahabr&utm_medium=rss&utm_campaign=492172
Нажмите здесь для печати.