14 апреля 2014 в 16:07

Кэш на запись и DRBD: почему полезно знать подноготную

Предыстория


Существует красивое решение для создания надёжного недорогого кластера основанное на DRBD + Proxmox VE. Страница в Wiki проекта Proxmox появилась 11 сентября 2009-го года и создана она была CEO компании Martin-ом Maurer-ом.



С тех самых пор это решение стало очень популярным, и никто не подозревал, что у этого решения есть скрытый подводный камень. В документации про это не пишут, а те, кто сталкивался с последствиями этой проблемы (например, зависание машины при онлайн миграции с одного хоста на другой), списывали всё на «случай». Кто-то грешил на железо, кто-то на Proxmox, а кто-то на драйверы внутри виртуальной машины. Конечно, хотелось бы, чтобы DRBD сам сообщал о своих проблемах, и, как-то подсознательно веришь в то, что он так и делает. Проверяешь /proc/drbd, видишь строку «cs:Connected ro:Primary/Primary ds:UpToDate/UpToDate» и продолжаешь верить что DRBD не причём.

Однажды, внимательно читая документацию к DRBD, я нашёл рекомендацию делать регулярную проверку DRBD на целостность, т.е. на идентичность двух нод между собой. Ну, а почему бы и нет, подумал я, проверка лишней не будет. И тут началась история длиной в целый год.

Оказалось, что каждую неделю в DRBD появлялись новые блоки out-of-sync. Т.е. данные на двух серверах отличались. Но почему?!

Замена железа, обновление версии DRBD, отключение offload и bonding-а к положительному результату не приводили, а новые блоки всё появлялись и появлялись. Анализ того, какие именно блоки оказывались out-of-sync, привёл к следующим результатам:
— часто — swap раздел виртуальной машины с Linux
— редко — Windows с разделом NTFS на виртуальной машине
— никогда — ext4 на виртуальной машине с Linux

Это было «интересно», но ответа на вопрос «почему и как это может быть» не давало. Обо всех своих изысканиях я рассказывал в списке рассылки «DRBD Users» (http://www.gossamer-threads.com/lists/drbd/users/25227) и однажды я получил ответ, которого так долго ждал. Lars Ellenberg, один из основных разработчиков DRBD, рассказал, что именно происходит, и как это проверить.

Как происходит запись в DRBD


Общий случай:
1) DRBD получает запрос от ОС за запись данных из буфера;
2) DRBD записывает данные на локальное блочное устройство;
3) DRBD отправляет данные второй ноде по сети;
4) DRBD получает подтверждение окончания записи от локального блочного устройства;
5) DRBD получает подтверждение окончания записи от второй ноды;
6) DRBD отправляет ОС подтверждение окончания записи.

А теперь представим себе, что данные в буфере неожиданно изменились и мы получим:
1) DRBD получает запрос от ОС за запись данных из буфера;
2) DRBD записывает данные на локальное блочное устройство;
2.5) данные буфера изменились;
3) DRBD отправляет данные второй ноде по сети;
4) DRBD получает подтверждение окончания записи от локального блочного устройства;
5) DRBD получает подтверждение окончания записи от второй ноды;
6) DRBD отправляет ОС подтверждение окончания записи.
И мы тут же получаем out-of-sync.

Как же такое получается? Очень просто. Буфер может использоваться приложением для кэширования на запись (приложение, у нас, это процесс виртуальной машины). В этом случае приложение может обновлять данные в буфере, не дожидаясь подтверждения записи этих данных на физическое устройство или контроллер, а в случае с DRBD это неприемлемо.

Самое неприятное во всем этом то, что для возможности потерять данные не нужно даже отключения питания. Достаточно лишь включить кэш на запись.

Как проверить? Нужно включить в настройках DRBD data-integrity-alg и ждать. Если вы получите что-то вроде «buffer modified by upper layers during write», то это оно и есть. Будьте осторожны, если DRBD работает в режиме dual-primary, то при включенном «data-integrity-alg» вы тут же получите split brain (об этом не написано в документации).

Кэширование записи в QEMU/KVM


По-умолчанию виртуальные машины в Proxmox VE используют для виртуальных дисков режим cache=none. Несмотря на то, что «none» как бы говорит нам «ничего не кэшируем», на самом деле это не совсем так. None, в данном случае, означает не использовать «host cache», но кэш на запись в блочное устройство по-прежнему используется. А отсюда мы получаем проблему с рассинхронизацией блоков в DRBD.



