Представим, что у нас есть приложение/сайт с достаточно высокой нагрузкой.
Многие разработчики приложений для «ВК» или «Одноклассников» сталкивались с ситуацией, когда приложение выходит в топ новых приложений и на вас сваливается огромнейшая нагрузка.
Допустим, в процессе обращения клиента к серверу, генерируется картинка. Серверов у нас много. Каким образом клиенту отдать эту картинку, если у вас нет единой файловой системы и файлы не синхронизируются между серверами?
Как поступить, когда на сервер ежесекундно приходит большое количество народу? Ответ прост — 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. Сервер 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