Это подстрочно-надмозговый перевод статьи Majal Mirasol «Adding Brotli to an Already-built NGINX Instance». Скрипт о котором идёт речь в этот посте позволяет в одну консольную команду собрать и установить модуль Brotli для NGINX, а то и вовсе без неё. Так же он подразумевает, что в вашей системе уже установлены все необходимые пакеты для сборки NGINX из исходников. Если нет, то раскоментируйте пятую строчку в скрипте. Эта статья, как и оригинал, распространяется под лицензией GPLv3. О замечаниях и предложениях просьба писать в личку. Куда именно смотри здесь: социальности.


Есть ли у вас уже работающий на сервере NGINX к которому вы хотели бы добавить сжатие Brotli? Но что если вы не хотите перекомпилировать свою сборку NGINX (здесь подразумевается, что к нему уже подключена пачка других модулей — прим. перев.) из исходников или заменять автоматически обновляющуюся версию из репозитория? Вы можете создать динамические модули для сжатия по алгоритму Brotli которые обновляются вместе с NGINX.


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


#!/bin/bash

# https://www.majlovesreg.one/tag/code/
# https://www.majlovesreg.one/adding-brotli-to-a-built-nginx-instance
# https://github.com/majal/maj-server/tree/master/nginx-modules-brotli

# Install needed development packages if not yet installed in the system
# sudo apt -y install git libpcre3 libpcre3-dev zlib1g zlib1g-dev openssl libssl-dev

# For predefined NGINX version, use:
# ngver=1.17.1

# For passing the version via the command line (i.e.: user@server:~$ ./mkbrotli 1.17.1), use:
ngver=$1

# For automated detection of currently installed NGINX version (not to be used for auto-updating, see hooks in post), use:
# ngver=$(nginx -v 2>&1 | grep -o '[0-9\.]*')

# Get configure parameters of installed NGINX. Not needed if NGINX was configured '--with-compat'.
# Uncomment one of the lines below if the script sucessfully builds modules but NGINX throws a "not binary compatible" error.
# confparams=$(nginx -V 2>&1 | grep -o -- '--prefix='.*)
# confparams=$(nginx -V 2>&1 | grep -o -- '--[^with]'.*)
# confparams=$(nginx -V 2>&1 | grep -- '--' | sed "s/.*' //")
# confparams=$(nginx -V 2>&1 | grep -o -- "--prefix='.*'$")

# To manually set NGINX modules directory:
# moddir=/path/to/modules/directory

# To automatically select NGINX modules directory:
[ -d /usr/share/nginx/modules ] && moddir=/usr/share/nginx/modules
[ -d $(nginx -V 2>&1 | grep -o 'prefix=[^ ]*' | sed 's/prefix=//')/modules ] && moddir="$(nginx -V 2>&1 | grep -o 'prefix=[^ ]*' | sed 's/prefix=//')/modules"
[ -d $(nginx -V 2>&1 | grep -o 'modules-path=[^ ]*' | sed 's/modules-path=//') ] && moddir="$(nginx -V 2>&1 | grep -o 'modules-path=[^ ]*' | sed 's/modules-path=//')"
[ "${moddir}" ] || { echo '!! missing modules directory, exiting...'; exit 1; }

# Set temporary directory and build on it
builddir="$(mktemp -d)"
cd "${builddir}"

echo
echo '################################################################################'
echo
echo "Building Brotli for NGINX ${ngver}"
echo "Temporary build directory: ${builddir}"
echo "Modules directory: ${moddir}"
echo

# Download and unpack NGINX
wget https://nginx.org/download/nginx-${ngver}.tar.gz && { tar zxf nginx-${ngver}.tar.gz && rm nginx-${ngver}.tar.gz; } || { echo '!! download failed, exiting...'; exit 2; }

# Download, initialize, and make Brotli dynamic modules
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli && git submodule update --init && cd ../nginx-${ngver}
[ "${confparams}" ] && nice -n 19 ionice -c 3 "./configure --add-dynamic-module=../ngx_brotli ${confparams}" || nice -n 19 ionice -c 3 ./configure --with-compat --add-dynamic-module=../ngx_brotli
nice -n 19 ionice -c 3 make modules || { echo '!! configure or make failed, exiting...'; exit 4; }

