Установка и настройка Nginx (LNMP) на Ubuntu

August 04, 2016

В 2002 году российский программист Игорь Сысоев начал разработку HTTP-сервера Nginx, в связи с потребностью в новом программном обеспечении для высоконагруженных сайтов. Сейчас, помимо обычного веб-сервера, Nginx используется, как обратный или почтовый прокси-сервер, а также, как TCP/UDP прокси-сервер для решения общих задач. Nginx настраивается и эффективно работает на всех основных Unix-подобных ОС, в том числе на Linux.

В июле 2016 года компания Netcraft проанализировала ответы более миллиарда загруженных сайтов и сообщила, что Nginx проксирует или обслуживает 27,9% из них. Достоинства Nginx по праву оценили такие крупные компании, как Вконтакте, Рамблер, Яндекс и Mail.ru.

В начале своей карьеры разработчик Nginx использовал серверы Apache для нужд компании Рамблер. Сысоев пытался оптимизировать Apache под запросы крупных и быстро растущих сайтов, например, создал патч, сжимающий (регулирующий) ответы сервера. В большинстве случаев устранить недостатки Apache не удавалось, поэтому началась работа над Nginx.

В чем была проблема с Apache? Apache создает отдельные треды (thread) для каждого события, поэтому не подходит для решения проблемы С10К. При обработке большого количества соединений, веб-сервер начинает перегружать вычислительные мощности, что приводит к нарушениям в работе. В свою очередь, Nginx использует неблокирующие асинхронные алгоритмы обработки событий и стабильно справляется с обработкой более 10 000 рабочих процессов одновременно. Поэтому построенные на Nginx проекты куда меньше страдают от вызванных перегрузкой проблем, чем аналогичные решения на Apache.

Apache и Nginx: наглядное сравнение

Чтобы более подробно рассмотреть разницу между Apache и Nginx проведем тестирование скорости работы серверов при решении нескольких типовых задач. 

Для тестов работы Apache была создана удобная однопоточная программа Apache Bench (AB). Эту программу можно использовать для тестирования любого HTTP веб-сервера, в том числе и для Nginx. Установим Apache Benchmark с помощью команды:

sudo apt-get install apache2-utils

Утилита ab имеет следующий синтаксис:

