Глеб Гончаров

Сисадмин и девопс из Ульяновска

О работе gzip_no_buffer в NGINX

В сентябре вышел пост SRE Dropbox об оптимизации веб-серверов в их геораспределённой сети доставки контента. Я заинтересовался опцией gzip_no_buffer в NGINX для сокращения времени получения первого байта TCP-пакета с сервера (TTFB).

Этой опции нет в официальной документации, а единственное упоминание в книге NGINX HTTP Server Кле́мана Неде́льку вторит ответу Игоря Сысоева в почтовой рассылке 2006-го года:

By default Nginx waits until at least one buffer (defined by gzip_buffers) is filled with data before sending the response to the client. Enabling this directive disables buffering.

Из описания кажется, что опция отключает буферизацию, если ответ укладывается в один буфер, а если контент больше, то просто буферизирует вывод. Спешу огорчить: это не так.

if (conf->no_buffer && ctx->in == NULL) {

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }

    cl->buf = ctx->out_buf;
    cl->next = NULL;
    *ctx->last_out = cl;
    ctx->last_out = &cl->next;

    return NGX_OK;
}

Если включен gzip_no_buffer, то заполняется один буфер, а затем возвращается клиенту без обработки следующей порции. В случае же превышения размера буфера, клиент получит ответ нулевой длины.

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

Я рекомендую не отключать буферизацию. Возможно, эта оптимизация и играет роль на больших объёмах трафика, однако в небольших проектах влияние на производительность будет малозаметным.

Версионируйте релизы правильно

Среди разработчиков свободного ПО есть мнение, что хеш коммита — хороший выбор для версии. Это не так:

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

Вот плохой пример: разработчики библиотеки x264 используют составной идентификатор из даты и номера снапшота (который почему-то не меняется с августа 2005-го года). Выходит, о потере совместимости нельзя будет узнать из номера версии.

Потому к версии этого пакета дописывают префикс — версию API из константы X264_BUILD из заголовочного файла x264.h. Это, на минуточку, популярный кодек H.264/MPEG-4 AVC в видеоплеере VLC и наборе библиотек FFmpeg.

Есть исключение: нумерация версий в проектах TeX и METAFONT. Каждое обновление добавляет дополнительную десятичную цифру в конце номера, ассимптотически приближая значение к числу Пи и Эйлера. Так Дональд Кнут хотел подчеркнуть, что добавление новой функциональности не планируется, а последующие изменения являются исправлениями ошибок.

Помните, что нумерация версий нужна не только машинам, но и людям. Выбирайте с умом.

Приватный репозиторий образов Vagrant

Для запуска виртуальной машины в Vagrant используют публичные образы или собирают собственный с помощью Packer. Компания HashiCorp предлагает использовать сервис Vagrant Cloud для хранения и версионирования этих образов, чтобы распространять обновления в команде разработчиков. Сервис бесплатный, но артефакты публичные и доступны каждому без авторизации.

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

Для решения этой проблемы написал vgrepo — простую программу командной строки для управления образами Vagrant. В паре с HTTP-сервером утилита упрощает доставку и обновление образов пользователям. Посмотрите скринкаст с демонстрацией работы:

asciicast

При запуске команд up, reload и resume Vagrant ищет метаданные образа в формате JSON. По ним Vagrant проверяет локальную версию и доступную в каталоге на сервере. Если у пользователя версия старше, чем в каталоге, то будет предложено обновить образ.

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

Пользователю достаточно указать ссылку на JSON в переменной config.vm.box_url. Обратите внимание, имя образа в config.vm.box должно совпадать с именем в метаданных:

Vagrant.configure("2") do |config|
  config.vm.box = "centos7-x64"
  config.vm.box_url = "https://vagrant.gongled.me/r/centos7-x64/"
end

На разработку проекта меня вдохновил туториал по развёртке приватного Vagrant Cloud, где подробно описан весь процесс. Если вам нравится проект и вы хотите помочь — присылайте ишьюсы и пул-реквесты на Гитхабе. С удовольствием читаю всё, что мне присылают.

Настройка RAID-контроллера SmartArray B120i в CentOS 7

Небольшая инструкция об установке CentOS 7 на HPE ProLiant Microserver Gen8 с аппаратным RAID-контроллером HP Dynamic Smart Array B120i:

Загрузите образ дискеты для RAID-контроллера с официального сайта Hewlett Packard. Запишите образ на USB-диск с помощью утилиты dd. Не забудьте заменить путь до блочного устройства /dev/sdd и отмонтировать устройство перед запуском:

gunzip < hpvsa-1.2.10-120.rhel7u0.x86_64.dd.gz | pv | sudo dd of=/dev/sdd

Запустите установку CentOS 7, на экране приглашения нажмите TAB, укажите опцию dd и отключите поддержку AHCI для использования драйвера от HP:

linux dd blacklist=ahci

Готово. Во время загрузки ядра будет предложено выбрать источник для драйвера. Выберите USB-диск и продолжите установку на логический диск.

Принудительно завершаем TLS-соединение в NGINX

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

В этом поможет расширение Server Name Indication (SNI) TLS. SNI предоставляет механизм передачи клиентом серверу информации о запрашиваемом хосте. Так веб-сервер узнаёт, какой сертификат ему следует использовать для соединения с виртуальным хостом.

Начиная с версии NGINX 1.11.5 доступен модуль ngx_stream_ssl_preread_module. Модуль извлекает имя сервера, доступное через SNI на фазе ClientHello без терминирования SSL/TLS. Значение заголовка доступно в переменной $ssl_preread_server_name. Используя её, можно динамически задавать адрес проксируемого TCP-бэкэнда:

stream {
  proxy_protocol on;

  upstream teardown {
    server 127.0.0.1:444;
  }

  upstream backend {
    server 127.0.0.1:445;
  }

  map $ssl_preread_server_name $name {
    example.com    teardown;
    default        backend;
  }

  server {
    listen 444;
    return "";
  }

  server {
    listen 443;
    ssl_preread on;
    proxy_pass $name;
  }
}

Так при обращении по HTTPS, NGINX перенаправит трафик в 445-й порт, а для example.com — в 444-й. В первом случае объявляем виртуальный хост и сертификат, во втором — веб-сервер разорвёт соединение.

Есть нюанс: используйте протокол PROXY для отображения реальных IP-адресов пользователей в логах. Например, для виртуального хоста example.com:

server {
  listen 445 http2 proxy_protocol;
  server_name example.com;

  set_real_ip_from 127.0.0.1;
  real_ip_header proxy_protocol;

  [...]
}

К сожалению, не выйдет рвать соединение для тех виртуальных хостов, где не указан SSL-сертификат. Как вариант, вместе с map включать файл со списком разрешённых хостов. Такой список придётся создать вручную, а после отправить SIGHUP мастер-процессу NGINX для применения изменений.

Страницы: 1 2