Статьи

Производительность Apache против Nginx: методы оптимизации

Несколько лет назад веб-сервер Apache Foundation , известный просто как «Apache», был настолько повсеместным, что стал синонимом термина «веб-сервер». Его демон-процесс в системах Linux имеет имя httpd (что означает просто http-процесс ) и предустанавливается в основных дистрибутивах Linux.

Первоначально он был выпущен в 1995 году, и, по словам Википедии , «он сыграл ключевую роль в начальном росте Всемирной паутины» . Согласно W3techs, это все еще наиболее используемое программное обеспечение для веб-серверов. Однако, согласно тем отчетам, которые показывают некоторые тенденции последнего десятилетия и сравнение с другими решениями , его доля на рынке уменьшается. Отчеты Netcraft и Builtwith немного отличаются, но все согласны с тенденцией к снижению доли рынка Apache и росту Nginx.

Nginx — произносится двигатель x — был выпущен в 2004 году Игорем Сысоевым с явным намерением превзойти Apache. На сайте Nginx есть статья, которую стоит прочитать, в которой сравниваются эти две технологии. Сначала он в основном использовался в качестве дополнения к Apache, в основном для обслуживания статических файлов, но он неуклонно растет, так как развивается для решения всего спектра задач веб-сервера.

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

Apache существует уже давно, и у него большой выбор модулей . Известно, что управление серверами Apache удобно для пользователя. Динамическая загрузка модулей позволяет компилировать и добавлять различные модули в стек Apache без перекомпиляции двоичного файла главного сервера. Зачастую модули будут находиться в репозиториях Linux distro, и после их установки через менеджеры системных пакетов их можно будет аккуратно добавить в стек с помощью таких команд, как a2enmod . Такого рода гибкость еще не видна с Nginx. Когда мы рассмотрим руководство по настройке Nginx для HTTP / 2 , модули — это то, с чем Nginx должен быть собран — настроен во время сборки.

Еще одна особенность, которая внесла свой вклад в рыночное правило Apache, — это файл .htaccess . Это серебряная пуля Apache, которая сделала его идеальным решением для сред общего хостинга, поскольку оно позволяет контролировать конфигурацию сервера на уровне каталогов. Каждый каталог на сервере, обслуживаемом Apache, может иметь свой собственный файл .htaccess .

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

Статистика общего ресурса сервера, Netcraft

Доля рынка серверов в 1995–2005 гг. Данные по Netcraft

LiteSpeed , или LSWS, является одним из претендентов на сервер, который обладает уровнем гибкости, который можно сравнить с Apache, но при этом не жертвуя производительностью. Он поддерживает Apache-стиль .htaccess , mod_security и mod_rewrite , и его стоит рассмотреть для общих настроек. Он был задуман как замена Apache и работает с cPanel и Plesk. Он поддерживает HTTP / 2 с 2015 года.

LiteSpeed ​​имеет три уровня лицензии : OpenLiteSpeed, LSWS Standard и LSWS Enterprise. Standard и Enterprise поставляются с дополнительным решением для кэширования, сравнимым с Varnish, LSCache, которое встроено в сам сервер и может управляться с помощью правил перезаписи в файлах .htaccess (для каждого каталога). Он также поставляется с некоторыми встроенными «батареями», смягчающими DDOS . Это, наряду с его архитектурой, управляемой событиями, делает его надежным конкурентом, ориентируясь в первую очередь на хостинг-провайдеров , ориентированных на производительность , но это может стоить настроить даже для небольших серверов или веб-сайтов.

Вопросы аппаратного обеспечения

Оптимизируя нашу систему, мы не можем выделить достаточно, уделяя должное внимание настройке нашего оборудования. Какое бы из этих решений мы ни выбрали для нашей установки, наличие достаточного объема ОЗУ имеет решающее значение. Когда процесс веб-сервера или интерпретатор, такой как PHP, не имеют достаточно оперативной памяти, он начинает подмену, и подкачка эффективно означает использование жесткого диска для пополнения оперативной памяти. Результатом этого является увеличение задержки каждый раз, когда к этой памяти обращаются. Это подводит нас ко второму пункту — месту на жестком диске. Использование быстрого SSD-хранилища является еще одним критическим фактором скорости нашего сайта. Нам также необходимо учитывать доступность ЦП и физическое расстояние центров обработки данных нашего сервера до предполагаемой аудитории.