# Replace Brotli in modules directory
[ -f "${moddir}/ngx_http_brotli_filter_module.so" ] && sudo mv "${moddir}/ngx_http_brotli_filter_module.so" "${moddir}/ngx_http_brotli_filter_module.so.old"
[ -f "${moddir}/ngx_http_brotli_static_module.so" ] && sudo mv "${moddir}/ngx_http_brotli_static_module.so" "${moddir}/ngx_http_brotli_static_module.so.old"
sudo cp objs/*.so "${moddir}/"
sudo chmod 644 "${moddir}/ngx_http_brotli_filter_module.so" || { echo '!! module permissions failed, exiting...'; exit 5; }
sudo chmod 644 "${moddir}/ngx_http_brotli_static_module.so" || { echo '!! module permissions failed, exiting...'; exit 6; }

# Clean up build files
cd "${builddir}/.."
sudo rm -r "${builddir}/ngx_brotli"
rm -r "${builddir}"

echo
echo "Sucessfully built and installed latest Brotli for NGINX ${ngver}"
echo "Modules can be found in ${moddir}"
echo "Next step: Configure dynamic modules and reload/restart NGINX."
echo
echo '################################################################################'
echo

# Start/restart NGINX.
# Not recommended if script is hooked since NGINX is automatically restarted by the package manager (e.g. apt) after an upgrade.
# Restarting NGINX before the upgrade could cause a module version mismatch.
# sudo nginx -t && { systemctl is-active nginx && sudo systemctl restart nginx || sudo systemctl start nginx; } || true
# echo
# systemctl --no-pager status nginx
# echo


Вы можете автоматизировать установку просто запустив этот скрипт. По умолчанию он работает с переданным через параметр командной строки номером версии NGINX. Чтобы узнать какая версия у вас установлена просто выполните эту команду в консоли:

nginx -v
nginx version: nginx/1.17.1


Как видно в этом примере номер версии 1.17.1. После того как вы скачаете этот скрипт, перейдите в директорию куда вы его скачали и выполните в терминале следующие две команды:

chmod +x mkbrotli
./mkbrotli 1.17.1

Теперь вы можете расслабится и отдохнуть пока скрипт автоматически загрузит, соберёт и установит динамические модули. Если что-то пойдёт не так дайте знать автору.


Активация модулей

Теперь, когда у нас есть модули для NGINX осталось сделать всего пару шагов, чтобы активировать Brotli сжатие.


Во-первых, добавьте модули в основной конфигурационный файл NGINX (мой находится здесь /etc/nginx/nginx.conf). Если вы не знаете где он находится выполните следующую команду в терминале:

nginx -V 2>&1 | grep -o 'conf-path=[^ ]*' | sed 's/conf-path=//'


Теперь отредактируем этот файл. Во-первых, вне любых блоков block { ... }, может быть в самой верхней части файла, добавьте следующие строки:

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

# http {
#   ...
# }


Во-вторых, активируйте Brotli (и задайте его конфигурационные директивы также известные как настройки) либо внутри блока http, либо внутри блока server ваших настроек для доменов (т.е. файлов внутри папки nginx/sites-enabled/). Что касается меня, то я положил эти директивы в директорию nginx/snippets/ и вызываю из с помощью include в мои конфигурациях для доменов.


# http {
#   ...

  brotli on;
  brotli_static on;
  brotli_types
    text/plain
    text/css
    text/xml
    text/javascript
    text/x-component
    application/xml
    application/xml+rss
    application/javascript
    application/json
    application/atom+xml
    application/vnd.ms-fontobject
    application/x-font-ttf
    application/x-font-opentype
    application/x-font-truetype
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/octet-stream
    font/opentype
    font/truetype
    font/eot
    font/otf
    image/svg+xml
    image/x-icon
    image/vnd.microsoft.icon
    image/bmp;

#   ...
# }


Автообновление вместе с NGINX

Одна из потенциальных проблем с динамическими модулями — автоматическое обновление NGINX с помощью менеджера пакетов (например aptyum, или pacman). Если вы такой же как и я, то все обновления на вашем сервере автоматизированы с помощью скриптов. Как вы можете заметить, динамические модули зависят от номера версии. NGINX не будет загружать модули собранные для другой версии. И, соответственно, возникает вопрос: как модули для Brotli будут автоматически обновляться вместе с NGINX?


Ответ состоит в том, чтобы повесить специальный хук в пакетный менеджер на обновление пакетов. В apt это займёт два или три шага:

  1. Запуск специального скрипта-воркера в хуке apt. Альтернативный вариант — хук должен напрямую вызывать mkbrotli и в этом случае воркер нам не понадобится
  2. Воркер вызывает один или несколько других скриптов-билдеров которые которые обновляют динамические модули (у нас же может быть подключён не только Brotli — прим. перев.)
  3. Билдеры собирают и устанавливают новые модули прямо перед обновлением NGINX


Вот пример моего хук-файла который находится в /etc/apt/apt.conf.d/:

// Hook to build and install dynamic modules before NGINX upgrades
// Script calls individual build scripts and passes back error codes
// Place this file in /etc/apt/apt.conf.d/

DPkg::Pre-Install-Pkgs {"/usr/local/sbin/nginx-mod-preinstall";};


А вот скрипт-воркер который я использую:

#!/bin/bash
# Call NGINX module build scripts and pass error codes to apt hook

# Get NGINX version to upgrade to
read ngfile < <(grep '/nginx_') || exit 0
ngver=$(echo $ngfile | sed 's/-.*//' | sed 's/.*_//')

# List of build scripts to run:
/usr/local/sbin/mkbrotli $ngver || exit $?
# /usr/local/sbin/mkmodsec $ngver || exit $?
# /usr/local/sbin/mkpagespeed $ngver || exit $?

И, наконец, билдер это… (барабанную дробь, пожалуйста!) конечно же mkbrotli ;-) (альтернативная ссылка для скачивания). Я надеюсь, что в NGINX когда-нибудь появится нативная поддержка Brotli потому, что это классно, но пока этого не случилось у нас есть вот такой обходной путь. Наслаждайтесь сжатием!


Google PageSpeed

PageSpeed это ещё один проект который ставит своей целью ускорение всего и вся в интернете основанный на лучших практиках по мнению Google. Для автоматической установки PageSpeed в NGINX я также написал скрипт и, если вы заинтересовались, то почитайте мой пост «Adding PageSpeed to an Already-running NGINX Instance» у меня в блоге.