@c0nst_float
Python Back-end developer

Nginx as reverse proxy: 1% запросов падает по тайм-ауту (504). Что можно предпринять?

У нас есть:
  1. Проект с нагрузкой ~ 1000-3000 RPS. В пиках - до 5К RPS.
  2. Партнёр к которому постоянно обращаемся за данными. (Одна из основных функций). Данные очень часто обновляются. Один пользователь создаёт нагрузку на партнёров ≈ 0.5 - 2 RPS. И это нормально, такая специфика...
  3. Основной сервер 16 Gb RAM, 16 Cores, Ubuntu 16.04.1 LTS, Nginx 1.10.3
  4. 5 серверов "поменьше" - на них крутится наш BackEnd
  5. Сервер с БД, Redis-ом и RabbitMQ


Упрощенно, взаимодействие можно представить следующим образом:
Основной nginx выступает в роли reverse-proxy. Балансирует нагрузку на наши сервера, и проксирует запросы к партнёрам.
vu_ALIr8oNw.jpgПроблема:
При нагрузке некоторые запросы (~1K из 100K) начинают отпадать по тайм-ауту.
Провели нагрузочное тестирование:
  • Если пользователь запрашивает данные от партнёров напрямую - все отдается быстро, четко и корректно. Вот линк на Yandex.Overload
  • А вот график тестирования при проксировании через наш основной Nginx. В среднем - в два раз быстрее благодаря кэшированию, но если посмотреть на квантиль "100%"...

Причем проблема становится ГОРАЗДО существеннее, при пиковых нагрузках.
Есть идея попробовать другой reverse-proxy, например "Traefik" или тот-же Apache.
Но ведь Nginx - инструмент проверенный временем и сотнями крупных компаний с нагрузкой гораздо выше, нежели у нас. Поэтому, вероятнее всего проблема - в наших конфигах или(и) неверно настроенным сетевым взаимодействием.
Привожу примеры конфигурационных файлов (упростил, но оставил основные настройки).
nginx.conf (main)
user www-data;
worker_processes 16;
worker_priority -10;
pid /run/nginx.pid;
worker_rlimit_nofile 200000;

events {
	worker_connections 2048;
	use epoll;
}

http {
	sendfile on;
	tcp_nopush off;
	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;

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	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/javascript text/xml application/xml application/xml+rss text/javascript;

        upstream backend {
        least_conn;
        ip_hash;
         server localhost:8082;
         server 37.48.123.321:8082;
         # other servers...
        }

        upstream partner_api {
                server WW.XX.YY.ZZ;
        }
        proxy_cache_path /etc/nginx/conf.d/data/cache/line levels=1:2 keys_zone=line_cache:150m max_size=1g inactive=60m;
        proxy_cache_path /etc/nginx/conf.d/data/cache/live levels=1:2 keys_zone=live_cache:150m max_size=1g inactive=60m;
        include /etc/nginx/sites-enabled/*;
}
/etc/nginx/sites-enabled/domain.com.conf
server {
  listen 443 ssl http2;
  server_name domain.com www.domain.com *.domain.com;
  ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;

  ssl_stapling on;
  ssl_stapling_verify on;

  resolver 127.0.0.1 8.8.8.8;

  add_header Strict-Transport-Security "max-age=31536000";

  include /etc/nginx/conf.d/reverse_porxy.conf;
 
  location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    error_page 500 502 503 504 /50x.html;
  }
}

server
{
    listen 80;
    server_name domain.com www.domain.com;
    location /
    {
        return 301 https://$host$request_uri;
    }
}
/etc/nginx/conf.d/reverse_porxy.conf
location ~ ^/api/remote/(.+) {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    
    include /etc/nginx/conf.d/reverse_proxy_settings.conf;
    proxy_pass http://partner_api/WebApiG/WebServices/BCService.asmx/$1;
}

location ~ ^/api/bs2/remote/api/line/(.+) {
  include /etc/nginx/conf.d/reverse_proxy_settings.conf;
  proxy_cache_valid any 2m;
  proxy_pass http://partner_api/WebApiG/api/line/$1;
  proxy_cache line_cache;
  proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}

location ~ ^/api/bs2/remote/api/live/(.+) {
  include /etc/nginx/conf.d/reverse_proxy_settings.conf;
  proxy_pass http://partner_api/WebApiG/api/live/$1;
  proxy_cache live_cache;
  proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}
# etc...
/etc/nginx/conf.d/reverse_proxy_settings.conf
proxy_buffering on;
proxy_ignore_headers "Cache-Control" "Expires";
expires    max;
proxy_buffers    32 4m;
proxy_busy_buffers_size    25m;
proxy_max_temp_file_size    0;
proxy_cache_methods    GET;
proxy_cache_valid    any 3s;
proxy_cache_revalidate    off;
proxy_set_header    Host "PARTNER_IP";
proxy_set_header    Origin "";
proxy_set_header    Referer "";
proxy_set_header    Cookie '.TOKEN1=$cookie__TOKEN1; ASP.NET_SessionId=$cookie_ASP_NET_SessionId;';
proxy_hide_header    'Set-Cookie';
proxy_http_version              1.1;


Что можете посоветовать? Может, прокинуть VPN тоннель до партнёров или может действительно заменить nginx на что-то другое? (Увеличивать таймаут, как везде советуют - не наш случай)
  • Вопрос задан
  • 2108 просмотров
Решения вопроса 1
@c0nst_float Автор вопроса
Python Back-end developer
Решением было: увеличение количества nginx воркеров и коннекшенов:
user www-data;
worker_processes 32;
worker_priority -10;
pid /run/nginx.pid;
worker_rlimit_nofile 200000;

events {
        worker_connections 4096;
        use epoll;
}
...

И самое главное: включение режима keep-alive для upstream-a:
upstream partner_api {
                server WW.XX.YY.ZZ;
                
                keepalive 512;
        }

Так-же оказывается, что установив proxy_http_version 1.1; поддержка keep-alive соединения не включится. Нужно также установить proxy_set_header Connection "";

Со всеми числами можно поиграть и добиться нужного результата.
Раньше у нас отваливалось 5-6 запросов в секунду. Сейчас тоже отваливается, но гораздо реже: 1 запрос в 10-15 минут. Текущий результат пока что удовлетворяет. В будущем будем добиваться максимально возможных результатов.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
ky0
@ky0 Куратор тега Nginx
Миллиардер, филантроп, патологический лгун
Увеличить таймаут, а потом в спокойной обстановке без отваливающихся запросов расследовать. Сетевая подсистема на убунтах нормально настроена, затюнена и т. д.?
Ответ написан
@grinat
Надо буферизацию вырубить у reverse_porxy.conf либо в domain.com.conf увеличить read timeout(вроде 30 сек по умолчанию). Просто nginx по умолчанию получает все данные и только потом их отдает дальше. Подозреваю что на нагрузке он не успевает дождаться получения данных, и срабатывает read timeout, поскольку ни байта он не получил. А если напрямую конектится, то nginx'a нет, read timeout не срабатывает и все ок.
https://nginx.org/ru/docs/http/ngx_http_proxy_modu...
Ответ написан
@klepiku
провода переобжать и проверить между станциями
Ответ написан
Комментировать
alone_lion1987
@alone_lion1987
Веб-разработчик
в конфиге по домену увеличиваем таймаут, который по дефолту минимальный. После этого можно изучить, почему долго обрабатывается запрос или производить дальнейшую оптимизацию (если нужно)

Добавляем в /etc/nginx/sites-enabled/domain.com.conf в секцию location / инструкцию:

fastcgi_read_timeout 600;  # ждать обработку 600 сек, например
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы