Перед тим як почати горизонтальне і вертикальне масштабування інфраструктури насамперед необхідно переконатися, що ресурси використовуються правильно, а конфігурація програм не створює вузьких місць для його продуктивності. Головна мета команди інженерів - забезпечити постійну, безперебійну роботу будь-якої спроєктованої і розгорнутої системи за допомогою мінімальних ресурсів.
Ми зіткнулися з вказаною вище проблемою, коли розгорнута нами система, якою щодня користувалося мільйон користувачів, які підключалися сплесками час від часу. Це означає, що розгортання кількох серверів або їх масштабування не буде в даній ситуації оптимальним рішенням.
Ця стаття присвячена налаштуванню Nginx для підвищення продуктивності, тобто для збільшення показників RPS (Requests Per Second) в HTTP API. Я постарався розповісти про оптимізацію, яку ми застосували в розгорнутій системі, щоб обробляти десятки тисяч запитів в секунду без витрачання величезної кількості ресурсів. План дій: необхідно запустити HTTP API (написаний на Python з використанням flask), проксювати за допомогою Nginx; потрібна висока пропускна здатність. Вміст API буде змінюватися з інтервалом в один день.
Ми використовували супервізор для запуску WSGI Server з наступними конфігураціями:
- Gunicorn з робочими Meinheld
- Кількість робочих: кількість ЦП * 2 + 1
- Прив'язали сокет до Unix-адреси замість IP, це трохи збільшить швидкість.
Команда для супервізора виглядає так:
gunicorn api:app --workers=5 --worker- class=meinheld.gmeinheld.MeinheldWorker --bind=unix:api.sock
Ми спробували оптимізувати конфігурацію Nginx і перевірили, що найкраще спрацює для нас. Для оцінки продуктивності API ми використовували wrk за допомогою наступної команди:
wrk -t20 -c200 -d20s http://api.endpoint/resource
Конфігурація за замовчуванням
Спочатку ми виконали тестування навантаження API без будь-яких змін і отримали наступну статистику:
Running 20s test @ http://api.endpoint/resource 20 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 192.48ms 274.78ms 1.97s 87.18% Req/Sec 85.57 29.20 202.00 72.83% 33329 requests in 20.03s, 29.59MB read Socket errors: connect 0, read 0, write 0, timeout 85 Requests/sec: 1663.71 Transfer/sec: 1.48MB
Оновлення конфігурації за замовчуванням
Відновімо стандартну конфігурацію Nginx, тобто nginx.conf в /etc/nginx/nginx.conf
worker_processes auto; #or should be equal to the CPU core, you can use `grep processor /proc/cpuinfo | wc -l` to find; auto does it implicitly. worker_connections 1024; # default is 768; find optimum value for your server by `ulimit -n` access_log off; # to boost I/O on HDD we can disable access logs # this prevent nginx from logging every action in a log file named `access.log`. keepalive_timeout 15; # default is 65; # server will close connection after this time (in seconds) gzip_vary on; gzip_proxied any; gzip_comp_level 2; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_min_length 256; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; # reduces the data that needs to be sent over the network
nginx.conf (/etc/nginx/nginx.conf
) Після змін ми запускаємо перевірку конфігурації:
sudo nginx -t
Якщо перевірка пройшла успішно, можна перезапустити Nginx, щоб відобразити зміни:
sudo service nginx restart
З такою конфігурацією ми провели тестування навантаження API й отримали такий результат:
Running 20s test @ http://api.endpoint/resource 20 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 145.80ms 237.97ms 1.95s 89.51% Req/Sec 107.99 41.34 202.00 66.09% 42898 requests in 20.03s, 39.03MB read Socket errors: connect 0, read 0, write 0, timeout 46 Non-2xx or 3xx responses: 2 Requests/sec: 2141.48 Transfer/sec: 1.95MB
Ці зміни скоротили тайм-аути та збільшили показники RPS (кількість запитів в секунду), але не набагато.
Додавання кеша Nginx
Оскільки в нашому випадку вміст кінцевої точки буде оновлюватися з інтервалом в один день, це створює сприятливі умови для кешування відповідей API. Але додавання кеша призводить до його недійсності... це одна з двох проблем.
У комп'ютерних науках є тільки дві складності: інвалідація кеша і іменування речей. - Філ Карлтон
Ми вибираємо просте рішення очищення каталогу кеша за допомогою cronjob після оновлення вмісту в нижче стоячій системі. Далі всю важку роботу буде виконувати Nginx, але тепер ми повинні бути впевнені, що Nginx готовий на 100%! Щоб додати кешування в Nginx, потрібно прописати кілька директив у файл конфігурації Nginx. Перед цим нам потрібно створити каталог для зберігання даних кеша:
sudo mkdir -p /data/nginx/cache
Зміни в конфігурації Nginx:
proxy_cache_path /data/nginx/cache keys_zone=my_zone:10m inactive=1d; server { ... location /api-endpoint/ { proxy_cache my_zone; proxy_cache_key "$host$request_uri$http_authorization"; proxy_cache_valid 404 302 1m; proxy_cache_valid 200 1d; add_header X-Cache-Status $upstream_cache_status; } ... }
Кешування проксюємих запитів (конфігурація Nginx) Після цієї зміни в конфігурації ми провели тестування навантаження API й отримали такий результат:
Running 20s test @ http://api.endpoint/resource 20 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 6.88ms 5.44ms 88.91ms 81.36% Req/Sec 1.59k 500.04 2.95k 62.50% 634405 requests in 20.06s, 589.86MB read Requests/sec: 31624.93 Transfer/sec: 29.40MB
Таким чином, ми отримали майже 19-кратне збільшення продуктивності внаслідок додавання кешування.
Кеш Nginx в RAM
Зробімо ще один крок вперед! В цей час дані нашого кеша зберігаються на диску. А якщо ми збережемо ці дані в RAM? У нашому випадку дані відповіді обмежені та не мають великого розміру. Отже, спочатку потрібно створити каталог, куди буде монтуватися кеш оперативної пам'яті:
sudo mkdir -p /data/nginx/ramcache
Щоб змонтувати створений каталог в RAM за допомогою tmpfs, використовуйте команду:
sudo mount -t tmpfs -o size=256M tmpfs /data/nginx/ramcache
Це монтує /data/ nginx/ramcache
в RAM, виділяючи 256МБ. Якщо ви захочете відключити RAM-кеш, просто виконайте команду:
sudo umount /data/nginx/ramcache
Щоб автоматично монтувати каталог кеша в RAM після перезавантаження, нам потрібно оновити файл /etc/fstab
. Додайте в нього такий рядок:
tmpfs /data/nginx/ramcache tmpfs defaults,size=256M 0 0
Примітка: Також ми повинні прописати значення proxy_cache_path
із зазначенням шляху до ramcache (/data/nginx/ramcache
). Після оновлення конфігурації ми знову провели тестування навантаження API та отримали такий результат:
Running 20s test @ http://api.endpoint/resource 20 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.57ms 5.69ms 277.76ms 92.94% Req/Sec 1.98k 403.94 4.55k 71.77% 789306 requests in 20.04s, 733.89MB read Requests/sec: 39387.13 Transfer/sec: 36.62MB
Зберігання кеша в оперативній пам'яті привело до значного поліпшення майже у 23 рази.
Журнал буферизованного доступу
Ми зберігаємо журнал доступу до застосунків які проксюємо, але можна спочатку зберегти журнал у буфер і тільки потім записати на диск:
- якщо наступний рядок логу не поміщається в буфер
- якщо дані в буфері старші, ніж зазначено в параметрі flush.
Ця процедура зменшить частоту запису що виконується з кожним запитом. Для цього нам просто потрібно додати параметри buffer
і flush
з відповідним значенням в директиві access_log
:
location / { ... access_log /var/log/nginx/fast_api.log combined buffer=256k flush=10s; error_log /var/log/nginx/fast_api.err.log; }
Таким чином, відповідно до наведеної вище конфігурації, спочатку журнали доступу будуть записуватися в буфер і зберігатися на диск тільки тоді, коли розмір буфера досягне 256 КБ або буферизовані дані стануть старші 10 секунд.
Після повторного тестування навантаження ми отримали такий результат:
Running 20s test @ http://api.endpoint/resource 20 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.21ms 3.19ms 84.83ms 83.84% Req/Sec 2.53k 379.87 6.02k 77.05% 1009771 requests in 20.03s, 849.31MB read Requests/sec: 50413.44 Transfer/sec: 42.40MB
Така конфігурація значно збільшила кількість запитів в секунду, приблизно в 30 разів у порівнянні з початковим етапом.
Висновок
У цій статті ми обговорили процес оптимізації конфігурації Nginx для поліпшення показників RPS. Показники RPS були збільшені з 1663 до ~ 50413 (збільшення приблизно в 30 разів), це забезпечує високу пропускну здатність. Завдяки налаштуванню стандартних параметрів можна поліпшити продуктивність системи. Закінчимо статтю цитатою:
Спочатку зроби так, щоб працювало. Потім зроби правильно. Потім оптимізуй. - Кент Бек
Джерела
- docs.nginx.com/nginx/admin-guide
- www.nginx.com/blog/tuning-nginx
- github.com/wg/wrk/wiki/installing-wrk-on-linux
- kb.virtubox.net/knowledgebase/improve-nginx-cache-performance-with-tmpfs
Джерело: Optimizations: Tuning Nginx for better RPS of an HTTP API
Ще немає коментарів