Re: nginx 1.18.0 ест всю память и swap на Ubuntu Server 20.04.1 LTS

Alexey Galygin mif на me.com
Пн Авг 31 21:42:14 UTC 2020


в общем
передаю новости с полей

жруна нашли — это NJS, на паре детских безобидных функций

TL;DR

измерили скорости дисков, потрясли поддержку… даже уже собрались всё переключить на SSD (Tier1)… но

понизили воркеров до 20, убедились, что их не больше
понизили коннекты на каждого до 2048

включили лимит на sendfile — 1m

включили aio on

отключили http2 — пусть DDoS/CDN оператор транслирует другие протоколы, не важно

на боевом сервере проверили эту же конфигурацию днём — сбоев нет на более слабом железе

* * *

затаились с коллегами в кустах
включили в полночь аплинк и стали ждать 5 минут (пока он рассосётся)… нервно поглядывая на часы

казалось бы всё ровно, так иногда мигает “D”, но в целом “R”, стабильно
и только расслабились, как, вдруг, началось…

как это происходит?
кто-то входит на мозольную URL (мы пока не знаем какую), после чего начинается труба!

не дожидаясь вырубили аплинк, ушли на старый сервер
но смертный жор продолжился

все воркеры начинают (продолжают) есть память по старшинству, даже уже без нагрузки

как только OOM-K убивает одного, другой начинает всё дожирать
и так пока все не будут убиты

в процессе стали смотреть, чем же они там заняты без нагрузки

оказалось проблема в NJS части, какой-то баг там именно в связке 1.18.0 + Ubuntu 20.04

у нас есть такой артефакт (переписанный с Perl на NJS):

function unescapeURI(r) {
        return r.uri.replace(/%20/g, " ");
}
function escapeURI(r) {
        return r.uri.replace(/\s/g, "%20").replace(/_/g, "%5F");
};

conf.d/ege.conf:81:		try_files		$uri $escaped_uri $unescaped_uri =404;
nginx.conf:123:	js_set $unescaped_uri unescapeURI;
nginx.conf:124:	js_set $escaped_uri escapeURI;
parts/file-storage-new.conf:51:	try_files	$uri $escaped_uri $unescaped_uri =404;
parts/file-storage-new.conf:55:	try_files	$uri $escaped_uri $unescaped_uri =404;
parts/static-stuff-new.conf:23:	try_files		$uri $escaped_uri $unescaped_uri @backendCache-15m;
parts/static-stuff-new.conf:40:	try_files		$uri $escaped_uri $unescaped_uri @backend;
parts/static-stuff-new.conf:47:	try_files		$uri $escaped_uri $unescaped_uri @backend;
parts/static-stuff-new.conf:59:	try_files		$uri $escaped_uri @backendCache-alljs;
parts/static-stuff-new.conf:117:	try_files		$uri $escaped_uri @backend @accessTicketFailback;
parts/static-stuff-new.conf:124:	try_files		$uri $escaped_uri @backend @accessTicketFailback;

всё ради того, чтобы какие-то адские бубны, чтобы пробел и подчёркивание заменить на %20 %5F и наоборот…

больше NJS ни для чего не нужен, но все воркеры, которые жрали сидели примерно в этих местах:

0x00007ffac2a7e4cb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
(gdb) bt
#0  0x00007ffac2a7e4cb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#1  0x00007ffac2a552ba in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#2  0x00007ffac2a555ec in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#3  0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#4  0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#5  0x00007ffac2a561c2 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#6  0x00007ffac2a56440 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#7  0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#8  0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#9  0x00007ffac2a39b53 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#10 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#11 0x00007ffac2a329c8 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#12 0x00007ffac2a2a218 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#13 0x0000556030cef6a5 in ngx_http_get_indexed_variable ()
#14 0x0000556030cf0695 in ngx_http_script_copy_var_len_code ()
#15 0x0000556030cf269d in ngx_http_script_complex_value_code ()
#16 0x0000556030d26ec5 in ?? ()
#17 0x0000556030cdefbc in ngx_http_core_rewrite_phase ()
#18 0x0000556030cdaa3d in ngx_http_core_run_phases ()
#19 0x0000556030ce5b3f in ?? ()
#20 0x0000556030ce5f14 in ?? ()
#21 0x0000556030ccd6ae in ?? ()
#22 0x0000556030cc3bf6 in ngx_process_events_and_timers ()
#23 0x0000556030ccb951 in ?? ()
#24 0x0000556030cc9e7b in ngx_spawn_process ()
#25 0x0000556030ccb010 in ?? ()
#26 0x0000556030ccc327 in ngx_master_process_cycle ()
#27 0x0000556030ca330e in main ()
(gdb)

