Один из 100500 примеров проксирования в NGINX: частный случай балансировки нагрузки
Ожидает приглашения
Задача проксирования с целью балансировки нагрузки описана на разных ресурсах 100500 раз. Однако в каждом конкретном случае есть 100500 нюансов, и когда я стал разбираться со своей проблемой, прямого ответа найти так и не удалось. Пришлось потратить несколько часов. Возможно, описание моего случая кому-то поможет сэкономить время.
Возникла задача балансировки нагрузки между основным сервером и его зеркалом. Нужно было сделать так, чтобы запросы к API разделялись между основным сервером и зеркалом по принципу: запросы на чтение — на зеркало, запросы на запись — на основной. В наличии был один важный факт: все запросы на запись к API выполняются методами POST, PUT и DELETE, а запросы на чтение — методом GET. Поэтому было решено воспользоваться этим фактом.
Исходная инфраструктура: Ubuntu 16 + nginx + uwsgi, в целом одинаковая и на основном сервере, и на зеркале, с той лишь разницей, что БД на зеркале является репликой основной БД в режиме read-only.
Что в итоге получилось — в конфигах nginx с комментариями.
Конфиг nginx на основном сервере:
Теперь внесём некоторые правки в конфиг на зеркале.
Это всё.
Если у сообщества есть замечания по этому решению — приму с благодарностью!
Возникла задача балансировки нагрузки между основным сервером и его зеркалом. Нужно было сделать так, чтобы запросы к API разделялись между основным сервером и зеркалом по принципу: запросы на чтение — на зеркало, запросы на запись — на основной. В наличии был один важный факт: все запросы на запись к API выполняются методами POST, PUT и DELETE, а запросы на чтение — методом GET. Поэтому было решено воспользоваться этим фактом.
Исходная инфраструктура: Ubuntu 16 + nginx + uwsgi, в целом одинаковая и на основном сервере, и на зеркале, с той лишь разницей, что БД на зеркале является репликой основной БД в режиме read-only.
Что в итоге получилось — в конфигах nginx с комментариями.
Конфиг nginx на основном сервере:
# Сокет, через который nginx общается с uwsgi-приложением
upstream flask_serv {
server unix:/tmp/flask.sock;
}
# Адрес зеркала, на котором реплика БД в read-only
upstream read_api {
server api.server.ru;
}
# Выбираем цель на основе метода запроса
map $request_method $upstream_location {
GET read_api;
default flask_serv;
}
server {
listen 443 ssl;
server_name server.ru;
root /var/www/server;
# Включаем запрос сертификата Let`s Encrypt (подробности опускаем)
include acme;
# Location для общих запросов (не к API). Они должны проксироваться через сокет вне зависимости от того, какой метод у запроса.
location / {
if ($http_referer !~* ^($|http://|https://) ){
return 403;
}
include uwsgi_params;
uwsgi_pass flask_serv;
uwsgi_read_timeout 300;
}
location /static/ {
root /var/www/server/application;
}
# Location для запросов к API. Распределяем запросы в зависимости от метода.
location /api/ {
include uwsgi_params;
# Если запрос на запись - обращаемся к сокету
if ($upstream_location = "flask_serv") {
uwsgi_pass unix:/tmp/flask.sock;
}
# По дефолту nginx записывает в заголовок Host домен server_name, т.е. переменную $host
# Это засада, и если не переписать его значением хоста, к которому идёт проксирование,
# nginx будет отправлять запрос на IP-адрес сервера api.server.ru, указывая в
# заголовке Host домен server.ru, и вы получите 502.
proxy_set_header Host api.server.ru;
# Укажем, что мы хотим пробросить IP адрес клиента на зеркало, чтобы в логах на зеркале
# увидеть не IP адрес server.ru, а IP адрес клиента. Это потребует некоторых манипуляций и в
# конфиге nginx на зеркале.
proxy_set_header X-Real-IP $remote_addr;
# Если запрос на чтение - проксируем на зеркало
if ($upstream_location = "read_api") {
proxy_pass http://$upstream_location;
}
}
ssl_certificate /etc/letsencrypt/live/server.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.ru/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/server.ru/chain.pem;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "RC4:HIGH:!aNULL:!MD5:!kEDH";
add_header Strict-Transport-Security 'max-age=600';
}
Теперь внесём некоторые правки в конфиг на зеркале.
server {
listen 80;
server_name api.server.ru;
...
# Укажем реальный IP основного сервера и заменим адрес проксирующего сервера на адрес клиента
set_real_ip_from xxx.xxx.xxx.xxx;
real_ip_header X-Real-IP;
...
}
Это всё.
Если у сообщества есть замечания по этому решению — приму с благодарностью!