Чтобы глубже погрузиться в аппаратную сторону настройки производительности, у Dropbox есть хорошая статья .

Мониторинг

Htop , который работает в Linux, Unix и macOS и дает нам цветной обзор наших процессов, является одним из практических способов контроля текущей производительности нашего стека серверов.

HTOP

Другими инструментами мониторинга являются New Relic , решение премиум-класса с обширным набором инструментов, и Netdata , решение с открытым исходным кодом, которое предлагает отличную расширяемость, детализированные метрики и настраиваемую веб-панель мониторинга, подходящую как для небольших систем VPS, так и для мониторинга сети. серверов. Он может отправлять сигналы тревоги для любого приложения или системного процесса по электронной почте, Slack, pushbullet, Telegram, Twilio и т. Д.

Панель инструментов Netdata

Monit — это еще один, безголовый инструмент с открытым исходным кодом, который может контролировать систему и может быть настроен на оповещение нас, или перезапуск определенных процессов, или перезагрузку системы при выполнении некоторых условий.

Тестирование системы

AB — Apache Benchmark — простой инструмент нагрузочного тестирования от Apache Foundation, а Siege — еще одна программа нагрузочного тестирования. В этой статье объясняется, как их настроить, и здесь у нас есть несколько более сложных советов для AB, а подробное описание Siege можно найти здесь .

Если вы предпочитаете веб-интерфейс, есть Locust , инструмент на основе Python, который очень удобен для тестирования производительности сайта.

Саранча установка

После того, как мы установим Locust, нам нужно создать файл locust в каталоге, из которого мы его запустим :

 from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 

Затем мы просто запускаем его из командной строки:

 locust --host=https://my-website.com 

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

Тюнинг Apache

Модули Apache mpm

Apache датируется 1995 годом и ранними днями появления Интернета, когда принятым способом работы серверов было создание нового процесса на каждом входящем TCP-соединении и ответ на него. Если появилось больше соединений, было создано больше рабочих процессов для их обработки. Затраты на порождение новых процессов были высоки, и разработчики Apache разработали режим prefork с заранее созданным числом процессов. Встроенные динамические языковые интерпретаторы в каждом процессе (например, mod_php ) все еще были дорогостоящими, и сбой сервера с настройками Apache по умолчанию стал обычным явлением. Каждый процесс мог обрабатывать только одно входящее соединение.

Эта модель известна как mpm_prefork_module в системе Apache MPM (Multi-Processing Module). Согласно веб-сайту Apache , этот режим требует небольшой настройки, поскольку он саморегулируемый, и наиболее важным является то, что директива MaxRequestWorkers должна быть достаточно большой, чтобы обрабатывать столько одновременных запросов, сколько вы ожидаете получить, но достаточно малой, чтобы обеспечить достаточно физической оперативной памяти. для всех процессов .

libapache2-mod-php7 mpm_prefork HTOP отчет

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

Мы можем добавить, что этот режим, возможно, является основной причиной плохого имени Apache. Это может стать неэффективным с точки зрения ресурсов.

Версия 2 Apache принесла еще два MPM, которые пытаются решить проблемы, возникающие в режиме prefork . Это рабочий модуль , или mpm_worker_module , и модуль событий .

Рабочий модуль больше не основан на процессах; это гибридный режим работы, основанный на потоке процессов. Цитируя сайт Apache ,

один процесс управления (родитель) отвечает за запуск дочерних процессов. Каждый дочерний процесс создает фиксированное число потоков сервера, как указано в директиве ThreadsPerChild , а также поток слушателя, который прослушивает соединения и передает их потоку сервера для обработки, когда они поступают.

Этот режим более ресурсоэффективен.

2.4 версия Apache принесла нам третий модуль MPM- событий . Он основан на рабочем MPM и добавил отдельный поток прослушивания, который управляет неактивными соединениями keepalive после завершения HTTP-запроса. Это неблокирующий асинхронный режим с меньшим объемом памяти. Подробнее об улучшениях версии 2.4 здесь .