#0  0x00007ffac3eaa28a in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffac3eaa73b in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffac3eab759 in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffac3eacdc8 in posix_memalign () from target:/lib/x86_64-linux-gnu/libc.so.6
#4  0x00007ffac2a8ad85 in njs_memalign () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#5  0x00007ffac2a7f735 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#6  0x00007ffac2a7fa06 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#7  0x00007ffac2a42aeb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#8  0x00007ffac2a5520b in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#9  0x00007ffac2a555ec in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#10 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#11 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#12 0x00007ffac2a561c2 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#13 0x00007ffac2a56440 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#14 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#15 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#16 0x00007ffac2a39b53 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#17 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#18 0x00007ffac2a329c8 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#19 0x00007ffac2a2a218 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so
#20 0x0000556030cef6a5 in ngx_http_get_indexed_variable ()
#21 0x0000556030cf0695 in ngx_http_script_copy_var_len_code ()
#22 0x0000556030cf269d in ngx_http_script_complex_value_code ()
#23 0x0000556030d26ec5 in ?? ()
#24 0x0000556030cdefbc in ngx_http_core_rewrite_phase ()
#25 0x0000556030cdaa3d in ngx_http_core_run_phases ()
#26 0x0000556030ce5b3f in ?? ()
#27 0x0000556030ce5f14 in ?? ()
#28 0x0000556030ccd6ae in ?? ()
#29 0x0000556030cc3bf6 in ngx_process_events_and_timers ()
#30 0x0000556030ccb951 in ?? ()
#31 0x0000556030cc9e7b in ngx_spawn_process ()
#32 0x0000556030ccb010 in ?? ()
#33 0x0000556030ccc327 in ngx_master_process_cycle ()
#34 0x0000556030ca330e in main ()

в общем, так мы исследовали каждого жруна — везде примерно одна и та же картина

когда они все пердохли нажравшись как комары, сервер отпустило

выкинули NJS куски — перезагрузились для чистоты эксперимента
20 минут — полёт нормальный

и вот что это было? как эти 2 детские функции сожрали всю память?.. вопрос

то что проблема с модулем NJS есть — факт

но самое главное, мы не знаем точно как выпилить этот артефакт, или чем его заменить в идеале…
чем может грозить его выпиливание
явно же это что-то древнее, может и не нужное уже

можно пропатчить всю многомилионную нашу хранилку, но чего там ожидать, чего ждёт nginx в каком виде пути?

но на всякий случай, может есть версия как-то это нативно переписать для конфига без всяких языков и модулей?
какие есть рекомендации? (совсем выкидывать всё же стрёмно…)


