Балансировка статических файлов средствами nginx

в 12:33, , рубрики: nginx, высокая производительность, системное администрирование, метки: ,

Представим, что у нас есть приложение/сайт с достаточно высокой нагрузкой.

Многие разработчики приложений для «ВК» или «Одноклассников» сталкивались с ситуацией, когда приложение выходит в топ новых приложений и на вас сваливается огромнейшая нагрузка.
Допустим, в процессе обращения клиента к серверу, генерируется картинка. Серверов у нас много. Каким образом клиенту отдать эту картинку, если у вас нет единой файловой системы и файлы не синхронизируются между серверами?

Как поступить, когда на сервер ежесекундно приходит большое количество народу? Ответ прост — nginx.

В этом топике я буду рассматривать балансировку как статических файлов, так и динамических.
Сразу оговорюсь. Варианты балансировки запросов в сторону MySQL мы сейчас обсуждать не будем.
То есть, грубо говоря, у нас есть сервера, на которых установлен одинаковый набор php скриптов, установлен и настроен php-fpm. Оба сервера отвечают на запросы клиентов и в конфигурацию DNS вписаны записи, по которым ваш домен отвечает по адресам 173.194.32.2 и 173.194.32.3.

Итак, поехали.

Настройки серверов

Сервер 1:
Внешний IP адрес: 173.194.32.2
Внутренний IP адрес: 192.168.0.1

Сервер 2:
Внешний IP адрес: 173.194.32.3
Внутренний IP адрес: 192.168.0.2

Схема нашей сети будет выглядеть примерно так:
Балансировка статических файлов средствами nginx

Конфигурация nginx. Сервер 1:

upstream nextserver {
                server 192.168.0.2;
}
upstream backend {
                server 127.0.0.1;
}

server {
        listen *:80;
        server_name 173.194.32.2;

        location / {
                root         /var/www/default; # сайт у нас лежит по этому пути.
                access_log   off; 
                try_files $uri $uri/ @nextserver;
        }

        location @nextserver {
                proxy_pass           http://nextserver;
                proxy_connect_timeout      70;
                proxy_send_timeout         90;
                proxy_read_timeout         90;
        }

        location ~* .(php5|php|phtml)$ {
                proxy_pass  http://backend;
        }
}

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

upstream nextserver

nextserver — список серверов для отдачи статических файлов

                server 192.168.0.2;

upstream backend

backend — список серверов для отдачи динамического контента(например php)

                server 127.0.0.1;

Кстати, если у вам обязательно надо, чтобы запросы от клиента попадали на один и тот же сервер каждый раз, то в секцию upstream надо добавить директиву ip_hash;

Location /

Стандартная директива. Корневой каталог сервера задается директивой root

root         /var/www/default;

Отключаем access_log. При большой нагрузке запись access лога во первых даст дополнительную нагрузку на диски, во вторых — быстро забьет диск.

access_log   off; 

В этой строке происходит самое интересное :) Мы проверяем существование файла, потом существование директории. Если файл есть, то nginx отдает его клиенту. Если же файла/директории не существует, клиент перенаправляется на location @nextserver.

try_files $uri $uri/ @nextserver;

location @nextserver

Тут все просто. При перенаправлении клиента на этот локейшен, мы перенаправляем запрос клиента посредством директивы proxy_pass на upstream nextserver. В данном случае это 192.168.0.2. Серверов в секции upstream может больше одного, поэтому при желании мы можем перенаправлять клиентов на несколько серверов.
Если сервера два, то первый клиент попадет на первый сервер, а второй клиент — на второй.
Кроме этого в данной секции происходит установка настроек перенаправления. В данном случае — тайм-ауты.

             proxy_pass           http://nextserver;
             proxy_connect_timeout      70;
             proxy_send_timeout         90;
             proxy_read_timeout         90;

location ~* .(php5|php|phtml)$

При запросе файлов, которые имеют расширение php5, php, phtml — переадресовываем клиента обработчику — в данном случае на upstream backend

             proxy_pass  http://backend;

Осталось дело за малым — прописать еще одну секцию server, на этот раз сервер должен слушать адрес 127.0.0.1.
По этому адресу мы должны будем получать подключения и отрабатывать их с помощью php-fpm.
Секцию server прописываем по образу и подобию описанного выше, за исключением одной детали. Меняем секцию location ~* .(php5|php|phtml)$ на указанную ниже.

location ~* .(php5|php|phtml)$ {
            fastcgi_pass        unix:/tmp/php-fpm.sock;
            fastcgi_index       index.php;
            fastcgi_param       SCRIPT_FILENAME  /var/www/default$fastcgi_script_name;
            include             fastcgi_params;
}

Описание данной секции подразумевает, что у вас установлен и запущен демон php-fpm и он слушает unix сокет по адресу /tmp/php-fpm.sock

Второй сервер настраиваем по аналогии с первым. Меняем адрес в секции upstream nextserver на 192.168.0.1(то есть говорим серверу, что отправлять запросы по несуществующим файлам надо на сервер 1).
Кроме этого меняем директиву server_name.

Конфигурация сервера 2 должна выглядеть примерно так:

upstream nextserver {
                server 192.168.0.1;
}
upstream backend {
                server 127.0.0.1;
}

server {
        listen *:80;
        server_name 173.194.32.3;

        location / {
                root         /var/www/default; # сайт у нас лежит по этому пути.
                access_log   off; 
                try_files $uri $uri/ @nextserver;
        }

        location @nextserver {
                proxy_pass           http://nextserver;
                proxy_connect_timeout      70;
                proxy_send_timeout         90;
                proxy_read_timeout         90;
        }

        location ~* .(php5|php|phtml)$ {
                proxy_pass  http://backend;
        }
}

Таким образом, если клиент запрашивает картинку, которая не существует на одном сервере, будет произведена попытка поиска картинки на втором сервере.
Единственная проблема в данном случае — если картинки нет на обоих серверах, то запросы будут передаваться по кругу от одного сервера, к другому до тех пор, пока не достигнет тайм-аута. В итоге будет возавращена 503 ошибка.

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

Автор: shinespb


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js