<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">в общем<div class="">передаю новости с полей</div><div class=""><br class=""></div><div class="">жруна нашли — это NJS, на паре детских безобидных функций</div><div class=""><br class=""></div><div class="">TL;DR</div><div class=""><br class=""></div><div class="">измерили скорости дисков, потрясли поддержку… даже уже собрались всё переключить на SSD (Tier1)… но</div><div class=""><br class=""></div><div class="">понизили воркеров до 20, убедились, что их не больше</div><div class="">понизили коннекты на каждого до 2048</div><div class=""><br class=""></div><div class="">включили лимит на sendfile — 1m</div><div class=""><br class=""></div><div class="">включили aio on</div><div class=""><br class=""></div><div class="">отключили http2 — пусть DDoS/CDN оператор транслирует другие протоколы, не важно</div><div class=""><br class=""></div><div class="">на боевом сервере проверили эту же конфигурацию днём — сбоев нет на более слабом железе</div><div class=""><br class=""></div><div class="">* * *</div><div class=""><br class=""></div><div class="">затаились с коллегами в кустах</div><div class="">включили в полночь аплинк и стали ждать 5 минут (пока он рассосётся)… нервно поглядывая на часы</div><div class=""><br class=""></div><div class="">казалось бы всё ровно, так иногда мигает “D”, но в целом “R”, стабильно</div><div class="">и только расслабились, как, вдруг, началось…</div><div class=""><br class=""></div><div class="">как это происходит?</div><div class="">кто-то входит на мозольную URL (мы пока не знаем какую), после чего начинается труба!</div><div class=""><br class=""></div><div class="">не дожидаясь вырубили аплинк, ушли на старый сервер</div><div class="">но смертный жор продолжился</div><div class=""><br class=""></div><div class="">все воркеры начинают (продолжают) есть память по старшинству, даже уже без нагрузки</div><div class=""><br class=""></div><div class="">как только OOM-K убивает одного, другой начинает всё дожирать</div><div class="">и так пока все не будут убиты</div><div class=""><br class=""></div><div class="">в процессе стали смотреть, чем же они там заняты без нагрузки</div><div class=""><br class=""></div><div class="">оказалось проблема в NJS части, какой-то баг там именно в связке 1.18.0 + Ubuntu 20.04</div><div class=""><br class=""></div><div class="">у нас есть такой артефакт (переписанный с Perl на NJS):</div><div class=""><br class=""></div><div class=""><pre class="c-mrkdwn__pre" data-stringify-type="pre" style="box-sizing: inherit; margin-top: 4px; margin-bottom: 4px; padding: 8px; --saf-0: rgba(var(--sk_foreground_low,29,28,29),0.13); line-height: 1.50001; font-variant-ligatures: none; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; tab-size: 4; border: 1px solid var(--saf-0); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background: rgba(var(--sk_foreground_min,29,28,29),0.04); counter-reset: list-0 0 list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; color: rgb(29, 28, 29); orphans: 2; widows: 2; font-family: Monaco, Menlo, Consolas, "Courier New", monospace !important;">function unescapeURI(r) {<br style="box-sizing: inherit;" class=""> return r.uri.replace(/%20/g, " ");<br style="box-sizing: inherit;" class="">}<span class="c-mrkdwn__br" data-stringify-type="paragraph-break" style="box-sizing: inherit; display: block; height: unset;"></span>function escapeURI(r) {<br style="box-sizing: inherit;" class=""> return r.uri.replace(/\s/g, "%20").replace(/_/g, "%5F");<br style="box-sizing: inherit;" class="">};</pre><div class=""><br class=""></div></div><div class=""><pre class="c-mrkdwn__pre" data-stringify-type="pre" style="box-sizing: inherit; margin-top: 4px; margin-bottom: 4px; padding: 8px; --saf-0: rgba(var(--sk_foreground_low,29,28,29),0.13); line-height: 1.50001; font-variant-ligatures: none; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; tab-size: 4; border: 1px solid var(--saf-0); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background: rgba(var(--sk_foreground_min,29,28,29),0.04); counter-reset: list-0 0 list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; color: rgb(29, 28, 29); orphans: 2; widows: 2; font-family: Monaco, Menlo, Consolas, "Courier New", monospace !important;">conf.d/ege.conf:81: try_files $uri $escaped_uri $unescaped_uri =404;<br style="box-sizing: inherit;" class="">nginx.conf:123: js_set $unescaped_uri unescapeURI;<br style="box-sizing: inherit;" class="">nginx.conf:124: js_set $escaped_uri escapeURI;<br style="box-sizing: inherit;" class="">parts/file-storage-new.conf:51: try_files $uri $escaped_uri $unescaped_uri =404;<br style="box-sizing: inherit;" class="">parts/file-storage-new.conf:55: try_files $uri $escaped_uri $unescaped_uri =404;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:23: try_files $uri $escaped_uri $unescaped_uri @backendCache-15m;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:40: try_files $uri $escaped_uri $unescaped_uri @backend;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:47: try_files $uri $escaped_uri $unescaped_uri @backend;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:59: try_files $uri $escaped_uri @backendCache-alljs;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:117: try_files $uri $escaped_uri @backend @accessTicketFailback;<br style="box-sizing: inherit;" class="">parts/static-stuff-new.conf:124: try_files $uri $escaped_uri @backend @accessTicketFailback;</pre><div class=""><br class=""></div></div><div class="">всё ради того, чтобы какие-то адские бубны, чтобы пробел и подчёркивание заменить на %20 %5F и наоборот…</div><div class=""><br class=""></div><div class="">больше NJS ни для чего не нужен, но все воркеры, которые жрали сидели примерно в этих местах:</div><div class=""><br class=""></div><div class=""><pre class="c-mrkdwn__pre" data-stringify-type="pre" style="box-sizing: inherit; margin-top: 4px; margin-bottom: 4px; padding: 8px; --saf-0: rgba(var(--sk_foreground_low,29,28,29),0.13); line-height: 1.50001; font-variant-ligatures: none; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; tab-size: 4; border: 1px solid var(--saf-0); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background: rgba(var(--sk_foreground_min,29,28,29),0.04); counter-reset: list-0 0 list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; color: rgb(29, 28, 29); orphans: 2; widows: 2; font-family: Monaco, Menlo, Consolas, "Courier New", monospace !important;">0x00007ffac2a7e4cb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">(gdb) bt<br style="box-sizing: inherit;" class="">#0 0x00007ffac2a7e4cb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#1 0x00007ffac2a552ba in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#2 0x00007ffac2a555ec in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#3 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#4 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#5 0x00007ffac2a561c2 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#6 0x00007ffac2a56440 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#7 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#8 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#9 0x00007ffac2a39b53 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#10 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#11 0x00007ffac2a329c8 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#12 0x00007ffac2a2a218 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#13 0x0000556030cef6a5 in ngx_http_get_indexed_variable ()<br style="box-sizing: inherit;" class="">#14 0x0000556030cf0695 in ngx_http_script_copy_var_len_code ()<br style="box-sizing: inherit;" class="">#15 0x0000556030cf269d in ngx_http_script_complex_value_code ()<br style="box-sizing: inherit;" class="">#16 0x0000556030d26ec5 in ?? ()<br style="box-sizing: inherit;" class="">#17 0x0000556030cdefbc in ngx_http_core_rewrite_phase ()<br style="box-sizing: inherit;" class="">#18 0x0000556030cdaa3d in ngx_http_core_run_phases ()<br style="box-sizing: inherit;" class="">#19 0x0000556030ce5b3f in ?? ()<br style="box-sizing: inherit;" class="">#20 0x0000556030ce5f14 in ?? ()<br style="box-sizing: inherit;" class="">#21 0x0000556030ccd6ae in ?? ()<br style="box-sizing: inherit;" class="">#22 0x0000556030cc3bf6 in ngx_process_events_and_timers ()<br style="box-sizing: inherit;" class="">#23 0x0000556030ccb951 in ?? ()<br style="box-sizing: inherit;" class="">#24 0x0000556030cc9e7b in ngx_spawn_process ()<br style="box-sizing: inherit;" class="">#25 0x0000556030ccb010 in ?? ()<br style="box-sizing: inherit;" class="">#26 0x0000556030ccc327 in ngx_master_process_cycle ()<br style="box-sizing: inherit;" class="">#27 0x0000556030ca330e in main ()<br style="box-sizing: inherit;" class="">(gdb)</pre><div class=""><br class=""></div></div><div class=""><pre class="c-mrkdwn__pre" data-stringify-type="pre" style="box-sizing: inherit; margin-top: 4px; margin-bottom: 4px; padding: 8px; --saf-0: rgba(var(--sk_foreground_low,29,28,29),0.13); line-height: 1.50001; font-variant-ligatures: none; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; tab-size: 4; border: 1px solid var(--saf-0); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background: rgba(var(--sk_foreground_min,29,28,29),0.04); counter-reset: list-0 0 list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; color: rgb(29, 28, 29); orphans: 2; widows: 2; font-family: Monaco, Menlo, Consolas, "Courier New", monospace !important;">#0 0x00007ffac3eaa28a in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6<br style="box-sizing: inherit;" class="">#1 0x00007ffac3eaa73b in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6<br style="box-sizing: inherit;" class="">#2 0x00007ffac3eab759 in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6<br style="box-sizing: inherit;" class="">#3 0x00007ffac3eacdc8 in posix_memalign () from target:/lib/x86_64-linux-gnu/libc.so.6<br style="box-sizing: inherit;" class="">#4 0x00007ffac2a8ad85 in njs_memalign () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#5 0x00007ffac2a7f735 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#6 0x00007ffac2a7fa06 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#7 0x00007ffac2a42aeb in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#8 0x00007ffac2a5520b in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#9 0x00007ffac2a555ec in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#10 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#11 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#12 0x00007ffac2a561c2 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#13 0x00007ffac2a56440 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#14 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#15 0x00007ffac2a54775 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#16 0x00007ffac2a39b53 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#17 0x00007ffac2a54343 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#18 0x00007ffac2a329c8 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#19 0x00007ffac2a2a218 in ?? () from target:/usr/lib/nginx/modules/ngx_http_js_module.so<br style="box-sizing: inherit;" class="">#20 0x0000556030cef6a5 in ngx_http_get_indexed_variable ()<br style="box-sizing: inherit;" class="">#21 0x0000556030cf0695 in ngx_http_script_copy_var_len_code ()<br style="box-sizing: inherit;" class="">#22 0x0000556030cf269d in ngx_http_script_complex_value_code ()<br style="box-sizing: inherit;" class="">#23 0x0000556030d26ec5 in ?? ()<br style="box-sizing: inherit;" class="">#24 0x0000556030cdefbc in ngx_http_core_rewrite_phase ()<br style="box-sizing: inherit;" class="">#25 0x0000556030cdaa3d in ngx_http_core_run_phases ()<br style="box-sizing: inherit;" class="">#26 0x0000556030ce5b3f in ?? ()<br style="box-sizing: inherit;" class="">#27 0x0000556030ce5f14 in ?? ()<br style="box-sizing: inherit;" class="">#28 0x0000556030ccd6ae in ?? ()<br style="box-sizing: inherit;" class="">#29 0x0000556030cc3bf6 in ngx_process_events_and_timers ()<br style="box-sizing: inherit;" class="">#30 0x0000556030ccb951 in ?? ()<br style="box-sizing: inherit;" class="">#31 0x0000556030cc9e7b in ngx_spawn_process ()<br style="box-sizing: inherit;" class="">#32 0x0000556030ccb010 in ?? ()<br style="box-sizing: inherit;" class="">#33 0x0000556030ccc327 in ngx_master_process_cycle ()<br style="box-sizing: inherit;" class="">#34 0x0000556030ca330e in main ()</pre><div class=""><br class=""></div></div><div class=""><div>в общем, так мы исследовали каждого жруна — везде примерно одна и та же картина</div><div><br class=""></div><div>когда они все пердохли нажравшись как комары, сервер отпустило</div><div><br class=""></div><div>выкинули NJS куски — перезагрузились для чистоты эксперимента</div><div>20 минут — полёт нормальный</div><div><br class=""></div><div>и вот что это было? как эти 2 детские функции сожрали всю память?.. вопрос</div><div><br class=""></div><div>то что проблема с модулем NJS есть — факт</div><div><br class=""></div><div>но самое главное, мы не знаем точно как выпилить этот артефакт, или чем его заменить в идеале…</div><div>чем может грозить его выпиливание</div><div>явно же это что-то древнее, может и не нужное уже</div><div><br class=""></div><div>можно пропатчить всю многомилионную нашу хранилку, но чего там ожидать, чего ждёт nginx в каком виде пути?</div><div><br class=""></div><div>но на всякий случай, может есть версия как-то это нативно переписать для конфига без всяких языков и модулей?</div><div>какие есть рекомендации? (совсем выкидывать всё же стрёмно…)</div><div><br class=""></div><div><br class=""><blockquote type="cite" class=""><div class="">On 31 Aug 2020, at 19:41, Maxim Dounin <<a href="mailto:mdounin@mdounin.ru" class="">mdounin@mdounin.ru</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Hello!<br class=""><br class="">On Mon, Aug 31, 2020 at 04:48:36PM +0300, Alexey Galygin wrote:<br class=""><br class=""><blockquote type="cite" class="">мы запросили у организации, которая занимается DDoS Protection<br class="">список всех запросов за интервал теста<br class=""><br class="">пытаемся по этим ссылкам ходить скриптом…<br class=""><br class="">на штатный resize пришлось не более 800 запросов за всё время эксперимента, вряд ли бы это забило всю память<br class="">(файлики jpg они маленькие, ну допустим текло по 1 Мб на запрос, ну утёк бы 1 Гб за 5 минут, а не 300…)<br class=""><br class="">ну и точно такой же nginx 1.18.0 на эталонном сервере так не утекает<br class=""><br class="">изменилось в стенде только — Ubuntu — была 16.04 стала 20.04 (тут я подозреваю, сменился аллокатор памяти, что-то с FS подкрутили, может дескрипторы если не утекают, то кэш избыточный накапливается в ОЗУ)<br class="">память — было 192 — стало 256<br class="">FS как была ext4 так и осталась ext4…<br class="">ЦОД — было нормальное железо — стала платформа VMWare Cloud Director… на вид работает даже шустрее<br class=""></blockquote><br class="">Это называется "сменилось примерно всё".<br class="">Я бы начал с простого:<br class=""><br class="">0. Внимательно изучил конфигурацию.<br class=""><br class="">Простая оценка максимального потребления памяти рабочим процессом: <br class="">worker_connections * (сумма всех буферов в конфиге). Стандартные <br class="">размеры буферов невелики, и сумму всех можно смело считать как <br class="">"меньше одного мегабайта", если в конфиге задано что-то иное - <br class="">соответственно прибавлять. Относительно большие размерыы буферов <br class="">встречаются только в image-фильтре (image_filter_buffer 1m) и mp4 <br class="">(mp4_max_buffer_size 10m).<br class=""><br class="">Нюанс: простая оценка не работает, если используются подзапросы <br class="">(SSI, cache background update, и так далее - каждый подзапрос <br class="">может иметь свои буфера, соответственно надо умножать на <br class="">количество подзапросов в одном соединении) и/или HTTP/2 (умножать <br class="">на http2_max_concurrent_streams + http2_max_concurrent_pushes).<br class=""><br class="">Если результат больше или сравним с наблюдаемым потреблением <br class="">памяти - то чинить конфигурацию, если заметно больше - то искать <br class="">дальше.<br class=""><br class="">Если я правильно помню приведённые фрагменты конфига, то там 4096 <br class="">worker_connections, буферов мегабайта на 2 минимум, используется <br class="">background update. Если HTTP/2 не используется, то общее <br class="">потребление памяти в пределе будет 4096 * 2m * 2 == 16 гигабайт. <br class="">Это меньше, чем наблюдается, но не сильно. Что уже наводит на <br class="">всяческие мысли. Если же ещё и HTTP/2 используется - то один <br class="">рабочий процесс может занимать до пары терабайт, что явно не <br class="">соответствует возможностям машины.<br class=""><br class="">То есть в принципе все наблюдаемые эффекты могут просто <br class="">объясняться конфигурацией и характером нагрузки, без всяких <br class="">утечек. Можно попробовать отключить HTTP/2, если вдруг <br class="">используется, и/или покрутить размеры буферов вниз. Ну и <br class="">количество рабочих процессов тоже.<br class=""><br class="">Отдельный вопрос - почему не наблюдаются такие же или близкие <br class="">размеры рабочих процессов на старом сервере. Если конфигурации <br class="">полностью совпадают - возможно, причина в несколько другом <br class="">характере нагрузки, в частности - из-за смены ядра и платформы. <br class="">Меня, в частности, сильно смущает, что все рабочие процессы в <br class="">состоянии "D", смотреть стоит на дисковую подсистему.<br class=""><br class="">1. Если есть какие-то сторонние модули - исключил их из конфига <br class="">(или сборки, если они собраны статически), и убедился, что <br class="">проблема воспроизводится. Если модули очень нужны - то можно <br class="">попробовать переписать конфиг так, чтобы вынести их в отдельный <br class="">инстанс nginx'а.<br class=""><br class="">2. Если в конфиге используется скриптовый код - perl, njs, <br class="">whatever - то этот код может потреблять произвольный объём памяти, <br class="">даже если код кажется простым. Опять же, лучше всего попробовать <br class="">воспроизвести проблему без этого кода.<br class=""><br class="">Ну и логи таки имеет смысл почитать, дабы точно понимать, что <br class="">происходит, а не строить предположения.<br class=""><br class="">[...]<br class=""><br class="">-- <br class="">Maxim Dounin<br class=""><a href="http://mdounin.ru/" class="">http://mdounin.ru/</a><br class="">_______________________________________________<br class="">nginx-ru mailing list<br class="">nginx-ru@nginx.org<br class="">http://mailman.nginx.org/mailman/listinfo/nginx-ru</div></div></blockquote></div><br class=""></div></body></html>