Мы загрузили тестовую установку WooCommerce с 1200 постами на виртуальном сервере и протестировали ее на Apache 2.4 со стандартным режимом, режимом prefork и mod_php.

Сначала мы протестировали его с помощью libapache2-mod-php7 и mpm_prefork_module по адресу https://tools.pingdom.com:

Тест MPM Prefork

Затем мы пошли на тестирование модуля MPM события.

Нам пришлось добавить multiverse в наш /etc/apt/sources.list :

 deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner 

Затем мы выполнили sudo apt-get update и установили libapache2-mod-fastcgi и php-fpm:

 sudo apt-get install libapache2-mod-fastcgi php7.0-fpm 

Поскольку php-fpm — это служба, отдельная от Apache, требуется перезапуск:

 sudo service start php7.0-fpm 

Затем мы отключили модуль prefork, включили режим событий и proxy_fcgi:

 sudo a2dismod php7.0 mpm_prefork sudo a2enmod mpm_event proxy_fcgi 

Мы добавили этот фрагмент к нашему виртуальному хосту Apache:

 <filesmatch "\.php$"> SetHandler "proxy:fcgi://127.0.0.1:9000/" </filesmatch> 

Этот порт должен соответствовать конфигурации php- /etc/php/7.0/fpm/pool.d/www.conf в /etc/php/7.0/fpm/pool.d/www.conf . Подробнее о настройке php-fpm здесь .

Затем мы настроили конфигурацию mpm_event в /etc/apache2/mods-available/mpm_event.conf , имея в виду, что наши ресурсы мини-VPS для этого теста были ограничены — поэтому мы просто сократили некоторые значения по умолчанию. Подробная информация о каждой директиве на веб-сайте Apache и советы, относящиеся к событию mpm, здесь . Имейте в виду, что запущенные серверы потребляют объем памяти независимо от того, насколько они заняты. Директива MaxRequestWorkers устанавливает ограничение на число разрешенных одновременных запросов: MaxConnectionsPerChild установить для параметра MaxConnectionsPerChild значение, отличное от нуля, поскольку оно предотвращает возможную утечку памяти.

 <ifmodule mpm_event_module> StartServers 1 MinSpareThreads 30 MaxSpareThreads 75 ThreadLimit 64 ThreadsPerChild 30 MaxRequestWorkers 80 MaxConnectionsPerChild 80 </ifmodule> 

Затем мы перезапустили сервер с помощью sudo service apache2 restart (если мы изменим некоторые директивы, такие как ThreadLimit, нам нужно будет явно остановить и запустить службу с sudo service apache2 stop; sudo service apache2 start ).

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

Тест Pingdom

Другие советы по настройке Apache:

Отключение .htaccess : htaccess позволяет установить конкретную конфигурацию для каждого отдельного каталога в корне нашего сервера без перезагрузки. Таким образом, обход всех каталогов, поиск файлов .htaccess при каждом запросе влечет за собой снижение производительности.

Цитата из документов Apache:

Как правило, файлы .htaccess следует использовать только в том случае, если у вас нет доступа к главному файлу конфигурации сервера. *
… В общем, по возможности следует избегать использования файлов .htaccess . Любая конфигурация, которую вы бы хотели поместить в файл .htaccess , может быть также эффективно выполнена в разделе <directory> в вашем файле конфигурации основного сервера. *

Решение состоит в том, чтобы отключить его в /etc/apache2/apache2.conf :

 AllowOverride None 

Если нам это нужно для конкретных каталогов, мы можем включить его в разделы в наших файлах виртуального хоста:

 AllowOverride All 

Дополнительные советы включают в себя:

  • Управляйте кэшем браузера с помощью mod_expires — устанавливая заголовки expires.

  • Держите HostNameLookups выключенным — HostNameLookups Off — это значение по умолчанию, начиная с Apache 1.3, но убедитесь, что оно отключено, потому что это может повлечь за собой снижение производительности.

  • Apache2buddy — это простой скрипт, который мы можем запустить и получить советы по настройке нашей системы: curl -sL https://raw.githubusercontent.com/richardforth/apache2buddy/master/apache2buddy.pl | perl curl -sL https://raw.githubusercontent.com/richardforth/apache2buddy/master/apache2buddy.pl | perl