> On 31 Aug 2020, at 19:41, Maxim Dounin <mdounin на mdounin.ru> wrote:
> 
> Hello!
> 
> On Mon, Aug 31, 2020 at 04:48:36PM +0300, Alexey Galygin wrote:
> 
>> мы запросили у организации, которая занимается DDoS Protection
>> список всех запросов за интервал теста
>> 
>> пытаемся по этим ссылкам ходить скриптом…
>> 
>> на штатный resize пришлось не более 800 запросов за всё время эксперимента, вряд ли бы это забило всю память
>> (файлики jpg они маленькие, ну допустим текло по 1 Мб на запрос, ну утёк бы 1 Гб за 5 минут, а не 300…)
>> 
>> ну и точно такой же nginx 1.18.0 на эталонном сервере так не утекает
>> 
>> изменилось в стенде только — Ubuntu — была 16.04 стала 20.04 (тут я подозреваю, сменился аллокатор памяти, что-то с FS подкрутили, может дескрипторы если не утекают, то кэш избыточный накапливается в ОЗУ)
>> память — было 192 — стало 256
>> FS как была ext4 так и осталась ext4…
>> ЦОД — было нормальное железо — стала платформа VMWare Cloud Director… на вид работает даже шустрее
> 
> Это называется "сменилось примерно всё".
> Я бы начал с простого:
> 
> 0. Внимательно изучил конфигурацию.
> 
> Простая оценка максимального потребления памяти рабочим процессом: 
> worker_connections * (сумма всех буферов в конфиге).  Стандартные 
> размеры буферов невелики, и сумму всех можно смело считать как 
> "меньше одного мегабайта", если в конфиге задано что-то иное - 
> соответственно прибавлять.  Относительно большие размерыы буферов 
> встречаются только в image-фильтре (image_filter_buffer 1m) и mp4 
> (mp4_max_buffer_size 10m).
> 
> Нюанс: простая оценка не работает, если используются подзапросы 
> (SSI, cache background update, и так далее - каждый подзапрос 
> может иметь свои буфера, соответственно надо умножать на 
> количество подзапросов в одном соединении) и/или HTTP/2 (умножать 
> на http2_max_concurrent_streams + http2_max_concurrent_pushes).
> 
> Если результат больше или сравним с наблюдаемым потреблением 
> памяти - то чинить конфигурацию, если заметно больше - то искать 
> дальше.
> 
> Если я правильно помню приведённые фрагменты конфига, то там 4096 
> worker_connections, буферов мегабайта на 2 минимум, используется 
> background update.  Если HTTP/2 не используется, то общее 
> потребление памяти в пределе будет 4096 * 2m * 2 == 16 гигабайт.  
> Это меньше, чем наблюдается, но не сильно.  Что уже наводит на 
> всяческие мысли.  Если же ещё и HTTP/2 используется - то один 
> рабочий процесс может занимать до пары терабайт, что явно не 
> соответствует возможностям машины.
> 
> То есть в принципе все наблюдаемые эффекты могут просто 
> объясняться конфигурацией и характером нагрузки, без всяких 
> утечек.  Можно попробовать отключить HTTP/2, если вдруг 
> используется, и/или покрутить размеры буферов вниз.  Ну и 
> количество рабочих процессов тоже.
> 
> Отдельный вопрос - почему не наблюдаются такие же или близкие 
> размеры рабочих процессов на старом сервере.  Если конфигурации 
> полностью совпадают - возможно, причина в несколько другом 
> характере нагрузки, в частности - из-за смены ядра и платформы.  
> Меня, в частности, сильно смущает, что все рабочие процессы в 
> состоянии "D", смотреть стоит на дисковую подсистему.
> 
> 1. Если есть какие-то сторонние модули - исключил их из конфига 
> (или сборки, если они собраны статически), и убедился, что 
> проблема воспроизводится.  Если модули очень нужны - то можно 
> попробовать переписать конфиг так, чтобы вынести их в отдельный 
> инстанс nginx'а.
> 
> 2. Если в конфиге используется скриптовый код - perl, njs, 
> whatever - то этот код может потреблять произвольный объём памяти, 
> даже если код кажется простым.  Опять же, лучше всего попробовать 
> воспроизвести проблему без этого кода.
> 
> Ну и логи таки имеет смысл почитать, дабы точно понимать, что 
> происходит, а не строить предположения.
> 
> [...]
> 
> -- 
> Maxim Dounin
> http://mdounin.ru/
> _______________________________________________
> nginx-ru mailing list
> nginx-ru на nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-ru

----------- следущая часть -----------
Вложение в формате HTML было извлечено…
URL: <http://mailman.nginx.org/pipermail/nginx-ru/attachments/20200901/7e6b5af2/attachment-0001.htm>


Подробная информация о списке рассылки nginx-ru