Много свободной RAM, NVMe Intel P4500 и все люто тормозит — история о неудачном добавлении раздела подкачки

    В данной статье я расскажу о ситуации, которая недавно произошла с одним из серверов нашего облака VPS, поставив меня в тупик на несколько часов. Я около 15 лет занимаюсь конфигурированием и траблшутингом серверов Linux, но данный случай совершенно не укладывается в мою практику — я сделал несколько ложных предположений и слегка отчаялся до того, как смог правильно определить причину проблемы и решить ее.


    Преамбула


    Мы эксплуатируем облако средних размеров, которое строим на типовых серверах следующего конфига — 32 ядра, 256 GB RAM и NVMe накопитель PCI-E Intel P4500 размером 4TB. Нам очень нравится эта конфигурация, поскольку она позволяет не думать о недостатке IO, обеспечив корректное ограничение на уровне типов инстансов (экземпляров) VM. Поскольку NVMe Intel P4500 обладает впечатляющей производительностью, мы можем одновременно обеспечить как полное предоставление IOPS машинам, так и резервное копирование хранилища на сервер резервных копий с нулевым IOWAIT.


    Мы относимся к тем самым староверам, которые не используют гиперконвергентные SDN и прочие стильные, модные, молодежные штуки для хранения томов VM, считая, что чем проще система, тем проще ее траблшутить в условиях "главный гуру уехал в горы". В итоге, мы храним тома VM в формате QCOW2 в XFS или EXT4, которая развернута поверх LVM2.


    Использовать QCOW2 нас вынуждает еще и продукт, который мы используем для оркестрации — Apache CloudStack.

    Для выполнения резервного копирования мы снимаем полный образ тома, как снимок LVM2 (да, мы знаем, что снимки LVM2 тормозные, но Intel P4500 нас выручает и здесь). Мы делаем lvmcreate -s .. и с помощью dd отправляем резервную копию на удаленный сервер с хранилищем ZFS. Здесь мы все же слегка прогрессивные — все же ZFS умеет хранить данные в сжатом виде, а мы можем их быстро восстанавливать с помощью DD или доставать отдельные тома VM с помощью mount -o loop ....


    Можно, конечно, снимать и не полный образ тома LVM2, а монтировать файловую систему в режиме RO и копировать сами образы QCOW2, однако, мы сталкивались с тем, что XFS от этого становилось плохо, причем не сразу, а непрогнозируемо. Мы очень не любим, когда хосты-гипервизоры "залипают" внезапно в выходные, ночью или праздники из-за ошибок, которые непонятно когда произойдут. Поэтому для XFS мы не используем монтирование снимков в режиме RO для извлечения томов, а просто копируем весь том LVM2.

    Скорость резервного копирования на сервер резервных копий определяется в нашем случае производительностью сервера бэкапа, которая составляет около 600-800 MB/s для несжимаемых данных, дальнейший ограничитель — канал 10Gbit/s, которым подключен сервер резервных копий к кластеру.


    При этом на один сервер резервных копий одновременно заливают резервные копии 8 серверов гипервизоров. Таким образом, дисковая и сетевая подсистемы сервера резервных копий, являясь более медленными, не дают перегрузить дисковые подсистемы хостов-гипервизоров, поскольку просто не в состоянии обработать, скажем, 8 GB/сек, которые непринужденно могут выдать хосты-гипервизоры.


    Вышеописанный процесс копирования очень важен для дальнейшего повествования, включая и детали — использование быстрого накопителя Intel P4500, использование NFS и, вероятно, использование ZFS.


    История о резервном копировании


    На каждом узле-гипервизоре у нас есть небольшой раздел SWAP размером 8 GB, а сам узел-гипервизор мы "раскатываем" с помощью DD из эталонного образа. Для системного тома на серверах мы используем 2xSATA SSD RAID1 или 2xSAS HDD RAID1 на аппаратном контроллере LSI или HP. В общем, нам совершенно без разницы, что там внутри, поскольку системный том у нас работает в режиме "почти readonly", кроме SWAP-а. А поскольку у нас очень много RAM на сервере и она на 30-40% свободна, то мы про SWAP не думаем.


    Процесс создания резервной копии. Выглядит эта задача примерно так:


    #!/bin/bash
    
    mkdir -p /mnt/backups/volumes
    
    DIR=/mnt/images-snap
    VOL=images/volume
    DATE=$(date "+%d")
    HOSTNAME=$(hostname)
    
    lvcreate -s -n $VOL-snap -l100%FREE $VOL
    ionice -c3 dd iflag=direct if=/dev/$VOL-snap bs=1M of=/mnt/backups/volumes/$HOSTNAME-$DATE.raw
    lvremove -f $VOL-snap

    Обратите внимание на ionice -c3, по факту эта штука для NVMe устройств совершенно бесполезна, поскольку планировщик IO для них установлен как:


    cat /sys/block/nvme0n1/queue/scheduler
    [none] 

    Однако, у нас есть ряд legacy-узлов с обычными SSD RAID-ами, для них это актуально, вот и переезжает AS IS. В общем, это просто интересный кусочек кода, который объясняет тщетность ionice в случае такой конфигурации.


    Обратите внимание на флаг iflag=direct для DD. Мы используем direct IO мимо буферного кэша, чтобы не делать лишних замещений буферов IO при чтении. Однако, oflag=direct мы не делаем, поскольку мы встречали проблемы с производительностью ZFS при его использовании.


    Эта схема используется нами успешно в течение нескольких лет без проблем.


    И тут началось… Мы обнаружили, что для одного из узлов перестало выполняться резервное копирование, а предыдущее выполнилось с чудовищным IOWAIT под 50%. При попытке понять почему не происходит копирование мы столкнулись с феноменом:


    Volume group "images" not found

    Начали думать про "конец пришел Intel P4500", однако, перед тем как выключить сервер для замены накопителя, было необходимо все же выполнить резервное копирование. Починили LVM2 с помощью восстановления метаданных из бэкапа LVM2:


    vgcfgrestore images

    Запустили резервное копирование и увидели вот такую картину маслом:


    Опять очень сильно загрустили — было понятно, что так жить нельзя, поскольку все VPS-ки будут страдать, а значит будем страдать и мы. Что происходило, совершенно непонятно — iostat показывал жалкие IOPS-ы и высочайший IOWAIT. Идей, кроме как "давайте заменим NVMe" не было, но вовремя произошло озарение.


    Разбор ситуации по шагам


    Исторический журнал. Несколько дней ранее на данном сервере потребовалось создать большую VPS-ку со 128 GB RAM. Памяти вроде как хватало, но для подстраховки выделили еще 32 GB для раздела подкачки. VPS была создана, успешно решила свою задачу и инцидент забыт, а SWAP-раздел остался.


    Особенности конфигурации. Для всех серверов облака параметр vm.swappiness был установлен в значение по-умолчанию 60. А SWAP был создан на SAS HDD RAID1.


    Что происходило (по мнению редакции). При резервном копировании DD выдавал много данных для записи, которые помещались в буферы RAM перед записью в NFS. Ядро системы, руководствуясь политикой swappiness, перемещало много страниц памяти VPS в область подкачки, которая находилась на медленном томе HDD RAID1. Это приводило к тому, что IOWAIT очень сильно рос, но не за счет IO NVMe, а за счет IO HDD RAID1.


    Как проблема была решена. Был отключен раздел подкачки 32GB. Это заняло 16 часов, о том, как и почему так медленно отключается SWAP можно почитать отдельно. Были изменены параметры swappiness на значение равное 5 во всем облаке.


    Как бы этого могло не случиться. Во-первых, если бы SWAP был на SSD RAID или NVMe устройстве, во-вторых, если бы не было NVMe-устройства, а было бы более медленное устройство, которое бы не выдавало такой объем данных — по иронии, проблема случилось от того, что NVMe слишком быстрый.


    После этого все стало работать как и раньше — с нулевым IOWAIT.

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 31

      0
      Спасибо, интересно.
      Где именно можно почитать, почему так медленно отключается swap?
      +4
      Сейчас меня закидают какашками, но… Какой смысл от 8Г свопа на сервере с 256Г памяти?
        0

        Видимо, чтобы был. Я где-то читал, что бывают проги, которые без свопа не хотят работать, может они решили так подстраховаться.

          +2

          Ну да, только это было, наверное, во времена Windows 98.

            0

            Нууу...


            Не так давно столкнулся с вылетами игрульки The Division 2 от Ubisoft на Win10 с отключённым swap. Вылетала стабильно через 15-30 минут игры, ничего внятного не писала, RAM даже на половину не была заполнена.


            Создание свапа на гигабайтик решило проблему полностью.

              0

              Это связано с исчерпанием виртуальной памяти, а не с отсутствием свопа. Посмотрите, ради интереса, значение Commit Size — общее количество выделенной виртуальной памяти, и увидитесь, насколько оно велико по сравнению с реально используемой памятью.


              То есть ситуация, когда при отсутствии свопа игра использует только 5-6 гигов памяти, а выделило 10-11 — абсолютно нормальная. Аналогичная фигня была, когда я гонял нейросетки. Судя по всему, это связано с маппингом памяти видеокарты в общее виртуальное пространство. То есть к объёму реально используемой памяти надо добавлять ещё и объём видеопамяти.


              А всё дело в том, что в винде нет оверкоммита — ситуации, когда система позволяет выделить больше памяти, чем ей доступно, из расчёта на то, что большая часть страниц использоваться не будет.


              Проблема решается как включением свопа, так и увеличением количества оперативной памяти.

                +1
                А всё дело в том, что в винде нет оверкоммита

                Есть там оверкоммит, по крайне мере в Win 10. Запустите Process Explorer, включите в колонках "Virtual Size" (запрошенная но не использованная память) и увидите что процессы запрашивают намного больше чем в сумме дают своп + оперативка. У меня 32G памяти, свопа нет совсем, и вот такое:


                void* ram = malloc((size_t)256 * 1024 * 1024 * 1024);

                отлично работает (VS, x64), ProcessExplorer честно показывает выделенные 256G. Реально же память начнёт использоваться только по мере обращения.


                Кстати, в этой же конфигурации никогда не было проблем из-за отсутствия свопа, кроме пары ситуаций когда кто-то реально сожрал всю память.

                  0
                  void ram = malloc((size_t)256 1024 1024 1024);

                  А у меня почему-то не работает (ram == NULL). И во всех документациях написано, что malloc в Windows приводит именно к выделению (commit) всей запрошенной памяти. Может, этот режим включается как-то отдельно?


                  Virtual Size

                  А вот здесь вы путаете состояния reserved и commiitted (link). Зарезервировать вы можете сколько угодно адресного пространства (точнее, пока не исчерпается таблица страниц), вот только чтобы эту память можно было использовать, её все равно нужно закоммитить.


                  Скорее всего, в вашем случае память сначала резервируется менеджером памяти, а дальше при выделении происходит ошибка. Из-за этого вы видете огромный Virtual Size, которые вообще ничего не значит.

                    –1
                    А у меня почему-то не работает (ram == NULL).

                    Интересный расклад… Да, каюсь, не проверял — у меня тоже ram == NULL, я смотрел только размер процесса — и он (Virtual size) соответствует запросу, несмотря на то что malloc() возвращает NULL. С другой стороны:


                    void *ram = VirtualAlloc(NULL, (size_t)256 * 1024 * 1024 * 1024, MEM_RESERVE, PAGE_READWRITE);

                    точно вернёт не NULL, и это попадёт в Virtual size. Правда, использовать его без явного commit тоже не получится.


                    А вот здесь вы путаете состояния reserved и commiitted

                    Virtual size это почти полный эквивалент линуксового VSZ, это именно reserved (в обоих системах), и размер reserved space может значительно превышать имеющуюся физическую память. Committed, в свою очередь, это либо использованный (= к нему обратились) участок памяти (RSS в Linux), либо явно указали что хотят его "живым" (Private bytes в Windows, если же оно реально использовано — то Working set).


                    Overcommit же обозначает только что что можно получить у системы адресное пространство по размеру больше чем размер памяти и свопа, но не даёт гарантий что его можно использовать. Таким образом, overcommit в Linux всё равно не даст вам получить и использовать больше чем у вас RAM + swap, другое дело что это более прозрачно для приложений.

                      0
                      Virtual size это почти полный эквивалент линуксового VSZ, это именно reserved (в обоих системах), и размер reserved space может значительно превышать имеющуюся физическую память.

                      Это просто резервирование адресов внутри процесса, к реальному выделению памяти это не имеет никакого отношения.


                      Committed, в свою очередь, это либо использованный (= к нему обратились) участок памяти (RSS в Linux), либо явно указали что хотят его "живым" (Private bytes в Windows, если же оно реально использовано — то Working set).

                      Уже ближе. Если память закоммитить, но не использовать, то страницы будут указывать на страницу, заполненную нулями, а реальный маппинг на физическую память или своп произойдёт только при первой записи в страницу (copy-on-write). И именно в этом случае произойдёт увеличение значения RSS или Private Bytes.


                      Таким образом, overcommit в Linux всё равно не даст вам получить и использовать больше чем у вас RAM + swap, другое дело что это более прозрачно для приложений.

                      Как раз нет, overcommit и заключается в том, что приложения могут суммарно закоммитить больше памяти, чем операционная система может предоставить, исходя из предположения, что к части памяти обращений не будет вообще.


                      Поэтому в Linux возможна ситуация, когда приложение крашится не на malloc, а в процессе заполнения выделенной памяти.


                      В Windows же, наоборот, такая ситуация невозможна, т.к. операционная система исходит из предположения, что приложению может потребоваться вся закоммиченная память, и держит эту память в резерве, даже если она никогда не понадобится.

                        0
                        Это просто резервирование адресов внутри процесса, к реальному выделению памяти это не имеет никакого отношения.

                        malloc() в линуксе (точнее, brk(), а также mmap()) тоже только резервирует, реальное выделение идёт после обращения. Собственно, в этом и разница — в линуксе это прозрачно, в вынь — нужно явно выделять.


                        Поэтому в Linux возможна ситуация, когда приложение крашится не на malloc, а в процессе заполнения выделенной памяти.

                        И чем это принципиально отличается от того что я сказал?


                        Таким образом, overcommit в Linux всё равно не даст вам получить и использовать больше чем у вас RAM + swap, другое дело что это более прозрачно для приложений.

                        Тот факт что крэш будет не на malloc() мало что меняет для большинства приложений, потому что большинство, всё же, либо сразу использует (пытается) запрошенную память, либо использует calloc(), если же оно осознаёт что памяти может быть меньше чем хочется, то вполне в состоянии явно управлять выделением (committing).


                        Overcommit в том виде как реализован в Linux реально хорош только когда у вас куча KVM или чего-то вроде (причём вы точно знаете что они никогда не вылезут за реальную память одновременно), или совсем уж специальные задачи где нужен большой непрерывный кусок памяти с редкими использованными блоками в нём.

                          0
                          потому что большинство, всё же, либо сразу использует (пытается) запрошенную память, либо использует calloc(), если же оно осознаёт что памяти может быть меньше чем хочется, то вполне в состоянии явно управлять выделением (committing).

                          Ну вот же пример: игры. Закоммичено 10-11 гигов, реально используется 4-5 гигов, а ещё для порядка 6 гигов система тупо резервизует место в оперативке + своп. В итоге на компе с 16 гигами оперативки приходится включать своп, чтобы поиграть, не закрывая при этом все остальные приложения.


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

          +2

          Смысл в том, что при высокой утилизации ram, часть неиспользуемых данных может быть перенесена в swap, освободив место под буферы файловой системы, что хорошо скажется на производительности. Кроме того, если вдруг используется zswap (а у нас такие ноды есть), то это must have.

            0
            А разве swap на ssd не смерти подобен?
              +4
              Это байки. Особенно, если учесть что большинство современных серверов — SSD-only. За 7 лет эксплуатации около 300 серверов с SSD я не видел никаких негативных аспектов размещения SWAP на этих накопителях. Особенно, если речь идет о современных серверных накопителях с развитыми средствами диагностики неисправностей.
                +3

                Нет. Система пишет в своп большими кусками — write amplification получается достаточно низкий, поэтому ресурса SSD хватает надолго. Не вижу смысла его жалеть, потому что комфорт от использования SSD стоит дороже затрат.


                У меня вот SSD с 72000 часов работы, своп на нём. Совсем недавно ресурс с 98% до 97% снизился. Скорее, я его заменю по причине морального устаревания.

                  0

                  А не подскажете, раз уж зашла тема, как оно на Windows Server? У меня 2008r2 на ssd стоит, причем перенесенный с обычного жесткого. Долго обычный бытовой диск проживёт?

                    +1

                    Просто посмотрите данные SMART диска на предмет media wearout.

                      0

                      От вендора зависит, на самом деле. Там ещё всякие значения с инфой о частоте ошибок, количестве ячеек при смерти и т.д.


                      У меня один из самых первых SSD посыпался уже через 1.5 года, видимо по причине сырой прошивки в контроллере, которая не умела равномерно писать на весь диск и давала write amplification в пару сотен. Довольно быстро начала деградировать скорость записи, а при остатке ресурса 70% начали портиться данные — искажался 1 байт на пару мегабайт данных, а это гораздо хуже, чем просто смерть диска. Данные SMART показали, что была ячейка, которая пережила уже больше 100 тысяч перезаписей.

            0
            скажите, как вы «прокидываете» NVME в VM'ы и сколько у вас обычно VM'ом на одной физ.ноде? спасибо.
              0

              Добрый день. Никак мы это не делаем (NVME), о чем в статье и написано — мы используем QCOW2 в файловой системе EXT4/XFS.


              Сколько vm на ноде? Столько, чтобы не начался steal между VM и влезали на RAM и хранилище. По нашему опыту и дизайну даже при полных занятых 256 GB больше 80% занятого CPU мы не видели. На практике всегда можно переместить "энергичную" VM на другой узел.

              0
              Как то странно выглядит рассказ о такой достаточно тривиальной истории, с учетом того, что у автора в профиле написано про большой опыт и так далее…
                0
                Не могу это никак комментировать.
                0
                Не раскрыта тема того, что случилось с LVM VG. Почему оказалась повреждена мета?
                Второй вопрос — почему вы не делаете снапшоты средствами QEMU (external snapshot)?
                  0
                  Добрый день.

                  LVM VG повредилась непонятно почему, поэтому тема и не раскрыта. Предполагаю, что во время того как под очень высоким LA сервер находился, что-то у LVM отработало нестандартно (в логах было пусто на эту тему).

                  Второй вопрос — почему вы не делаете снапшоты средствами QEMU (external snapshot)?


                  Это системный бэкап «судного дня», пользователи же могут делать бэкапы с помощью средств Apache CloudStack, который использует возможности Qemu, но может фризить виртуалки на несколько секунд.

                  Ибо вот эта штука все еще работает и не только в RH: bugzilla.redhat.com/show_bug.cgi?id=920020 (в общем, без фриза под большой нагрузкой, при создании снимка, тома QCOW2 могут повреждаться). Я это видел раза два. Если мы будем делать снимки средствами Libvirt, нам придется фризить VM в произвольное время, чтобы сделать снимок без угрозы состоянию тома QCOW2.

                  Более того, без настроенного внутри VM qemu-agent все равно разницы никакой нет, а qemu-agent стоит не во всех VM.

                  Ну, и в целом, мы стремимся к надежности. Условный «системный» уровень LVM и DM мы считаем куда более надежным, чем «прикладной» уровень QEMU/LibVirt.
                    0
                    Спасибо, интересная ссылка.
                    Но я говорил о external snapshot, он не использует возможности QCOW, все операции со снапшотом делаются QEMU, можно снапшотить даже raw диски.
                    Почему не используете понятно.)
                      0
                      Я посмотрю на этот способ. Однако, мне все же не ясна польза от этого, если я делаю полный бэкап тома гипервизора?

                      UPD: все же без обширного тестирования я бы на это не пошел. К примеру, у меня 500 VM, делаю я бэкапы 1 раз в сутки — 182500 бэкапов в год.

                      Насколько этот код такой же зрелый как и для lvcreate -s, что ему можно доверять? Даже при вероятности сбоя на уровне 0.1% в год я могу потерять 182 тома. С учетом того, что подобная «проблема» может происходить время от времени — то там, то здесь, будет очень тяжело диагностироваться, особенно если «проблемным» оказался снимок, а не том после blockpull/blockcommit.

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

                      Сейчас проверка резервной копии делается легко — fsck.{ext4, xfs} volname.raw

                      Как этот механизм работает под нагрузкой, хотя бы вот как в описанной статье?
                        0
                        Плюсы в следующем:
                        • Не зависит от дискового бэкенда. Диск может быть в LVM/ZFS/QCOW/NFS и тд.
                        • Можно вместе со снапшотом делать дамп памяти. Актуально например для серверов БД (чтобы не повреждать ее).

                        В вашем случае добавляется еще и возможная неконсистентность XFS при снапшоте, а значит и QCOW образов на ней. В общем что-то вы конечно восстановите, но если приложению в госте критична консистентность данных, то можно напороться. В остальном ваш способ вполне подходит для резервного бэкапа.

                        Насчет надежности не скажу, у нас этот способ работает на паре машин, проблем не было. Но тут конечно стоит более масштабно протестировать.
                        А вы уверены, что lvcreate -s надежный?) Вы же сами написали про повреждение меты LVM.
                        Уверены, что у вас данные всех томов правильные и в какие то случайные сектора не записалась каша (например данные снапшотов)?
                          0
                          В вашем случае добавляется еще и возможная неконсистентность XFS при снапшоте, а значит и QCOW образов на ней. В общем что-то вы конечно восстановите, но если приложению в госте критична консистентность данных, то можно напороться. В остальном ваш способ вполне подходит для резервного бэкапа.


                          lvcreate делает freeze и unfreeze файловой системы перед созданием снимка. Тут проблем нет. Это даже для бэкапа СУБД вполне применяется, например, MySQL.

                          Я уверен, что LVM2 значительно более надежный, чем QEMU.

                          Вы забываете, что я живу в оркестраторе — это не ручное управление VM на уровне Libvirt, это at scale. Там вообще нет никакого маневра, особо. Если, к примеру, узел уйдет в отрыв, когда я «снаружи» условно делаю какие-то операции мимо оркестратора, типа там снимки свои, не факт, что он мне потом не скажет — «извини, голубчик», а я пойду руками возвращать как было.

                          Само собой, у меня резервная копия «неконсистентная», но это минимальное зло, с учетом, что у пользователей есть весь инструментарий, которым они могут пользоваться для обеспечения необходимого уровня безопасности.

                Only users with full accounts can post comments. Log in, please.