Ngx_slab_alloc() failed: no memory in upstream zone

Добрый день.

Прощупываем angie внутри своей компании. Пытаемся переносить конфигурации с действующих nginx на свежую версию angie, после перекомпиляции модулей angie и попытки старта сервиса angie он выпадает с ошибкой

ngx_slab_alloc() failed: no memory in upstream zone xxxx

После увеличения памяти апстрим зоне примерно в 8 раз сервис angie поднимается и работает стабильно. При этом со старой конфигурацией (с меньшим объёмом памяти) nginx работает успешно.

Так же, при успешно запущенном angie, после обратного уменьшения памяти в апстрим зоне , сервис angie успешно релоадится (не рестартится) и применяет изменения.

Итого:

  1. Первый старт сервиса невозможен со старыми настройками памяти апстрим зон.
  2. Релоад сервиса после уменьшения памяти проходит успешно, изменения применяются
  3. Последующий рестарт сервиса с “меньшим” значением памяти выпадает с той же ошибкой.

Что это может быть?)
Спасибо

Добрый день.

Подробная статистика по каждому upstream-серверу хранится в разделяемой памяти. В nginx такой статистики в принципе не было, поэтому там требовалось меньше памяти под каждый сервер в блоке upstream. Но речь идет всего о нескольких дополнительных килобайт на каждый сервер. Если в блоке upstream указано доменное имя, то оно может резолвится в несколько серверов.

Видимо ранее размер зоны был сконфигурирован совсем крохотным?

Повторить эксперимент с уменьшением памяти и релоадом - у меня не получилось. На моей конфигурации каждый раз получал такую же ошибку при попытке уменьшить размер зоны и сервер продолжал работать с предыдущей конфигурацией, где памяти хватает.

В связи с этим просьба показать конфигурации, на которой это воспроизводится.

Если вы имеет в виду подробную статистику, которая “включается” директивой api, которой в nginx нет, то она не включена и в нашем окружении.

Фактически мы просто скопировали виртуальную машину с nginx, развернули на нём angie и пытаемся запустит сервис с той же конфигурацией (не считая смены pid в конфигурации и перекомпилирования модулей). Никаких фич, которые предоставляет angie мы не включаем, ни резолвер, ни апи.

Если говорить о конфигурации, то в конфигурации апстримов находится 40 серверов указанных в виде DNS имени, которые резолвятся только в один IP адрес (к теме про резолв в несколько адресов).

Мы посчитали что такое поведение обусловленно именно рестартом сервиса (не релоадом) и решили рестартнуть наши nginx сервисы, но они рестартнули успешно.

Более того, заметили интересную особенность.

При уменьшении размера памяти апстрим зоны с 3 бэкендами с 64кб до 2кб и прокатке команды nginx -t - нгинкс ругается “nginx: [emerg] zone “xxxx” is too small”

При уменьшении размера памяти апстримы зоны с 40 бэкендами, с “рабочих” 512кб до 2кб на сервере с angie при тестировании конфига (angie -t) сервис не ругается, при релоаде сервиса - релоадится успешно, при рестарте - ожидаемая ошибка.

Возможно angie использует какой-то другой метод “подсчёта” алоцируемой памяти в апстрим зонах?

Метрики считаются всегда, когда сервер собран с модулем API. С точки зрения производительности - гораздо дешевле увеличивать счетчики, чем на каждый счетчик делать отдельную проверку - включена ли где-то в конфигурации директива api или нет.

Точно также ведет себя и модуль stub_status в nginx - если он собран, то на счетчики выделяется память и они считаются постоянно.

Уменьшил размер зоны до 2k, при reload получил:

2023/07/20 20:28:40 [notice] 23505#23505: signal process started
2023/07/20 20:28:40 [emerg] 23482#23482: zone "one" is too small in /home/vbart/Development/angie/build/../test.conf:15

Аналогично с angie -t:

% sbin/angie -t -c ../test.conf
angie: [emerg] zone "one" is too small in /home/vbart/Development/angie/build/../test.conf:15
angie: configuration file /home/vbart/Development/angie/build/../test.conf test failed

Данная ошибка выдается когда указанное в директиве zone значение меньше, чем 8 страничек памяти. Соответствующее условие в коде nginx при проверке синтаксиса директивы:

