Сервер Angie вобрал в себя всю функциональность, которая десятилетиями формировалась в Nginx. Кроме того, разработчики за несколько лет добавили еще несколько модулей и расширили возможности существующих. Тем не менее, при реализации сложных задач может потребоваться индивидуальное решение, нестандартное поведение сервера и кастомизированная конфигурация. В таких задачах полезно использовать модули для расширения функциональности сервера на базе различных языков программирования: доступны njs, Lua и Perl.

Навигация по циклу

  1. Почему стоит переходить на Angie.

  2. Установка Angie из пакетов и в докере.

  3. Переезд с Nginx на Angie. Пошаговая инструкция.

  4. Настройка location в Angie. Разделение динамических и статических запросов.

  5. Перенаправления в Angie: return, rewrite и примеры их применения.

  6. Сжатие текста в Angie: статика, динамика, производительность.

  7. Серверное кэширование в Angie: тонкости настройки.

  8. Настройка TLS в Angie: безопасность и скорость.

  9. Настройка Angie в роли обратного HTTP-прокси.

  10. Балансировка нагрузки для HTTP(S) в Angie.

  11. Мониторинг Angie с помощью Console Light и API.

  12. Балансировка и проксирование L4-трафика в Angie.

  13. Клиентское кэширование в Angie.

  14. Динамические группы проксируемых серверов в Angie.

  15. Мониторинг Angie с Prometheus и Grafana.

  16. Отказоустойчивый кластер Angie с VRRP и Keepalived.

  17. Контроль доступа в Angie.

  18. Аутентификация клиентов в Angie с помощью TLS-сертификатов.

  19. Кастомизация Angie (njs, Lua, Perl).

  20. Запуск CGI-скриптов в Angie.

Видеоверсия

Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.

Модуль Perl

Начнём с классического модуля Perl, вносящего динамику в конфигурацию Angie. На всякий случай: документация предупреждает нас об экспериментальном статусе модуля. Он появился на ранних этапах развития Nginx и до сих пор поставляется в комплекте с Angie. Хотя сегодня язык Perl не пользуется большой популярностью у разработчиков, он имеет ряд преимуществ. Во‑первых, интерпретатор Perl входит в комплект большинства дистрибутивов Linux, то есть обладает высокой совместимостью. Также, язык обладает очень высокой стабильностью с точки зрения синтаксиса. Если вы найдёте решение, написанное лет 20 назад, практически всегда оно будет работать на современной версии интерпретатора.

Использовать модуль Perl можно для нескольких задач:

  • определение внутренних переменных Angie;

  • подключение полноценных обработчиков запросов;

  • использование при включении Perl‑скриптов в SSI.

Модуль Perl работает только в контексте HTTP. Установка и подключение модуля стандартны:

apt install angie-module-perl

Подключение (в контексте main):

load_module modules/ngx_http_perl_module.so;

Рассмотрим несложный пример определения переменных. Задача следующая: конвертировать URI запросов в нижний регистр. Например, /Uri -> /uri, /URI -> /uri, /TeSt -> /test.

Для решения задачи настроим переменную с изменённым URI через подпрограмму Perl:

http {
  perl_set $my_uri_to_lowercase 'sub {
    my $r = shift;
    my $uri = $r->uri;
    $uri = lc($uri);
    return $uri;
  }';
}

В директиве perl_set мы создаём переменную $my_uri_to_lowercase, которая будет содержать новый URI запроса. Для этого мы заносим в Perl‑переменную $uri исходный URI запроса и применяем к нему функцию lc — перевод в нижний регистр. Результат работы функции присваивается Angie‑переменной $my_uri_to_lowercase.

Использование новой переменной можно реализовать следующим образом: создаём локацию с для URI с символами в верхнем регистре и возвращаем редирект на новый URI из переменной:

server {
  location ~ [A-Z] {
    return 301 $scheme://$host$my_uri_to_lowercase;
  }
}

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

Для реализации более сложных сценариев наверняка пот��ебуется подключить дополнительные модули Perl (из CPAN). Сделать это можно директивой perl_require. Рассмотрим пример с подключением модуля и установкой обработчика запроса. Конфигурация Angie:

http {
    perl_modules perl/lib;
    perl_require hello.pm;

    server {
        location / {
            perl hello::handler;
        }
    } 
}

Здесь указывается директория для поиска Perl‑модулей (perl_modules) и подключается модуль hello.pm. В серверном блоке для локации определяется обработчик hello::handler — вызов функции handler из модуля hello.pm.

Код модуля Perl для обработчика (hello.pm):

package hello;

use nginx;

sub handler {
    my $r = shift;

    $r->send_http_header("text/html");
    return OK if $r->header_only;

    $r->print("hello!\n<br/>");

    if (-f $r->filename or -d _) {
        $r->print($r->uri, " exists!\n");
    }

    return OK;
}

1;
__END__