Всего два режима можно считать надёжными при использовании с DRBD: directsync и writethrough. Первый не использует кэширование вообще, т.е. читает всегда напрямую с блочного устройства (это может быть RAID контроллер) и пишет в блочное устройства (это может быть RAID контроллер) обязательно дожидаясь подтверждения записи. Второй режим использует «host cache» для чтения.

Таким образом, система виртуализации может стать катастрофически медленной, если вы не используете физический RAID с кэшированием за запись и BBU. А если вы используете RAID с BBU, то виртуальная машина получит подтверждение о записи сразу же после помещения данных в кэш контроллера.

Почему же нет out-of-sync на ext4


Использование режимов cache=directsync и cache=writethrough являются самыми надёжными, т.к., в этом случае, нам не нужно беспокоиться о том, что происходит внутри виртуальной машины. Но это не единственный способ. Это нужно учитывать в тех случаях, когда нам недостаточно производительности RAID или же у нас нет RAID вообще.

Убедиться в том, что данные уже записаны можно не только на уровне процесса виртуальной машины, но и внутри VM. Здесь мы знакомимся с понятием barrier, которое предполагает, что файловая система сама знает, когда её нужно сбросить данные из кэша на физическое устройство и дождаться завершения этой операции. А man mount говорит нам, что «The ext4 filesystem enables write barriers by default». Именно поэтому мы и не увидим никогда out-of-sync на тех областях, где расположена файловая система использующая barriers.

Полезные ссылки:
— Proxmox и DRBD: pve.proxmox.com/wiki/DRBD
— Out of sync: www.gossamer-threads.com/lists/drbd/users/25227
— Ещё про out of sync: forum.proxmox.com/threads/18259-KVM-on-top-of-DRBD-and-out-of-sync-long-term-investigation-results
— Описание параметра cache для kvm/qemu: www.suse.com/documentation/sles11/book_kvm/data/sect1_1_chapter_book_kvm.html
— Ещё про кэш: www.ilsistemista.net/index.php/virtualization/23-kvm-storage-performance-and-cache-settings-on-red-hat-enterprise-linux-62.html?start=2
Станислав Герман-Евтушенко @giner
карма
87,1
рейтинг 0,0
Linux, DevOps
Похожие публикации
Самое читаемое Администрирование