В Angie эта проверка никак не менялась:

Как и вообще не менялся код функции, обрабатывающий данную директиву.

Можете привести минимальный пример конфигурации, на котором наблюдалось у вас такое поведение? И укажите пожалуйста на какой системе? Angie установлен из наших пакетов?

Конфигурация site-enabled:

server {
    listen 80;
    server_name test.domain.com;

    access_log /var/log/nginx/test.domain.com.access.log main;
    error_log  /var/log/nginx/test.domain.com.error.log;

    include /etc/nginx/access_list/list1.list;
    include /etc/nginx/access_list/list2.list;
    deny all;

    set $my_real_ip $remote_addr;
    if ($http_x_real_ip) {
        set $my_real_ip $http_x_real_ip;
    }

    client_max_body_size 128m;
    large_client_header_buffers 8 64k;
    charset utf-8;

    location / {

        proxy_set_header X-Real-IP $my_real_ip;
        proxy_set_header Host $host;
        proxy_read_timeout 120s;
        proxy_pass http://test;
        proxy_next_upstream error;

        error_page 502 503 504 =503 /503.json;
    }

    location = /metrics {
        deny all;
    }

    include /etc/nginx/conf.d/error-handling.conf;
}

Конфигурация апстримов используемых этим сайтом:

upstream test {
    server host01.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host01.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host02.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host02.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host03.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host03.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host04.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host04.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host05.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host05.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host06.az1.local:11111 max_fails=3 fail_timeout=10s backup;
    server host06.az1.local:11112 max_fails=3 fail_timeout=10s backup;
    server host01.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host01.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host02.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host02.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host03.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host03.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host04.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host04.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host05.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host05.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host06.az2.local:11111 max_fails=3 fail_timeout=10s backup;
    server host06.az2.local:11112 max_fails=3 fail_timeout=10s backup;
    server host01.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host01.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host02.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host02.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host03.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host03.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host04.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host04.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host05.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host05.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host06.az3.local:11111 max_fails=3 fail_timeout=10s;
    server host06.az3.local:11112 max_fails=3 fail_timeout=10s;
    server host01.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host01.az4.local:11112 max_fails=3 fail_timeout=10s;
    server host02.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host02.az4.local:11112 max_fails=3 fail_timeout=10s;
    server host03.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host03.az4.local:11112 max_fails=3 fail_timeout=10s;
    server host04.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host04.az4.local:11112 max_fails=3 fail_timeout=10s;
    server host05.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host05.az4.local:11112 max_fails=3 fail_timeout=10s;
    server host06.az4.local:11111 max_fails=3 fail_timeout=10s;
    server host06.az4.local:11112 max_fails=3 fail_timeout=10s;
    zone test 64k;
}
  • Доменные имена изменены

Вывод команды nginx -V:

nginx version: nginx/1.20.1
built by gcc 8.3.0 (Debian 8.3.0-6)
built with OpenSSL 1.1.1d  10 Sep 2019 (running with OpenSSL 1.1.1n  15 Mar 2022)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.20.1/debian/debuild-base/nginx-1.20.1=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

Вывод команды angie -V:

Angie version: Angie/1.2.0
built by gcc 8.3.0 (Debian 8.3.0-6)
built with OpenSSL 1.1.1n  15 Mar 2022
TLS SNI support enabled
configure arguments: --prefix=/etc/angie --conf-path=/etc/angie/angie.conf --error-log-path=/var/log/angie/error.log --http-log-path=/var/log/angie/access.log --lock-path=/run/angie.lock --modules-path=/usr/lib/angie/modules --pid-path=/run/angie.pid --sbin-path=/usr/sbin/angie --http-client-body-temp-path=/var/cache/angie/client_temp --http-fastcgi-temp-path=/var/cache/angie/fastcgi_temp --http-proxy-temp-path=/var/cache/angie/proxy_temp --http-scgi-temp-path=/var/cache/angie/scgi_temp --http-uwsgi-temp-path=/var/cache/angie/uwsgi_temp --user=angie --group=angie --with-file-aio --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads --with-http_image_filter_module=dynamic --with-http_perl_module=dynamic --with-http_xslt_module=dynamic --add-dynamic-module=module-auth-spnego-v1-1-1 --add-dynamic-module=module-brotli-v1-0-0rc --add-dynamic-module=module-cache-purge-2-5-3 --add-dynamic-module=module-dav-ext-v3-0-0 --add-dynamic-module=module-echo-v0-63 --add-dynamic-module=module-geoip2-3-4 --add-dynamic-module=module-headers-more-v0-34 --add-dynamic-module=module-jwt-v3-2-0 --add-dynamic-module=module-keyval-0-1-0 --add-dynamic-module=module-lua-v0-10-24 --add-dynamic-module=module-lua-stream-v0-0-13 --add-dynamic-module=module-ndk-v0-3-2 --add-dynamic-module=module-njs-0-8-0/nginx --add-dynamic-module=module-postgres-1-0rc7 --add-dynamic-module=module-redis2-v0-15 --add-dynamic-module=module-rtmp-v1-2-2 --add-dynamic-module=module-set-misc-v0-33 --add-dynamic-module=module-subs-e12e965ac1837ca709709f9a26f572a54d83430e --add-dynamic-module=module-upload-2-3-0 --add-dynamic-module=module-vod-1-31 --with-cc-opt='-g -O2 -fdebug-prefix-map=/home/jenkins/releng/work/build/angie-1.2.0=. -fstack-protector-strong -Wformat -Werror=format-security' --with-ld-opt='-Wl,-z,relro -Wl,-z,now'

Обе системы на базе Debian 10, количество ОЗУ одинаковое - 8Гб, и nginx и angie устанавливались из официальных репозиториев, оба используют модули - vts, geoip (под анжи скомпилированы отдельно).

Так же хост с angie не находится под нагрузкой, трафик на него не идёт, на хост с nginx - идёт постоянный трафик

Если без сторонних модулей проверить, изменение зоны на 2k по прежнему не выдает ошибки при angie -t? Команда angie -T при этом выдает конфиг, в котором зона 2k? Как убеждаетесь в том, что reload проходит успешно?

Добрый день.

Да, заметил ошибку в проверке конфига.
При выполнении angie -t или angie -T он считывает конфиг по пути, который указан при сборке приложения, а поскольку мы его не собирали, а скачивали с репозиториев, то по дефолту angie считает конфигурационным файлом - /etc/angie/angie.conf который проходит проверку без проблем. У нас же конфигурационный файл - /etc/nginx/nginx.conf поскольку полностью переносили конфигурацию с действующего nginx.

Но вопрос с ошибкой аллокации памяти апстрим зон остаётся открытым.

Я почему и спросил о том, как было установлено, что reload выполнен успешно. Дело в том, что операция перезагрузки конфигурации осуществляется путем отправки сигнала, а сигналы не имеют обратной связи. Получив сигнал, процесс master перечитывает конфигурацию, создает и заполняет новые зоны разделяемой памяти. Если на данном этапе случается какая-то ошибка, то она пишется в лог и процедура на этом завершается, а рабочие процессы продолжают работать с предыдущей конфигурацией.

Таким образом, узнать успешен был reload или нет, что в nginx, что в Angie - можно только проверив error log после отправки сигнала или же убедившись, что новые рабочие процессы запустились (у рабочих процессов поменяется pid).

Да, действительно, при reload ошибки не возникает, в логе пишется ошибка

2023/07/24 17:28:12 [warn] 31600#31600: could not build optimal variables_hash, you should increase either variables_hash_max_size: 1024 or variables_hash_bucket_size: 64; ignoring variables_hash_bucket_size

При этом PID не меняется, и при angie -t действительно тестируется конфигурация по пути, указанном в процессе компиляции (скрипт configure).

Cпасибо большое за помощь, топик можно закрывать

Спасибо вам, что обратили внимание на проблему с возросшим потреблением памяти в upstream-зоне. У нас родилась идея, как можно уменьшить потребление и в следующей версии это будет реализовано.

При сборке по умолчанию, т.е. без флагов --without-http_api_module и --without-http_upstream_sticky_module - оно все равно будет по объективным обстоятельствам повыше, чем у nginx, не имеющего данных возможностей, но оно уменьшится примерно на 3Кб на каждый сервер по сравнению с текущим потреблением.

Если же собирать с флагами --without-http_api_module и --without-http_upstream_sticky_module, то потребление памяти в upstream-зонах и сейчас не должно быть больше, чем у nginx.