apache2buddy

Nginx

Nginx — это управляемый событиями и неблокирующий веб-сервер. Чтобы процитировать один постер в Hacker News ,

Форкинг процессов невероятно дорог по сравнению с циклом обработки событий. HTTP-серверы на основе событий неизбежно побеждают.

Это заявление вызвало споры о Hacker News, но, как показывает наш опыт, простое переключение с Apache mpm_prefork на Nginx часто может означать спасение сайта от сбоев. Простое переключение на Nginx очень часто является лекарством само по себе.

Архитектура Nginx

Более подробное визуальное объяснение архитектуры Nginx можно найти здесь .

Настройки Nginx

Nginx рекомендует привязать количество рабочих к числу ядер ПК (так же, как мы это делали с конфигурацией apache mpm_event), установив для worker_processes значение auto (по умолчанию 1) в /etc/nginx/nginx.conf .

worker_connections устанавливает количество соединений, которые может обработать каждый рабочий процесс. По умолчанию 512, но обычно его можно увеличить.

Keepalive соединения — это аспект сервера, который влияет на производительность, что обычно не видно в тестах .

keepalive.cf

По данным сайта Nginx ,

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

Установление новых соединений TCP может быть дорогостоящим — не говоря уже о том, когда используется шифрование HTTPS . Протокол HTTP / 2 смягчает это благодаря своим функциям мультиплексирования . Повторное использование существующего соединения может сократить время запроса.

Mpm_prefork и mpm_worker в Apache страдают от ограничений параллелизма, которые контрастируют с циклом событий keepalive. Это несколько исправлено в Apache 2.4, в модуле mpm_event, и является единственным режимом работы по умолчанию в Nginx. Работники Nginx могут обрабатывать тысячи входящих соединений одновременно, и если он используется в качестве обратного прокси-сервера или балансировщика нагрузки, Nginx затем использует локальный пул keepalive-соединений без накладных расходов на TCP-соединение.

keepalive_requests — это параметр, который регулирует количество запросов, которые клиент может сделать через одно соединение keepalive.
keepalive_timeout устанавливает время, когда простое соединение keepalive остается открытым.

keepalive — это параметр, относящийся к соединению Nginx с вышестоящим сервером — когда он действует как прокси или балансировщик нагрузки. Это означает количество неактивных восходящих соединений keepalive на рабочий процесс.

Включение восходящих соединений keepalive требует помещения этих директив в основную конфигурацию Nginx:

 proxy_http_version 1.1; proxy_set_header Connection ""; 

Входящие соединения Nginx управляются модулем ngx_http_upstream_module .

Если наше приложение продолжает опрашивать наше приложение на предмет обновлений, увеличение keepalive_requests и keepalive_timeout ограничит количество соединений, которые необходимо установить. Директива keepalive не должна быть слишком большой, чтобы позволить другим соединениям достигать нашего вышестоящего сервера.

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

Использование unix сокетов

По умолчанию Nginx использует отдельный процесс PHP, в который он перенаправляет запросы файлов PHP. В этом он действует как прокси (точно так же как Apache, когда мы настраиваем его с php7.0-fpm).

Часто наша установка виртуального хоста с Nginx будет выглядеть так:

 location ~ \.php$ { fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; } 

Поскольку FastCGI — это протокол, отличный от HTTP, первые две строки пересылают некоторые аргументы и заголовки в php-fpm, а третья строка указывает способ прокси нашего запроса — через сокет локальной сети.

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

Но если мы размещаем всю нашу установку в одной системе, мы должны использовать сокет Unix для подключения к процессу прослушивания php:

 fastcgi_pass unix:/var/run/php7.0-fpm.sock; 

Считается, что сокеты Unix имеют лучшую производительность, чем TCP , и эта настройка считается более безопасной. Вы можете найти более подробную информацию об этой установке в этой статье Rackspace .

Этот совет, касающийся сокетов Unix, также применим для Apache. Подробнее здесь .