ab [options] [http[s]://]hostname[:port]/path

Из опций как правило указываются количество конкурентных запросов (потоков) -с и общее количество запросов -n, например:

ab -c 10 -n 100 http://example.com/

-  100 общих запросов в 10 потоков.

С помощью Apache Benchmark были проведены сравнительные тесты быстродействия веб-сервера Apache и связки Nginx+PHP-FPM для трёх различных скриптов:

1. index.html - статичная веб-страница: 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
<p>Hello i am main!</p>
    </body>
</html>

 2. Простой php-скрипт phpinfo.php, выводящий информацию о настройках php на сервере:

<?php phpinfo(); ?>

3. “Тяжелый” php-скрипт speedtest.php, который исполняет алгоритм наивного поиска простых чисел, дополнительно усложненный алгоритмом работы через комплексные числа для искусственного увеличения нагрузки на вычислительные мощности сервера: 

<?php
class Complex {
    public $real = 0;
    private $imag = 0;
    public function __construct($real, $imag = null) {
        $this->real = (float)$real;
    }
    public function __toString() {
        return sprintf("(%d+%dj)", $this->real, $this->imag);
    }
}
 
$start = microtime(TRUE);
$primeNumbers = array();
$output = '';
 
for ($i = 2; $i < 100000; $i++) {
    $divisible = false;
    $in = new Complex($i);
    foreach($primeNumbers as $number) {
        if ($in->real % $number->real == 0) {
            $divisible = true;
            break;
        }
    }
    if ($divisible == false){
        $primeNumbers[] = $in;
        $output .= $in;
    }
}
?>

Результаты тестов представлены в таблице в формате количество запросов в секунду / время на обработку одного запроса:

Файл Nginx Apache
index.html 4373.21 / 45.733 мс 3582.10 / 55.833 мс
phpinfo.php 3424.28 / 58.406 мс 1253.39 / 79.784 мс
speedtest.php 0.19 / 8925.181 мс 0.25 / 7863.261 мс

Разница в скорости обработки запросов клиента связана с архитектурой веб-серверов. Apache для каждого нового соединения запускает отдельный процесс (thread), в то время когда Nginx обрабатывает новое соединение как событие (event) внутри уже запущенного рабочего процесса (worker). Также, разница объясняется тем, что Nginx при обработке php-скриптов пересылает их к серверу РHP-FPM, что также занимает определенное время, в то время, как Apache обрабатывает динамический контент самостоятельно. В итоге, Nginx намного лучше справляется со статичным контентом и простыми скриптами, а то время, как Apache быстрее обрабатывает ресурсоемкие скрипты, где философия "один клиент - один процесс" позволяет добиться лучшей производительности.

Установка Nginx и PHP-FPM

Первым делом установим пакет nginx из репозитория :

sudo apt-get install nginx

Чтобы проверить работоспособность сервера следует перейти по его IP-адресу. На экране должна появиться приветственная страница Nginx.

Эффективность работы Nginx можно существенно увеличить с помощью грамотной настройки. Разница между стандартной и отрегулированной конфигурацией особенно заметна при больших нагрузках.

Настройка Nginx

Главный конфигурационный файл Nginx, находится по адресу /etc/nginx/nginx.conf. Его полное содержание должно выглядеть примерно следующим образом:

user www-data;
worker_processes 4;
pid /run/nginx.pid;
 
events {
         worker_connections 768;
         # multi_accept on;
}
 
http {
 
         ##
         # Basic Settings
         ##
 
         sendfile on;
         tcp_nopush on;
         tcp_nodelay on;
         keepalive_timeout 65;
         types_hash_max_size 2048;
         # server_tokens off;
 
         # server_names_hash_bucket_size 64;
         # server_name_in_redirect off;
 
         include /etc/nginx/mime.types;
         default_type application/octet-stream;
 
         ##
         # Logging Settings
         ##
 
         access_log /var/log/nginx/access.log;
         error_log /var/log/nginx/error.log;
 
         ##
         # Gzip Settings
         ##
 
         gzip on;
         gzip_disable "msie6";
 
         # gzip_vary on;
         # gzip_proxied any;
         # gzip_comp_level 6;
         # gzip_buffers 16 8k;
         # gzip_http_version 1.1;
         # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
 
         ##
         # nginx-naxsi config
         ##
         # Uncomment it if you installed nginx-naxsi
         ##
 
         #include /etc/nginx/naxsi_core.rules;
 
         ##
         # nginx-passenger config
         ##
         # Uncomment it if you installed nginx-passenger
         ##
        
         #passenger_root /usr;
         #passenger_ruby /usr/bin/ruby;
 
         ##
         # Virtual Host Configs
         ##
 
         include /etc/nginx/conf.d/*.conf;
         include /etc/nginx/sites-enabled/*;
}
 
 
#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#                 listen     localhost:110;
#                 protocol   pop3;
#                 proxy      on;
#       }
#
#       server {
#                 listen     localhost:143;
#                 protocol   imap;
#                 proxy      on;
#       }
#}

Для того, чтобы грамотно настроить Nginx, важно понимать функции основных директив. По большей части значения настроек подобраны оптимально или установлены на автоконфигурацию, но ручное изменение некоторых директив может привести к заметному улучшению скорости и качества работы сайта.   

worker_processes отвечает за количество рабочих процессов. В новых версиях желательно установить для неё значение auto для автоматического определения оптимального количества.

worker_connections устанавливает максимальное количество одновременно возможных соединений. Их число можно приблизительно рассчитать по формуле 1 / <время выполнения задачи, cек.>. Как правило, стандартное значение 1024 является оптимальным и редактируется уже по результатам анализа нагрузки на сервер.

multi_accept - при включении рабочий процесс за один раз будет принимать сразу все новые соединения.

sendfile включает метод отправки данных sendfile, более эффективный чем стандартный.

Настройки кеширования

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

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

open_file_cache_valid регулирует период проверки информации о файле в кеше – оптимально стоит устанавливать несколько большее значение, чем в параметре inactive директивы open_file_cache max, чтобы после каждой проверки гарантированно удалялись все неиспользуемые файлы.

open_file_cache_min_uses определяет количество обращений к файлу, необходимое для того, чтобы файл был помещён в кеш или не был из него удалён.

open_file_cache_errors запрещает или разрешает кеширование ошибок поиска файлов.

Сжатие

Ещё один немаловажный момент в настройке скорости работы веб-сервера-  это сжатие Gzip. Эта опция полезна для экономии трафика и ускорения загрузки веб-страниц у пользователей с низкой скоростью интернет-соединения. Но следует обратить внимание, что процедура сжатия сама по себе вносит дополнительную нагрузку на ЦПУ и при сверхнагрузках на сервер может вызвать обратный эффект - количество обрабатываемых запросов уменьшится из-за нехватки вычислительных мощностей.

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

gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;

В этой конфигурации Nginx будет стандартно сжимать все файлы, тип которых указан в gzip_types.

Применение изменений

После внесения любых изменений в конфигурационные файлы Nginx для применения новых настроек используйте команду:

service nginx reload 

Настройка виртуальных хостов

Файлы настроек виртуальных хостов находятся в папке /etc/nginx/sites-available. Изначально там находится файл стандартных настроек default, обеспечивающий работу сервера “из коробки”:

# You may add here your
# server {
#       ...
# }
# statements for each of your virtual hosts to this file
 
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
 
server {
         listen 80 default_server;
         listen [::]:80 default_server ipv6only=on;
 
    root /usr/share/nginx/html;
         index index.html index.htm;
 
         # Make site accessible from http://localhost/
         server_name localhost;
 
         location / {
                   # First attempt to serve request as file, then
                   # as directory, then fall back to displaying a 404.
                   try_files $uri $uri/ =404;
                   # Uncomment to enable naxsi on this location
                   # include /etc/nginx/naxsi.rules
         }
 
         # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
         #location /RequestDenied {
         #       proxy_pass http://127.0.0.1:8080;   
         #}
 
         #error_page 404 /404.html;
 
         # redirect server error pages to the static page /50x.html
         #
         #error_page 500 502 503 504 /50x.html;
         #location = /50x.html {
         #       root /usr/share/nginx/html;
         #}
 
         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
         #
         #location ~ \.php$ {
         #       fastcgi_split_path_info ^(.+\.php)(/.+)$;
         #       # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
         #
         #       # With php5-cgi alone:
         #       fastcgi_pass 127.0.0.1:9000;
         #       # With php5-fpm:
         #       fastcgi_pass unix:/var/run/php5-fpm.sock;
         #       fastcgi_index index.php;
         #       include fastcgi_params;
         #}
 
         # deny access to .htaccess files, if Apache's document root
         # concurs with nginx's one
         #
         #location ~ /\.ht {
         #       deny all;
         #}
}
 
 
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
#       listen 8000;
#       listen somename:8080;
#       server_name somename alias another.alias;
#       root html;
#       index index.html index.htm;
#
#       location / {
#                 try_files $uri $uri/ =404;
#       }
#}
 
 
# HTTPS server
#
#server {
#       listen 443;
#       server_name localhost;
#
#       root html;
#       index index.html index.htm;
#
#       ssl on;
#       ssl_certificate cert.pem;
#       ssl_certificate_key cert.key;
#
#       ssl_session_timeout 5m;
#
#       ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
#       ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
#       ssl_prefer_server_ciphers on;
#
#       location / {
#                 try_files $uri $uri/ =404;
#       }
#}

 Для создания нового виртуального хоста скопируем файл default в ту же папку и переименуем его в example:

cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example
Подключение виртуального хоста

В папке sites-available находятся файлы конфигураций для всех виртуальных хостов веб-сервера, поэтому после создания нового хоста следует подключить его. Для этого нужно создать ссылку на этот файл в папке sites-enabled командой:  

sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/

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

nano /etc/nginx/sites-available/example

Директива listen определяет адрес и порт для IP или путь для UNIX-сокета, на которых сервер будет принимать запросы:

listen 127.0.0.1:80 [default_server];

Можно указать адрес и порт, либо только адрес или только порт. Так же у директивы есть параметр default_server (до версии 0.8.1 назывался просто default), который позволяет сделать виртуальный хост сайтом по умолчанию. В таком случае на него будут перенаправлятся все запросы, не относящеся к другим виртуальным хостам или поступающие напрямую на IP-адрес сервера. По умолчанию эта опция включена в настройках стандартного хоста default, поэтому чтобы избежать ошибки (сервер по умолчанию должен быть только один) нужно удалить этот параметр из файла настроек default или отключить стандартно сконфигурированный сервер, удалив ссылку на него из папки sites-enabled. Все параметры директивы являются опциональными.

 Директива root отвечает за корневой каталог сайта, создадим для тестового хоста example отдельный каталог командой:

mkdir /usr/share/nginx/example

И установим его в качестве корневого, изменив значение директивы в файле настроек:

root /usr/share/nginx/example;

Директива index определяет файлы, которые nginx будет искать в корневом каталоге сайта и использовать в качестве индекса, в неё нужно добавить значение index.php:

index  index.html index.htm index.php;

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

Директива server_name устанавливает доменное имя сайта:

server_name example.com www.example.com *.example.com;

Помимо установки главного доменного имени возможно так же настроить механизм автоматического формирования субдоменов в зависимости от структуры сайта. Для этого нужно изменить значение директивы root:

root /usr/share/nginx/example /$subdomain;

 и добавить следующее условие: 

    set $subdomain "";
    if ($host ~* ^([a-z0-9-\.]+)\.example.com$) {
        set $subdomain $1;
    }
    if ($host ~* ^www.example.com$) {
        set $subdomain "";
    }

Это условие изменяет корневую папку для URL вида subdomain.example.com на /usr/share/nginx/example/subdomain. Благодаря ему при создании папки в корневом каталоге сайта её содержимое будет доступно по адресу имя_папки.example.com.

В конце для завершения настройки и применения новых параметров попросим Nginx перечитать файл конфигурации:

service nginx reload
Проверка работы домена и субдоменов

1. Если вы использовали несуществующий домен (например, example.com) для доступа на сайт придётся отредактировать файл hosts, для того чтобы все запросы с вашей локальной машины, поступающие на адрес example.com перенаправлялись на ваш сервер, а не на реально существующий сайт example.com. Этот файл расположен в /etc/hosts для unix-систем и в %SystemRoot%\system32\drivers\etc\hosts для современных версий Windows. Добавим в  него строку:

<ip_адрес_сервера> example.com

для перенаправления с основного домена и строки

<ip_адрес_сервера> first.example.com
<ip_адрес_сервера> second.example.com

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

Если же вы использовали не тестовый, а реально существующий домен – пропускайте этот пункт и настраивайте DNS-записи у регистратора домена.

2. Создадим в корневой директории сайта файл индекса index.html для главного домена:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <p>Hello, I am the main domain!</p>
    </body>
</html>

и папку first, в которую положим файл индекса для субдомена:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <p>Hello, World, I am the first!<p>
    </body>
</html>

После этого набрав в адресной строке браузера example.com вы увидите содержимое index-файла корневого каталога:

По адресу first.example.com будет доступно содержимое папки first:

А при переходе на second.example.com веб-сервер автоматически покажет стандартную страницу 404, так как каталога second не существует в корневом каталоге сайта.

Настройка PHP-FPM

Изначально nginx не способен исполнять динамические файлы - если в папку стандартно сконфигурированного виртуального хоста положить файл с расширением .php и попытаться получить к нему доступ по адресу //localhost/myfile.php то начнется его скачивание, а исполнение будет невозможно. Для того чтобы файлы с динамическим содержимым корректно выполнялись к Nginx нужно подключить бэкенд-обработчик php-fpm.

Для установки PHP-FPM используется команда:

sudo apt-get install php5-fpm

Сервис запустится автоматически в конце установки.

Для подключения обработчика php к настроенному нами виртуальному хосту раскомментируем в файле настроек example относящиеся к php-fpm строки и удалим лишние, приведя блок location ~ \.php$ к следующему виду:

    location ~ \.php$ { 
        fastcgi_split_path_info ^(.+\.php)(/.+)$; 
        fastcgi_pass     127.0.0.1:9000; 
        fastcgi_index index.php; 
        include fastcgi_params; 
    }

Перезапустим веб-сервер для применения новых настроек:

service nginx reload

После этого для теста работы php можно создать файл phpinfo.php следующего содержания:

<?php phpinfo(); ?>

и поместить его в корневые каталоги хоста example и стандартного хоста default (корневой каталог которого по умолчанию /usr/share/nginx/html) чтобы сравнить результаты.

На default хосте при попытке открыть phpinfo.php начинается скачивание файла:

В то время, как на хосте example с правильно подключенным php-fpm файл корректно выполнится:

Установка MySQL

Сервер баз данных MySQL устанавливается командой

sudo apt-get install mysql-server php5-mysql

В процессе установки необходимо указать пароль root пользователя MySQL

На все вопросы при установке отвечаем утвердительно.

После этого устанавливается phpmyadmin командой:

sudo apt-get install phpmyadmin

При установке будет предложено выбрать веб-сервер, с которым будет работать MySQL, опции Nginx по умолчанию нет, потому в этом меню нужно нажать клавишу TAB и кнопку OK без выбора какой-либо опции в списке. 

На всплывающий вопрос о том, хотим ли мы использовать стандартный конфиг dbconfig-common отвечаем утвердительно, вводим придуманный ранее пароль для root пользователя MySQL.

По завершении установки создадим ссылку в корневом каталоге ранее настроенного виртуального хоста example для того чтобы веб-сервер мог корректно найти файлы phpmyadmin и работать с ними:

sudo ln -s /usr/share/phpmyadmin /usr/share/nginx/example

Далее следует включить модуль mcrypt который нужен для работы phpmyadmin и перезапустить PHP-FPM командами:

sudo php5enmod mcrypt
sudo service php5-fpm restart

Сервер MySQL успешно установлен, phpmyadmin доступен по адресу виртуального хоста example.

Если phpmyadmin нужно подключить ещё к какому-либо виртуальному хосту нужно создать ещё одну ссылку на файлы phpmyadmin в том каталоге, который прописан в строке root файла настроек виртуального хоста - в том каталоге, где лежат файлы этого хоста, например, это каталог /usr/share/nginx/html для стандартно сконфигурированного виртуального хоста default. Для работы phpmyadmin к виртуальному хосту обязательно нужно подключить PHP-FPM или любой другой backend-сервер.

После этого можно перейти по адресу сконфигурированного виртуального хоста и найти страницу логина phpmyadmin по адресу <адрес_хоста>/phpmyadmin/

Перевод правил mod_rewrite в конфигурацию Nginx

Большинство правил mod_rewrite можно без труда перенести в Nginx при помощи автоматических конвертеров, но они не всегда дают гарантированно корректный результат, и в таком случае приходит на помощь ручной перевод. Основной инструмент для реализации аналога mod_rewrite в Nginx это директивы модуля ngx_http_rewrite_module:

  •  rewrite регулярное_выражение замена [флаг] - основная директива модуля, если URL запроса соответствует указанному регулярному выражению, то он изменяется в соответствие со строкой замены.  Флаг имеет несколько значений (возможно указать несколько флагов одновременно), наиболее используемое из которых last - после замены вся обработка текущего URL завершается, после чего ищется новый блок location, под который попадает измененный URL;
  • if (условие) { ... } - обыкновенный условный оператор, позволяющий создавать ветвление в правилах замены;
  • break и return - используются для прерывания обработки текущего запроса, директива return позволяет вернуть клиенту код ошибки или URL.

Указанные директивы могут использоваться как в главном конфигурационном файле nginx.conf для влияния на все запросы ко всем виртуальным хостам, так и в файлах настроек отдельных хостов. Они могут устанавливаться в блоках server для указания каких-то общих правил замены, либо в блоках location для обработки только конкретных URL по заданной маске.

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

RewriteEngine On
RewriteRule ^([^/]*)/$ ?city=$1 [L]
RewriteRule ^([a-z\-]+)\/([a-z\-]+)?$ /index.php?city=$1&page=$2 [L]
RewriteRule ^users/profile/id(.*) /index.php?city=users&page=profile&id=$1 [NC,L]

будет перенесена:

rewrite ^/([^/]*)/$ /?city=$1 last;
rewrite ^/([a-z\-]+)\/([a-z\-]+)?$ /index.php?city=$1&page=$2 last;
rewrite ^/users/profile/id(.*) /index.php?city=users&page=profile&id=$1 last;

Заключение

Пакет LNMP установлен и готов к работе. Далее могут следовать этапы более тонкой настройки всего веб-сервера и виртуальных хостов под конкретные задачи, установка cms, организация защиты сайта и базы данных от несанкционированного проникновения настройка файлового хранилища или почтового сервера и множество других интересных задач. Более подробная информация по настройке доступна в официальной документации Nginx и MySQL.

Подпишитесь на нашу рассылку,
чтобы получать последние обновления нашего блога!