Раздача статического контента — счет на милисекунды

    image

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

    И вот мы решили ускорять то, что и так работает быстро и, заодно, поделиться опытом того, что получилось в итоге. Конечно же я расскажу о граблях, о том где не надо HTTP/2, о том почему мы покупаем 7,6Tb NVMe SSD вместо 8x1Tb SATA SSD и много другой узкоспециализированной информации.

    Давайте сразу договоримся что хранение и раздача контента это 2 разные задачи и говорить мы будем только про раздачу (продвинутый кеш)

    Начнем с железа…

    NVMe SSD


    Как вы уже поняли, мы будем идти в ногу с прогрессом и задействуем для хранения кеша современный 7.68 TB SSD HGST Ultrastar SN260 {HUSMR7676BHP3Y1} NVMe, HH-HL AIC. Тесты винта показывают не такую красивую картинку как в маркетинговых матерриалах, но вполне оптимистичную

    [root@4 www]# hdparm -Tt --direct /dev/nvme1n1
    
    /dev/nvme1n1:
     Timing O_DIRECT cached reads:   2688 MB in  2.00 seconds = 1345.24 MB/sec
     Timing O_DIRECT disk reads: 4672 MB in  3.00 seconds = 1557.00 MB/sec
    
    [root@4 www]# hdparm -Tt /dev/nvme1n1
    
    /dev/nvme1n1:
     Timing cached reads:   18850 MB in  1.99 seconds = 9452.39 MB/sec
     Timing buffered disk reads: 4156 MB in  3.00 seconds = 1385.08 MB/sec
    

    Конечно же размер и производителя подбираете «под себя», можно рассматривать и SATA интерфейс, но мы же пишем о том к чему нужно стремиться :)

    Если вы все же остановили свой выбор на NVMe, для получения инфо о винте установим пакет nvme-cli и смотрим характеристики нашей «рабочей лошадки»

    [root@4 www]# nvme smart-log /dev/nvme1n1
    Smart Log for NVME device:nvme1n1 namespace-id:ffffffff
    critical_warning                               : 0
    temperature                         : 35 C
    available_spare                     : 100%
    available_spare_threshold           : 10%
    percentage_used                     : 0%
    data_units_read                     : 158 231 244
    data_units_written                  : 297 968
    host_read_commands                  : 45 809 892
    host_write_commands                 : 990 836
    controller_busy_time                : 337
    power_cycles                        : 18
    power_on_hours                      : 127
    unsafe_shutdowns                    : 14
    media_errors                        : 0
    num_err_log_entries                 : 10
    Warning Temperature Time            : 0
    Critical Composite Temperature Time : 0
    Temperature Sensor 1                : 35 C
    Temperature Sensor 2                : 27 C
    Temperature Sensor 3                : 33 C
    Temperature Sensor 4                : 35 C
    

    Как видно, винт чувствует себя прекрасно, забегая наперед скажу, что и под нагрузкой температурный режим выдерживается в таких же пределах.

    В пиковые периоды мы раздаем около 4000/фото в секунду (размер фото около 10-100K), на половине такой нагрузки iostat не поднимается более 0.1%, там еще RAM играет большое значение, но об этом еще напишу)

    Несколько слов о том, почему сейчас мы делаем ставку на дорогие NVMe, вместо мешка дешевых SATA SSD. Мы провели тесты, которые показывают, что при схожей архитектуре сервера, объемом RAM и одинаковой нагрузке, например, Ultrastar SN260 7.68TB NVMe работает с в 10 раз меньшим iowait-ом чем 8xSamsung SSD 850 PRO 1TB в Stripped RAID с Areca ARC-1882 Raid Controller PCI. На серверах есть небольшое отличие в кол-ве ядер c NVMe 26, с ARC-1882 24 ядра, количество RAM 128G там и там. К сожалению, нет возможности сравнить энергопотребление на этих серверах. Представилась возможность измерить энергопотребление NVMe платформы с аналогичной по назначению системой на процах AMD с программным Stripped RAID из 8xINTEL SSDSC2BB480G4 480G и контроллером ARC-1680 Raid Controller PCI на 24 ядрах AMD Opteron(tm) Processor 6174, при одинаковой нагрузке, новая система «жрет» в 2.5 раза меньше энергии 113 Watts против 274 Watts на AMD. Ну и загрузка CPU и iowait там тоже на порядок меньше (на AMD нет аппаратного шифрования)

    Файловая система


    8 лет назад мы использовали btrfs, пробовали еще XFS, но ext4 на большой параллельной нагрузке ведет себя живее, так что наш выбор ext4. Правильная настройка ext4, может дополнительно повысить и без того отличную производительность этой fs.
    Оптимизация начинается с момента форматирования, например, если вы, в основном, раздаете файлы 1-5K, можно немного уменьшить размер блока при форматировании:

    mkfs.ext4 -b 2048 /dev/sda1 
    

    или даже

    mkfs.ext4 -b 1024 /dev/sda1
    

    Для того чтоб узнать текущий размер блока на файловой системе используйте:

    tune2fs -l /dev/sda1 | grep Block
    

    или

    [root@4 www]# fdisk -l /dev/nvme1n1
    
    Диск /dev/nvme1n1: 7681,5 ГБ, 7681501126656 байтів, 1875366486 секторів
    Одиниці = секторів з 1 * 4096 = 4096 байтів
    Розмір сектора (логічного/фізичного): 4096 байтів / 4096 байтів
    Розмір введення-виведення (мінімальний/оптимальний): 4096 байтів / 4096 байтів
    Тип мітки диска: dos
    Ідентифікатор диска: 0x00000000
    

    Так как на нашем NVMe SSD размер сектора 4k, то размер блока делать меньше этого значения неоптимально, форматируем:

    mkfs.ext4 /dev/nvme1n1
    

    Обратите внимание, я не разбивал диск на разделы, а форматирую его целым блочным устройством. Для OS я использую другой SSD и там разбивка есть. В файловую систему его можно подмонтировать как /dev/nvme1n1

    Монтировать кеш диск желательно таким образом, чтоб выжать из него максимум по скорости, для этого все ненужное отключаем, мы будем монтировать с опцией “noatime,barrier=0”, если вам атрибут atime важен, в ядре 4 и выше есть опция lazytime, она держит значение atime в ОЗУ и частично решает проблему частых обновлений времени доступа при чтении.

    RAM


    Если ваша статика помещается в оперативную память, забудьте про все вышенаписанное и получайте удовольствие от раздачи с файлов с ОЗУ.

    При раздаче с ОЗУ, ОС сама затягивает в свой кеш часто запрашиваемые файлы и Вам не нужно для этого что-либо настраивать, но если устройство на котором хранятся файлы медленное а файлов очень много (сотни тысяч), может случится так что ОС одновременно запросит много файлов из файловой системы и ваше устройство хранения при этом начнет сильно «тормозить». Можно опробовать решить эту проблему следующим образом, загрузить статику на RAM-диск и раздавать с него.

    Пример создания RAM-диска:

    mount -t tmpfs -o size=1G,mode=0700,noatime tmpfs /cache
    

    если вы забыли с какими параметрами монтировали, то можно посмотреть с помощью findmnt:

    findmnt --target /cache
    


    Можно перемонтировать без перезагрузки:

    mount -o remount,size=4G,noatime /cache
    

    Можно также комбинировать часть в RAM (какие-нибудь часто часто запрашиваемые превьюшки) остальное на SSD.

    В nginx это будет выглядеть как-то так:

     location / {
        root /var/cache/ram;
        try_files $uri @cache1;
     }
    
     location @cache1 {
        root /var/cache/ssd;
        try_files $uri @storage;
     }
    

    Если контент в RAM не влезает, придется довериться ядру, мы ставим 128G RAM при размере активного кеша 3-5G и подумываем про увеличение до 256G.

    CPU


    Особых требований к частоте процессора нет, скорее есть требование к функциональности: если ваш трафик необходимо будет шифровать (например, по протоколу https), важно выбрать такой процессор, который будет поддерживать аппаратное шифрование AES-NI (Intel Advanced Encryption Standard).

    В Linux проверить поддержку процессором инструкций AES-NI можно командой

    grep -m1 -o aes /proc/cpuinfo
    aes
    

    Если нет вывода “aes”, то процессор такие инструкции не поддерживает и шифрование будет пожирать производительность процессора.

    Настраиваем nginx


    Общие идеи описаны в предыдущей статье, но все же есть несколько моментов по оптимизации, про них раскажу сейчас:

    • Мы избегаем реврайтов на верхнем уровне директивы server, не приветствуем регулярные выражения в секциях location. Если без этого никак, то постарайтесь хотя бы обернуть регулярку в обычный location, например:

              location /example/dir {
                  rewrite ^/example/dir(.*) /newexample/$1;
              }
      

      Если этого не сделать, то регулярка будет применяться для каждого запроса к системе раздачи, а в нашем примере примерка регулярки редиректа будет происходить только в случае, когда путь к фото начинается на /example/dir
    • При хранении контента в кеше придерживайтесь правила не хратить более 255 файлов или папок в одной папке (если форматировали диск с настройками “по-умолчанию” с размером блока 4K), 128 если 2K и т.д.
    • Ставим новое ядро, желательно не ниже 4.1. В nginx не забываем включить поддержку SO_REUSEPORT Эта директива положительно влияет на параллельную загрузку файлов с сервера раздачи.
    • Размещаем сервер поближе к вашим пользователям, таким образом мы делаем счастливее пользователей и про это знают и это ценят поисковые системы

    Как насчет CDN


    CDN это хорошо, если вы работаете на разные страны, у вас много контента и мало трафика, но если трафика много, контента мало, и вы работаете на конкретную страну, то есть смысл все просчитать и понять, что Вам выгоднее. Например, мы работем на Украинский рынок, у многих мировых лидеров, предоставляющих сервис CDN, серверов в Украине нет и раздача идет с Германии или Польши. Вот и получаем вместо +3-5ms — +30-50ms к ответу на ровном месте. Размещение сервера 2U в хорошем украинском ДЦ стартует от 18$ + оплата за канал, например, 100Mbps — 10$ итого 28$. Коммерческая цена раздачи CDN-трафика в Украине около 0.05$/G, т.е. если раздаем более 560G/месяц, то уже можно рассматривать вариант самостоятельной раздачи. Сервисы RIA.com раздают несколько террабайт статики в день, и поэтому мы уже давно приняли решения о самостоятельной раздаче.

    Как дружить с поисковиками


    Для многих поисковиков важными характеристиками являются TTFB (time to first byte) и то насколько «близко» контент расположен к тому, кто его ищет, кроме того, важны тексты в ссылках на контент, описание в Exif-тегах, уникальность, размер контента и т.д.
    Все о чем, я здесь пишу, в основном для того, чтоб протюнить TTFB и быть поближе к пользователю. Можно пойти на хитрость и по User-Agent-у детектить поисковых ботов и отдавать для них контент с отдельного сервера чтоб исключить «заторы» или «притормаживания в пиковые периоды» (обычно боты дают равномерную нагрузку), таким образом делая счастливыми не пользователя а поисковики. Мы так не делаем, кроме того есть подозрение что Гугл Хром и Яндекс Браузер больше доверяют той информации, которую им поставляют браузеры о скорости загрузки Ваших страниц с позиции клиента.

    Также стоит отметить, что нагрузка от разных ботов может быть настолько существенной, что вам придется тратить чуть ли не половину ресурсов на обслуживание этих ботов. Проекты RIA.com обслуживают около 10-15 миллионов запросов от ботов с сутки (сюда включены обращения не только к статике но и к обычным страницам) это не намного меньше, чем количество запросов от реальных пользователей.

    Оптимизация для раздачи статического контента


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

    • Первое, на что стоит обратить внимание — это формат фото: оказывается, что jpeg, который очень популярен для многих проектов уже уступает по размеру при сравнимом качестве более новым форматам в Web, например формат WebP который продвигает компания Google с 2010 года. По разным данным получаем на 20-30% меньше размер, при равном качестве. На клиенте при этом можно использовать также специальный тег , который позволяет описать в себе несколько форматов, которые может отобразить браузер и в случае если браузер не поддерживает формат WebP он загрузит, например, jpeg
      Также немного о SEO требованиях:

      • Часть SEO-оптимизации вы уже сделали тем, что разместили сервер раздачи поближе к клиенту и ускорили его отдачу
      • Несколько слов о exif-тегах, многие их вырезают при масштабировании фото — напрасно! Гугл тоже анализирует эту информацию, почему бы не указать для фото то, что изображено на фото в exif-теге ImageDescription, или в exif-теге Copyright не вписать авторские права на свой контент, если, конечно он Ваш :)
      • Не забывайте про HTTP-заголовки, в которых указывается разная полезная мета-информация о файле контента. Например, с помощью заголовка Expires, можно указать время хранения контента в кеше браузера.

      Если ваш сайт работает по протоколу HTTP/2, то стоит проэкспериментировать со следующими оптимизациями:

      • Вы можете отказаться от использования css-спрайтов, так как мультиплексирование передачи маленьких файлов по одному соединению может компенсировать выиграш, который обычно достигается по http/1.1 при использовании этой оптимизации
      • попробуйте технологию web-push, обратите внимание, что такой метод оптимизации загрузки не учитывает наличие в кеше браузера контента, который пушится, но это решается использованием куков и несложной настройкой nginx, как показано в примере

        server {
            listen 443 ssl http2 default_server;
        
            ssl_certificate ssl/certificate.pem;
            ssl_certificate_key ssl/key.pem;
        
            root /var/www/html;
            http2_push_preload on;
        
            location = /demo.html {
                add_header Set-Cookie "session=1";
                add_header Link $resources;
            }
        }
        
        map $http_cookie $resources {
            "~*session=1" "";
            default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, 
                     </image2.jpg>; as=style; rel=preload";
        }
        

      • Из нашего опыта, раздачу контента лучше осуществлять с именем домена отличным от основного домена проекта.

      Загадочный HTTP/2


      Как вы знаете HTTP/2 мультиплексирует одно соединение, через которое передается несколько файлов, при этом возможна приоретизация в отправке того или иного файла, сжатие заголовков и другие плюсы нового протокола, но есть и минусы, о которых мало кто пишет. Зайду издалека: может есть кто постарше из хабражителей, кто помнит интернет до эпохи uTorrent, многим из вас приходилось использовать специальные качалки flashget, download master и др. Помните, как они работали? Они качали один файл в 6 или 8 потоков, открывая 6-8 соединений с отдающим сервером. Почему они так делали? Ведь канал с раздающим и принимающим сервером не должен зависеть от количества соединений между ними, но на самом деле эта зависимость есть, если канал плохой, с потерей пакетов и ошибками передачи пакетов. Оказывается, что при таких раскладах, в несколько потоков качается быстрее. Кроме того если канал используется несколькими клиентами то увеличение количества соединений от одного клиента помогает получать больше полосу в канале и перетягивать «одеяло ресурсов» на себя. Конечно, так происходит не всегда, но все же есть угроза получить «загребущего» конкурента в виде браузера работающего по протоколу http/1.1, который откроет 6 конекшинов к одному сайту, вместо 1 по http2. В моей практике был случай с сайтом типа «фотохостинг обоев для рабочего стола», который отказался от http/2, по той причине что сайт на http/2 протоколе заметно тормозил, без видимой нагрузки на самом сервере, ребята остались на https, но перешли на http/1.1 и ситуация разрешилась.

      Я бы еще поэкспериментировал с получением контента с разных доменов (но фактически с одного сервера), этот прием называется domain sharding, возможно как можно было выйти из ситуации не отказываясь от http/2 и принуждая устанавливать браузеру столько соединений сколько нужно администратору сайта.

      Вместо заключения


      Тот момент, когда сайт начинает подтормаживать нельзя назвать неприятным, ведь вы чувствуете что трафик растет, клиентов становиться больше. Мы никогда не ставили перед собой задачу избежать “тормозов”, мы учились быстро реагировать на этот рост. Оптимизация быстродействия — это процесс бесконечный, не отказывайте себе в удовольствии посоревноваться со своими конкурентами в искусстве быть быстрыми!
    • +13
    • 6,9k
    • 6
    Поделиться публикацией

    Комментарии 6

      +1
      Помню, RIA был очень тормозной, когда активно пользовался два года назад. Сейчас намного лучше. Наверное, работают советы :)
        0
        Там javascript тормозил :)
        0
        Понятно, что стоимость потребительских SSD ниже, как и их скорость, тоже ниже, однако со стороны бизнеса должны быть требования по скорости загрузки файлов и т.п., действительно ли они не выполнялись на
        8xINTEL SSDSC2BB480G4 480G
        Или выполнялись, но было бабло? :)
        Я бы еще вспомнил что контроллер ARC-1882 сам по себе является узким местом, он малопроизводительный для такой конфигурации, превращает в ssd с 100к iops каждая в RAID с масксимум 60к iops всего, да и латенси его довольно велик. Еще бы через адаптек 6805 ssd подключили.
          0
          Понятно, что стоимость потребительских SSD ниже, как и их скорость, тоже ниже, однако со стороны бизнеса должны быть требования по скорости загрузки файлов и т.п., действительно ли они не выполнялись на 8xINTEL SSDSC2BB480G4 480G

          Не выполнялись, по нескольким причинам:
          1. Это старый сервер, который постепенно апгрейдили, на разных этапах пробовали разные конфигурации. Вначале было 4 SSD в stripped raid без RAID-контроллера. Это работает плохо, так как RAID-контроллер с батарейкой это снижение рисков развала RAID-масива (с такой проблемой столнулись).
          2. Потом собрали то что есть но пожалели денег на быстрий RAID-контроллер и, возможно, небольшие iops-ы отчасти и его рук дело.
          3. Нету аппаратного шифрования, это тоже проблема, держать прожорливые процы только чтоб шифровать трафик — неоптимально и если дать на них большую нагрузку то и процы начнут тормозить и iowait поползет вверх. (Процессоры можно озадачить более полезной нагрузкой, например, предпорезкой разных форматов фоток)
          4. Возможно можно было бы компенсировать добавлением RAM-а, так там уже нету свободных слотов.
          5. Есть еще одна неприятная особенность, система начинает тормозить не линейно, например нагрузка возросла на 5% а скорость отдачи снизилась на 50% (я кончно, утрирую, но суть, я надеюсь, понятна)
          6. На сервере всегда должен быть резерв, на случай выхода из строя друго-го кеш-сервера.
          Или выполнялись, но было бабло? :)

          Очень быстро никогда не бывает, если у Вас iowai не 0 значит диск иногда тормозит.

          Я бы еще вспомнил что контроллер ARC-1882 сам по себе является узким местом, он малопроизводительный для такой конфигурации, превращает в ssd с 100к iops каждая в RAID с масксимум 60к iops всего, да и латенси его довольно велик. Еще бы через адаптек 6805 ssd подключили.

          Да тут спору нет, почему ставили Araca я уже писал, просто NVMe дискам не нужен RAID-контроллер и это прекрасно!
          0
          При раздаче с ОЗУ, я не надеюсь на ОС, которая помещает в память то, что считает нужным и монтирую раздел со статикой на RAM-диск

          Есть результаты тестов или это бездумное следование городским легендам?

            0
            Когда речь идет о малом количестве файлов и папок, то разницы не будет. Но когда файлов очень много и винт не быстрый, т.е. есть ощутимый iowait то загрузка в ОЗУ может помочь.

            Простой эксперимент:
            Берем папку 100 000 файлов, для того чтоб ее сгенерить используем скрипт, например такой:
            #!/bin/bash
            
            # Init variables
            EXAMPLEFILE=./example.png
            FILESCOUNT=100000
            CACHE_DIR=/home/cache
            URL="http://imagerequests"
            
            for i in `seq 0 $FILESCOUNT`; do
                let "X = i % 100"
                let "D = i - X"
                let "L1 = D/100"
            
                let "X = i % 1000"
                let "D = i - X"
                let "L2 = D/1000"
            
                TARGET_DIR=$CACHE_DIR/$L2/$L1
            ....
                if [ ! -d $TARGET_DIR ]; then
                   mkdir -p $TARGET_DIR
                fi
                if [ ! -f $TARGET_DIR/$i.png ]; then
                   cp $EXAMPLEFILE $TARGET_DIR/$i.png
                fi
                echo "$URL/$L2/$L1/$i.png"
            done
            


            При этом на стандартном выводе формируется список URL-ов, кладем его в /etc/siedge/urls.txt.

            У меня вся папка получилась окло 70M, полный размер на FS около 400M

            Примонтировал так
            mount -t tmpfs -o size=1G,noatime,async tmpfs /home/ram
            


            Запускаем sedge:
            siege -c 1000 -t 10s -i -b
            


            Для диска
            Transactions: 52106 hits
            Availability: 100.00 %
            Elapsed time: 9.40 secs
            Data transferred: 38.69 MB
            Response time: 0.02 secs
            Transaction rate: 5543.19 trans/sec
            Throughput: 4.12 MB/sec
            Concurrency: 134.60
            Successful transactions: 52341
            Failed transactions: 0
            Longest transaction: 2.10
            Shortest transaction: 0.00


            Для RAM:
            Transactions: 102892 hits
            Availability: 100.00 %
            Elapsed time: 9.04 secs
            Data transferred: 76.22 MB
            Response time: 0.00 secs
            Transaction rate: 11381.86 trans/sec
            Throughput: 8.43 MB/sec
            Concurrency: 15.30
            Successful transactions: 103132
            Failed transactions: 0
            Longest transaction: 0.32
            Shortest transaction: 0.00


            Понятно, что когда все файлы загрузятся в RAM из fs то время сравниться. Ну а если файлов будет пару миллионов, то с файловой системы все может и не взлететь, а рам задосить невозможно.

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

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

          Самое читаемое