gzip_static : общепринятое мнение о производительности веб-сервера заключается в сжатии наших статических ресурсов. Это часто означает, что мы попытаемся пойти на компромисс и попытаться сжать только те файлы, которые превышают некоторый порог, поскольку сжатие ресурсов на лету при каждом запросе может быть дорогостоящим. Nginx имеет директиву gzip_static которая позволяет нам обслуживать сжатые версии файлов — с расширением .gz — вместо обычных ресурсов:

 location /assets { gzip_static on; } 

Таким образом, Nginx будет пытаться обслуживать style.css.gz вместо style.css (в этом случае мы должны позаботиться о style.css ).

Таким образом, циклы ЦП не будут тратиться на сжатие «на лету» для каждого запроса.

Кеширование с помощью Nginx

Рассказ о Nginx не будет полным без упоминания о том, как кэшировать контент. Кэширование Nginx настолько эффективно, что многие системные администраторы не думают, что отдельные уровни для кэширования HTTP, такие как Varnish, имеют большой смысл. Возможно, это менее сложный, но простота является особенностью . Включить кеширование с помощью Nginx довольно просто.

 proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m; 

Это директива, которую мы помещаем в наш файл виртуального хоста, вне блока server . Аргумент proxy_cache_path может быть любым путем, в котором мы хотим сохранить наш кэш. levels определяют, сколько уровней каталогов Nginx должен хранить в кэшированном контенте. Из соображений производительности обычно приемлемы два уровня. Повторение через каталоги может быть дорогостоящим. Аргумент keys_zone — это имя зоны совместно используемой памяти, используемой для хранения ключей кэша, и 10m — это место для этих ключей в памяти (обычно достаточно 10MB; это не место для реального кэшированного содержимого). max_size является необязательным и устанавливает верхний предел для кэшируемого содержимого — здесь 10 ГБ. Если это не указано, оно займет все доступное пространство. inactive указывает, как долго контент может оставаться в кеше без запроса до его удаления Nginx.

Установив это, мы добавили бы следующую строку с именем нашей зоны памяти в server или в блок location :

 proxy_cache my_cache; 

Дополнительный уровень отказоустойчивости с Nginx может быть достигнут, сказав ему обслуживать элементы из кэша, когда он сталкивается с ошибкой сервера в источнике, или в вышестоящем сервере, или когда сервер не работает:

 proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504; 

Более подробную информацию о директивах server или блока location для дальнейшей настройки кэширования Nginx можно найти здесь .

Директивы proxy_cache_* предназначены для статических ресурсов, но мы обычно хотим кэшировать динамический вывод наших веб-приложений — будь то CMS или что-то еще. В этом случае мы будем использовать директиву fastcgi_cache_* вместо proxy_cache_* :

  fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=my_cache:10m inactive=60m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; fastcgi_cache_use_stale error timeout invalid_header http_500; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; add_header NGINX_FASTCGI_CACHE $upstream_cache_status; 

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

Затем в нашем сервере или блоке местоположения мы можем установить некоторые исключения для кэширования — например, когда строка запроса присутствует в URL запроса:

 if ($query_string != "") { set $skip_cache 1; } 

Кроме того, в нашем блоке \.php внутри server , в случае PHP, мы добавили бы что-то вроде:

 location ~ \.php$ { try_files $uri =404; include fastcgi_params; fastcgi_read_timeout 360s; fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php7.0-fpm.sock; fastcgi_index index.php; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache my_cache; fastcgi_cache_valid 60m; } 

Выше строки fastcgi_cache* и fastcgi_no_cache регулируют кэширование и исключения.
Подробные ссылки на все эти директивы можно найти на веб-сайте Nginx docs .

Чтобы узнать больше, сотрудники Nginx организовали бесплатный вебинар на эту тему, и есть несколько электронных книг .

Вывод

Мы попытались представить некоторые методы, которые помогут нам улучшить производительность нашего веб-сервера, и теорию, лежащую в основе этих методов. Но эта тема никоим образом не исчерпана: мы до сих пор не рассмотрели настройки обратного прокси-сервера, состоящие из Apache и Nginx, или многосерверных установок. Достижение наилучших результатов на обоих этих серверах — это вопрос тестирования и анализа конкретных реальных случаев. Это своего рода бесконечная тема.