Основная часть модуля — функция обработчика запроса (handler). Она выводит текст «hello!» и в случае наличия файла или директории, совпадающих с URI, добавляет «$uri exists!»

Важно понимать, что Perl‑модуль работает в синхронном режиме, поэтому во время исполнения кода рабочий процесс не может обрабатывать другие запросы. По этой причине не рекомендуется реализовывать таким образом длительные операции с непредсказуемым временем ответа.

Модуль Lua

Сторонние модули позволяют использовать популярный скриптовый язык Lua в Angie. Мы рассмотрим базовые возможности модуля lua из комплекта фреймворка OpenResty.

При подключении модуля lua также необходим модуль NDK. Установка:

apt install angie-module-lua

Подключение модулей:

load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;

Для примера использования модуля будем использовать задачу записи тела запроса (request body) в лог-файл. Для этого определим соответствующий формат лога и дополнительную переменную:

http {
  log_format bodylog '$remote_addr - $remote_user [$time_local] '
      '"$request" $status $body_bytes_sent '
      '"$http_referer" "$http_user_agent" $request_time '
      '<"$request_body" >"$resp_body"';
  lua_need_request_body on;

  body_filter_by_lua '
    local resp_body = string.sub(ngx.arg[1], 1, 1000)
    ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
    if ngx.arg[2] then
      ngx.var.resp_body = ngx.ctx.buffered
    end
  ';

  server {
    listen 80;
    set $resp_body "";
    access_log  /var/log/nginx/server.log bodylog;
  }
}

Этот код добавляет специальный формат лога и устанавливает директиву lua_need_request_body on, чтобы у Lua был доступ к телу запроса. Далее добавлен обработчик для тела запроса на Lua, задача которого — заполнить переменную в Angie ($resp_body). В данном примере размер ограничен 1000 байтами. Сама переменная объявляется на уровне блока сервера, и там же используется кастомный access_log.

В отличие от модуля Perl, Lua можно использовать как в контексте HTTP, так и в Stream. Кроме того, Lua позволяет создавать неблокирующий код для доступа к внешним системам. В общем, возможности модуля Lua широки и охватывают практически все аспекты работы с запросом и ответом в Angie.

Модуль NJS

Наконец, мы добрались до модуля NJS. Пожалуй, это самый естественный способ расширения функциональности для Angie. Дело в том, что NJS — подмножество языка JavaScript, специально адаптированное для использования в Nginx (а значит, и в Angie). Создание модуля NJS было обусловлено необходимостью создания гибких конфигураций сервера и нестандартных сценариев обработки запросов.

Модуль NJS поддерживает работу как в контексте HTTP, так и в Stream. Установка модуля стандартна:

apt install angie-module-lua

Подключение модуля, как обычно, производится в контексте main (существуют отдельные модули для http и stream):

load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;

Для примера возьмём задачу перевода тела ответа в нижний регистр. Будем использовать код примеров из репозитория. Клонируем репозиторий в директорию /etc/angie:

cd /etc/angie
git clone https://github.com/nginx/njs-examples.git

В конфигурации сервера подключим файлы скриптов и обработчик NJS.

http {
  js_path "/etc/angie/njs-examples/njs/";
  js_import main from http/response/to_lower_case.js;

  server {
        listen 80;
        location / {
            js_body_filter main.to_lower_case;
            proxy_pass http://localhost:8080;
        }
  }

  server {
        listen 8080;
        location / {
            return 200 'Hello World';
        }
  }
}

Директива js_path определяет директорию для поиска файлов njs-скриптов. Далее мы импортируем файл скрипта по относительному пути (код мы получили из репозитория).

Первый блок server (80 порт) отвечает за основную точку входа в наше импровизированное приложение. При этом в корневой локации подключён обработчик js_body_filter. Он будет вызывать функцию to_lower_case из файла to_lower_case.js для обработки тела ответа. Код скрипта максимально лаконичен:

function to_lower_case(r, data, flags) {
    r.sendBuffer(data.toLowerCase(), flags);
}
export default {to_lower_case};

По сути, скрипт приводит к нижнему регистру любые входящие данные. 

Второй блок server (8080) выступает бэкендом и отвечает на любой запрос фразой «Hello World»

При проверке такой конфигурации увидим эффект:

curl localhost
hello world

Возможности работы с запросами, ответами и заголовками здесь очень широкие. Для быстрого старта можно начать с примеров в эталонном репозитории модуля.

Итоги

Возможности Angie можно расширять за счет стандартных и сторонних модулей на базе ряда языков программирования. С помощью программной обработки можно преобразовывать заголовки, модифицировать ответ или запрос, создать собственную систему аутентификации. Наиболее близким по задумке к Angie является модуль NJS, так как он создавался специально для расширения возможностей сервера разработчиками Nginx. То есть, NJS в какой‑то степени позволяет создавать динамические модули для сервера без необходимости работать c внутренностями сервера Angie на языке C.

Следующая статья цикла: Запуск CGI-скриптов в Angie.