Комментарии (45)

  • 0
    Спасибо! Пригодился
  • 0
    Очень интересно. Спасибо!
  • +1
    В линуксе давным-давно идёт срач на тему персистентных страниц при записи. Другими словами, «можно ли писать в грязные страницы». Срач типовой для подобной ситуации — одних ужасает, что данные могут меняться в середине записи (и записываться непонятно как), других ужасает то, как дорого и медленно выглядит решение проблемы.

    [Решение: полный отказ от кешей на запись любого уровня и переход на ssd].
    • +1
      Я бы предпочёл, чтобы в DRBD был параметр «copy before write», который включен по-умолчанию и можно было бы его выключить «если знаешь, что делаешь». Кстати, Lars упонянул, что подобная ситуцаия может встериться и с програмным RAID. Интересно копирует ли mdraid буфер перед записью?
      • 0
        Именно вокруг этого параметра в LKML срач и стоит.

        Нет, никто не копирует, потому что это очень дорого. Производительность проседает кратно. А дальше — см написанное выше — два лагеря, одни ужасаются при виде коррапченных данных, другие при виде провисших графиков производительности.

        Насколько я знаю, в линуксах этот параметр уже можно менять (и он будет глобальным для всех блочных устройств и файловых систем). Ща попробую погуглить.

        UPD: Называется stable pages,
        lwn.net/Articles/442355/
        • 0
          Да, про stable pages как раз Lars и говорил, только про параметр я ничего не нашёл.
          • +1
            Ага, я посмотрел, оно так и не смержено.

            Вот тредик с вялым отбрыкиванием от патча:
            thread.gmane.org/gmane.comp.file-systems.ext4/25009

            В последней версии они предлагают лочить конкурентных записантов пока запись не будет завершена.
        • 0
          А зачем копировать все целиком, когда хватит битика, отвечающего за то, была запись или нет. drdb мог бы битик проверять после записи и если что докатывать изменения.
          • 0
            Вот такими решениями оно и получается ад и ужас.

            Ну, записали мы локально. Собираемся отправить на пира. Обнаружили изменения. Наши действия? Записать локально и послать на пира? За это время оно опять поменяется. И опять, и опять. Условно говоря, сетевая latency 100µs, а клиент делает 20kIOPS.
            • 0
              Так и наплевать на латенси, клиенту приедет подтверждение записи сразу после попадения в кеш.
              Ну и сколько у вас таких страниц, которые успевают перезаписываться? Я думаю что не очень много.
              • 0
                Подтверждение сразу после попадания в кеш == write back. Достаточно одной странно записанной страницы, чтобы развалить LVM огромного размера. Или удалённую файловую систему с iscsi.
                • 0
                  Так сколько будет таких страниц? Если у вас будет ждать полной записи на все ноды 1% запросов, то на общую производительность это скажется совсем не сильно.
                  Вообще мне казалось что блокировочки тут должен делать элеватор. Но подозреваю что таки нет.
                  • 0
                    Если от этого 1% зависят остальные (что бывает при обновлении метаданных), то этот 1% будет тормозить всё остальное. Причём банальное cp будет идти с той же скоростью, а вот OLDP (то есть осмысленная структурированная нагрузка) будет простаивать в дедлоках.

                    Собственно, если в DRBD и нижележащих штуках полностью выключить всё и сделать stable pages, то мы получим кровоточащую производительность — потому что само drbd тоже активно пишет метаданные и мы получим такую простую математику (C-mode):
                    операция записи:
                    latency до drbd + латенси локального drbd + латенси нижележащего устройства с метаданными (IO1) + латенси нижележащего устройства с данными (IO2) + латенси сети, плюс латенси удалённого drbd плюс латенси метаданных удалённого drbd (IO3) + латенси данных удалённого drbd плюс латенси ответа.

                    Самыми большими обычно являются latency сети и IO, то есть мы получаем IO_latency*4 + network_latency. Если у нас 0.1ms сеть, 0.1ms SSD, то мы получим минимум 0.5ms на запись. Что означает при одном потоке на запись максимум 2к IOPS. При том, что в параллельном IO на тех же SSD мы имеем 50к IOPS.
                    • 0
                      Так у вас выбор или все писать до конца, или только то что менялось. Второе всяко быстрее.
                      А кто хочет надежно за иопсами не гонится.
  • 0
    Кто-нибудь обладает информацией о поддержке barriers на NTFS в Windows? Кэш на запись можно включить/отключить в настройках диска, а вот про barriers я ничего не нашёл.
    • 0
      Барьеры — это абстракция линукса. В виндах её нет, и даже если бы она была, это была бы абстрация виндов. То есть на уровне устройства никаких барьеров нет, а есть writeall, etc — зависит от конкретной шины. Некоторые устройства (консьюмерские ssd) могут вообще не поддерживать синхронную запись.
      • 0
        Как бы оно не называлось, но что-то подобное должно существовать, иначе нельзя было бы гарантировать целостность при использовании кэша на запись.
        • 0
          При чём тут вообще кеш на запись? Кеш на запись на уровне хоста означает, что при потере питания или краше ядра данные превращаются в тыкву.

          stable pages, точнее, их отсутствие, говорит про ситуацию, когда в страницу пишется несколько комплектов данных не дожидаясь, что сосед закончит. Частным случаем этого является write-back, т.е. drbd говорит «угу», не закончив операцию, и огребает следующую запись в тот же блок. Другим случаем является write-through при конкурентной записи.

          Проблема именно в том, что страница, отправленная на запись в блочное устройство, может меняться. Не важно чем, важно, что это чистой воды race condition. При этом патч предполагает блокировку всех, кто пытается конкурентно писать — а это может сильно уронить производительность. Потому патч не принят — ведь файловых систем это не касается.
          • 0
            Я сейчас вообще не про DRBD. Без barriers мы можем, столкнуться с ситуацией, когда в результате reordering (так может делать железка или планировщик) и отключения питания мы получим не просто потерю коммита, а порушенную файловую систему.
            • 0
              Насколько я понимаю, все файловые системы в курсе про reordering. Вообще, сам reordering никаким образом sync-записи не мешает, потому что если мы отправили запрос на запись, следующей записи не будет, пока не будет успеха от этой.
              • 0
                Вот тут об этом хорошо написано: lwn.net/Articles/283161/
                Как с этим обстоят дела в Windows найти не могу.
  • 0
    А почему DRBD не может получить уведомление о событии записи (п. 2.5)?
    • 0
      Как я понимаю, механизма нет )

      Судя по lwn.net/Articles/442355/ ситуация такая:
      1) Используется кэш на запись (как у автора)
      2) Приложение посылает запрос на запись
      3) Данные пишутся в страницу кэша
      4) Страница помечается как грязная
      5) В очередь планировщика IO ставится запрос на сброс грязной страницы на диск
      НО несмотря на то, что страница помечена как грязная, писать в нее все еще можно. С этого момента начинается русская рулетка. Худший сценарий выглядит так:
      6) Очередь планировщика IO доходит до первого запроса и начинается его исполнение. Это ОЧЕНЬ медленная операция по сравнению со всем остальным. При этом данные уходят на уровень DRBD в виде указателя на страницу кэша. Грязная страница в этот момент не блокируется на запись (патч как раз это и должен делать).
      7) DRBD пишет данные на первый том
      8) Приложение посылает второй запрос на запись в то же место (как пример, менеджер виртуальной памяти пишет в своп)
      9) Этот запрос попадает в кеш, в ту же страницу, что и первый, и перезаписывает данные. При этом в очередь планировщика IO уходит второй запрос на сброс страницы.
      10) DRBD начинает писать данные на второй том, у него указатель на уже измененные данные. На второй том именно они и уходят.
      11) Получаем рассинхронизацию

      Я, правда, не совсем понимаю вот что: второй запрос на IO, который появился при изменении данных в кэше, должен исправить ситуацию — DRBD перезапишет парные блоки еще раз и все будет ок? Интересно, почему этого не происходит?
      К тому же, в lwn.net/Articles/442355/ проблему обсуждают не в контексте DRBD, а в том, что некоторые контроллеры вычисляют на этапе 6 контрольную сумму, потом пишут данные на диск на этапе 10, вычисляют сумму повторно и ругаются на «checksum error».

      Решений проблемы, как я понимаю, два — копировать данные при начале операции IO средствами нижних слоев стека или блокировать страницу на время IO и помещать новые данные в другие страницы средствами менеджера кеша. Или кэш на запись отключить )

      QEMU — тоже молодец: параметр cache=none отключает не только кеш на уровне VM, но и контроль за операциями IO вообще, так что запрос просто уходит в память гипервизора, VM сообщается, что запрос выполнен, а успел ли гипервизор его записать — не проверяется. При этом, правда, VM сообщается, что устройство, на которое она пишет, работает в режиме writeback, так что если VM успевает вовремя слать sync (как это делает Linux с ext4), то все хорошо )
      • 0
        К тому же, в lwn.net/Articles/442355/ проблему обсуждают не в контексте DRBD, а в том, что некоторые контроллеры вычисляют на этапе 6 контрольную сумму, потом пишут данные на диск на этапе 10, вычисляют сумму повторно и ругаются на «checksum error».

        DRBD делает точно так же при включенном data-integrity-alg. Причём, если режим работы primary/secondary, то secondary автоматически переподключается (disconnect -> connect), а в случае с dual primary тут же получаем split brain.

        QEMU — тоже молодец: параметр cache=none отключает не только кеш на уровне VM, но и контроль за операциями IO вообще, так что запрос просто уходит в память гипервизора

        Только не гипервизора, а блочного устройства. Кэш хоста, в этом случае, как раз не используется.
        • 0
          К тому же, в lwn.net/Articles/442355/ проблему обсуждают не в контексте DRBD, а в том, что некоторые контроллеры вычисляют на этапе 6 контрольную сумму, потом пишут данные на диск на этапе 10, вычисляют сумму повторно и ругаются на «checksum error».

          Я это к тому, что на lwm.net проблемы с DRBD не признают и считают дефект косметическим — ну, поругался контроллер на checksum, ну, записал данные еще раз — все же ок?
          • 0
            Угу, это печаль :)
      • 0
        Как-то так все печально, что даже не верится.
        Интересно как все это устроено в виндах.
        • 0
          Как-то так все печально, что даже не верится.
          Интересно как все это устроено в виндах.

          Так же, просто в виндах нет механизмов аналогичных DRBD и, поэтому, нет проблем.
          Хотя может в случае софтрейда в винде что-то проявляется.

          Я, например, много лет юзаю md-raid в линуксе и таких проблем не вижу — проверки рейд проходит регулярно.
      • +1
        Тут даже кеш не важен — если у нас есть что-то типа iscsi поверх lvm, поверх drbd, то второй запрос записи в ту же область мы можем словить параллельно первому. То есть первый запрос мы не выполнили, а уже пришёл второй.

        Особенно актуально на всяких недокластерных файловых системах.
  • +1
    вставлю свои 5 копеек…
    аппаратные RAID контроллеры (точнее их драйвера) достаточно честно относятся к barrier, и если оно включено, то драйвер контролера при получении запроса на запись с barrier, скомандует контроллеру честно положить данные на диск, минуя write cache контроллера. Таким образом, мы имеем: 1) быстрый, но битый DRBD с выключенным barrier или 2) целый DRBD, неиспользуемый write-cache контроллера + низкую производительность на запись с включенным barrier

    короче говоря, или мы вообще без кеша на запись живем или drbd ломается(((
    • 0
      Я чуть выше описал сценарий, при котором даже при отключенном кеше конкурентная запись побъёт данные.
  • 0
    Друзья, наткнулся на статью, и испугался…

    Подскажите, чем опасна связка:

    /dev/sd[a,b] (SSD) -> md -> lvm -> drbd0 -> drbd10 -> ext4 -> qcow2 -> KVM

    Хочу иметь не дорогое решение для виртуализации c синхронизацией на три узла. Если будет использован режим: writethrough (в KVM), и drbd только в режиме: master/slave.

    Можно спать спокойно?

    Можно-ли linux хосты держать в других резимах? — Если побьётся swap, то это же не страшно. Всё одно, на другом хосте оно поднимится через reboot.
    • 0
      Очень сложно. Если ошибка произойдёт на любом из уровней, восстанавливать будет сложно.
      • 0
        Согласен. Не буду так сильно рисковать. Сделаю чуть попроще. Но всё же, очень хочу drbd. Вы от него не отказались в итоге?
        • 0
          Нет, не отказался. Сейчас полёт нормальный. Работает на 4х кластерах с различной конфигурацией, т.е. на 8ми хостах в целом.
          • 0
            Если я Вам опишу свою планируемую схему, не сочтёте за труд, вникнуть, и подсказать мне, где я могу быть не правым?
            • 0
              Пишите, попробуем.
              • 0
                Написано несколько официальным текстом, чтобы можно было технически обосновать, ну и понять, чего же я вообще хочу. — Без этого никуда. Это некий драфт ТЗ. Посмотрите пожалуйста, и прокомментируйте текст (там прям можно комментировать): docs.google.com/document/d/1SXHOvFkQExFErPuQEVPrGHFsoUfEgcV0LV7KnA0VzKY/edit?usp=sharing
                • –1
                  Напишите мне в гугл чат. Обсудим.
                  • 0
                    Написал вроде… Я в goole не особо понимаю чего они там натворили, но вроде как в hangouts теперь только можно.
                • 0
                  Простите, что вмешиваюсь, но мне кажется, что использовать DRBD для бэкапов — это плохая идея. Например, есть у нас СУБД, у нее кэш на 10G в RAM и она использует его по своему усмотрению. В момент репликации кэш, естественно, не сбрасывается, и в результате на slave получается тыква вместо бэкапа. Вообще, в Windows VSS writers не зря придумали, как и в Linux перед созданием LVM-снэпшота для бэкапа MySQL команду FLUSH TABLES WITH READ LOCK посылают не зря. Если вам нужны целостные бекапы — применяйте инструментарий именно для бэкапов.
                  • 0
                    Для гарантированно целостных бекапов: vm выключаются, и с них снимается qcow2 снепшот (т.е. состояние в случае чего должно быть консистентным). В случае поднятия на втором узле машин при резком сбое основного узла — это будет как power cut для вирт. машин (если файлы образов не побьются при этом конечно). — Так или иначе, все БД рассчитаны на такой случай (наверно). Машины, где используется БД — можно использовать в режиме cache writeback.

                    Я сам в общем не могу сказать, что я уверен, в том, чего сейчас несу. Но как-то так я это себе предстваляю.

                    В противном случае, выходит, что drbd вообще не нужен как и любая сетевая распределённая файловая система…
    • 0
      Использую lvm поверх него drbd, libvirt cache=writethrough. Виртуалки на raw drbd. Все равно ошибки проскакивают(!!!), несмотря на то, что обновлял весь софт (gentoo). спасает конечно проверка checksum, но приятного от этого мало. Где было возможно ушел на Ceph, чего и Вам желаю.
      • 0
        Подскажите несколько вопросов:
        1. Как долго в среднем делается checksum?
        2. Можно ли checksum ставить на паузу?
        3. Как там с приоритетом io при проверке checksum?

        Ceph — на две машины мне кажется это слишком…
        • 0
          Паузу, на соклько я помню, сделать нельзя. Длительность процесса verify зависит от размера DRBD ресурса.
          Посмотрите вот это раздел pve.proxmox.com/wiki/DRBD#Integrity_checking

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.