nginx — Блог Валерия Леонтьева https://valera.ws Место публикации личных заметок. Технологии, управление, бизнес, жизнь Sun, 02 Feb 2014 12:11:19 +0000 ru-RU hourly 1 https://wordpress.org/?v=5.6.2 https://valera.ws/wp-content/uploads/2020/02/favicon.png nginx — Блог Валерия Леонтьева https://valera.ws 32 32 Архитектура веб приложений: экстерьер (видео-лекция) https://valera.ws/2014.01.18~web-applications-architecture-exterio/ https://valera.ws/2014.01.18~web-applications-architecture-exterio/#respond Sat, 18 Jan 2014 17:34:57 +0000 http://valera.ws/?p=730 Читать далее Архитектура веб приложений: экстерьер (видео-лекция) ]]> Архитектура веб-приложений: экстерьерРассказ о популярной универсальной архитектуре стека, в котором работает веб-приложение. Само приложение может быть написано на любом интерпретируемом языке с использованием любого фреймворка фреймворков. В данном случае это не важно, так как архитектура программной инфраструктуры — технологического стека, в котором оно работает, отличается мало.

Обсуждается путь запроса пользователя до и внутри сервера, его обработка и возврат ответа. Затрагиваются вопросы состояния приложения — работы с хранилищами, кэширования.

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

Смотрите в полноэкранном режиме.

]]>
https://valera.ws/2014.01.18~web-applications-architecture-exterio/feed/ 0
Nginx: пример конфига для сайта с плюшками https://valera.ws/2013.03.23~nginx-bonus-config-example/ https://valera.ws/2013.03.23~nginx-bonus-config-example/#respond Sat, 23 Mar 2013 08:42:26 +0000 http://valera.ws/?p=702 Читать далее Nginx: пример конфига для сайта с плюшками ]]> nginxПросто готвый пример универсального конфига nginx с использованием php-fpm, и секциями для базовых инструментов (phpMyAdmin, RockMongo) и функционалом для закрытия сайта в режим обслуживания. Сервер одновременно слушает и HTTP, и HTTPS. Все запросы с www перекидываются на адрес «без-www».

Листинг /etc/nginx/sites-available/example.com.conf:

map $http_cookie $isDevHack {
    default "";
    ~DEVELOPER_SECRET_COOKIE=10101 "/non-existed-location";
}

server {
    listen 80;
    listen 443 ssl;
    server_name example.com www.example.com;

    access_log /var/log/nginx/example.com.access_log;
    error_log /var/log/nginx/example.com.error_log;

    root /home/example.com/htdocs/;
    index index.html index.php;
    client_max_body_size 15M;

    location /phpmyadmin/ {
        root /usr/share/;
        index index.php index.html index.htm;
        location ~ ^/mysql-pma/(.+\.php)$ {
            try_files $uri =404;
            root /usr/share/;
            fastcgi_pass unix:/tmp/example.com.pool.socket;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include /etc/nginx/fastcgi_params;
        }
    }

    # Redirect www to no-www
    if ($host = 'www.example.com') {
        rewrite ^/(.*)$ http://example.com/$1 permanent;
    }

    # Only requests to our Host are allowed
    if ($host !~ ^(example.com|www.example.com)$ ) {
        return 444;
    }

    # Locations
    location / {
        if (-f "$isDevHack/home/example.com/maintenance") {
            return 503;
        }
        try_files $uri $uri/ /index.php?$args;
    }

    # RockMongo
    location /rockmongo/ {
        root /home/example.com/;
        try_files $uri $uri/ /index.php?$args;
    }
    location ~ ^/rockmongo/.*\.php {
        root /home/example.com/;
        fastcgi_pass unix:/tmp/example.com.pool.socket;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ \.(php|phtml) {
        fastcgi_pass unix:/tmp/example.com.pool.socket;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # APC status page
    location = /apc-status.php {
        fastcgi_pass unix:/tmp/example.com.pool.socket;
        fastcgi_param SCRIPT_FILENAME /home/example.com/apc.php;
        include fastcgi_params;
    }

    # Memcached status page
    location = /memcached-status.php {
        fastcgi_pass unix:/tmp/example.com.pool.socket;
        fastcgi_param SCRIPT_FILENAME /home/example.com/memcached.php;
        include fastcgi_params;
    }

    location ~ \.(tpl|xml|log)$ {
        deny all;
    }

    # Errors
    error_page 503 @maintenance;
    location @maintenance {
        rewrite ^(.*)$ /maintenance-mode.html break;
    }

    # SSL
    ssl_certificate /etc/nginx/ssl/example.com.chained.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    ssl_session_timeout 5m;
    ssl_protocols SSLv2 SSLv3 TLSv1;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
}
]]>
https://valera.ws/2013.03.23~nginx-bonus-config-example/feed/ 0
Nginx: сайт в режиме обслуживания, кроме разработчиков https://valera.ws/2013.03.23~nginx-maintenance-mode-except-developers/ https://valera.ws/2013.03.23~nginx-maintenance-mode-except-developers/#respond Sat, 23 Mar 2013 08:08:20 +0000 http://valera.ws/?p=698 Читать далее Nginx: сайт в режиме обслуживания, кроме разработчиков ]]> nginxНа днях стала задача: сделать средствами nginx возможность перевода сайта в режим обслуживания для всех пользователей, кроме разработчиков. Под режимом обслуживания понимается то, что все запросы к скриптам сайта должны выдавать одну и ту же страницу с сообщением о том, что сайт временно недоступен (плюс HTTP-ответ с кодом 503).

Стоит отметить то, что исполняемый код сайта (PHP) на целевом сервере работает по схема схема nginx—php-fpm, а выбор файла — через try_files. Так же подчеркиваю, что требуется проверка, является ли пользователь разработчиком. Эти два фактора вместе отметали простые классические решения этой проблемы (в Гугле их масса, например).

Самое простое решение без проверки (разработчик или нет) заключается в том, что  бы проверять наличие специального файла в начале до проверки других условий. Это можно сделать с помощью try_files, подставив файл страницы с ошибкой первым в очереди. Но помимо того неудобства, что требуется добавлять/удалять файл в исходниках сайта (что не очень хорошо при заливке исходников через системы контроля версий), этот вариант просто не работает в сочетании с секциями if, которые появятся для добавления проверки «разработчик или нет». После входа в любую секцию if try_files ведет себя своеобразно и неправильно резолвит полные пути к файлам; источник проблемы описан тут.

Кстати, а по какому признаку мы будем проверять, является ли пользователь разработчиком? Ну, вариантов достаточно много. Самый удобный и популярный вариант — по наличию «специальной» cookie. Еще один популярный вариант — по IP. На какой фактор полагаться, большой разницы нет. В любом случае требуется проверка значения некоторой переменной, и осуществить эту проверку можно только с помощью секции if.

Резюмируя описанное выше: мы не можем делать проверку на наличие файла страницы обслуживания вместе с проверкой дополнительного фактора «разработчик или нет», используя try_files. Значит, проверку на наличие файла можно делать только с помощью if. В nginx if не может принимать два условия. Но эта проблема решается. Казалось бы, что цель достигнута. Проверяем 2 фактора: наличие файла и факт того, что пользователь — разработчик. Но такие проверки обязательно требуют входа хотя бы в один if. И тут опять фэйл: после входа в любой if try_files ведет себя своеобразно и неправильно резолвит полные пути к файлам после входа в секцию if; источник проблемы описан тут.

Короче, все довольно запутано и мутно. После нескольких часов мучительного подбора вариантов решение таки было найдено. Я выбрал способ проверки разработчика по cookie. Схема вышла очень простой. Один из if-ов был заменен map-ом, который работает нормально с try_files. А объединение условий (наличия файла и cookie) было реализовано через подмену пути к этому файлу. Т.е. в случае, когда пользователь — разработчик, мы просто портим имя файла, наличие которого свидетельствует о включенном режиме обслуживания.

Итоговая конфигурация.

В секции http (вне секций server) прописываем проверку «разработчик или нет». Она будет распространяться на все сайты в случае виртуального хостинга. В данном случае, если у пользователя установлена cookie DEVELOPER_SECRET_COOKIE со значением 1100110101, то пользователь будет признан разработчиком.

map $http_cookie $isDevHack {
    default "";
    ~DEVELOPER_SECRET_COOKIE=1100110101 "/non-existed-location";
}

В соответствующей секции server объявляем собственный обработчик ошибки 503, которая будет использована для индикации режима обслуживания:

error_page 503 @maintenance;
location @maintenance {
    rewrite ^(.*)$ /maintenance-mode.html break;
}

В данном случае подразумевается, что страница, которую нужно показать в случае работы в режиме обслуживания, находится в файле maintenance-mode.html, который лежит в корне сайта ($document_root данной секции server).

Теперь осталось определить, нужно ли показывать страницу «на обслуживании». В секциях location, для которых должно работать это правило, в начале (до применения других правил, которые работают в нормальном режиме работы сайта) добавляем:

if (-f "$isDevHack/home/site_root/maintenance") {
    return 503;
}

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

Пример конфига nginx целиком для сайта example.com.

]]>
https://valera.ws/2013.03.23~nginx-maintenance-mode-except-developers/feed/ 0
Выдача файла из PHP через nginx (Accel-Redirect) + докачка + некоторые тонкости https://valera.ws/2012.03.06~accel-redirect-apache-dokachka/ https://valera.ws/2012.03.06~accel-redirect-apache-dokachka/#respond Tue, 06 Mar 2012 20:28:27 +0000 http://valera.ws/?p=644 Читать далее Выдача файла из PHP через nginx (Accel-Redirect) + докачка + некоторые тонкости ]]> Как контролировать скачивание больших файлов, проверяя права доступа или считая количество закачек? Как сделать, чтобы при проксировании на Apache работала докачка? Как вообще работает докачка, почему она не работает с nginx в IE 9 и как она работает в других браузерах?

1. Мы хотим контролировать скачивание больших файлов, проверяя права доступа или считая количество закачек. Мы используем nginx и PHP (или другой серверный язык).

Это реализуется очень просто через заголовок Accel-Redirect. nginx получает запрос, передает его скрипту, скрипт в заголовки ответа выдает Accel-Redirect с ссылкой на файл, который нужно выдать пользователю. Если файл выдавать нельзя (например, нет прав), скрипт просто выдаст ошибку (например, 403).

В данном случае не важно, работает ваш PHP через проксирование из nginx на Apache, либо на *CGI. Предположим, что вы хотите отдавать на скачку файлы, которые находятся в каталоге /var/files. Тогда в секции server в nginx добавьте:

location /_download_files {
        root /var/files;
        internal;
}

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

header("X-Accel-Redirect: /_download_files/filename.zip");

Никакой выдачи (echo) делать не надо, после отдачи заголовка можно завершить работу скрипта. В этом случае будет скачан файл /var/files/filename.zip. Вместо filename.zip может быть любой длинный путь с каталогами, который должен повторять путь к файлу в /var/files/.

В принципе этого достаточно, чтобы переложить выдачу файла на плечи nginx. Если нужно обязательно показать диалог закачки (даже если файл такого типа, который браузер может показать сам), нужно добавить выдачу заголовка:

header("Content-Type: application/x-force-download");

Обратите так же внимание на то, что в случае проксирования на Apache последний обязательно установит Content-Type (по умолчанию обычно text/html) в случае PHP-скриптов, и этот тип отправится пользователю, так как nginx его не перепишет при выполнении Accel-Redirect. Т.е. в этом случае эту строку нужно писать обязательно. Как будет в случаях с *CGI, не знаю — не проверял.

Чтобы предложить пользователю имя файла для сохранения, отличное от того, что в адресной строке (это особо актуально в случаях «download.php&file_id=12332»), отправим такой заголовок:

Content-Disposition: attachment; filename="Super File Name.zip";

2. В такой схеме при проксировании на Apache не работает докачка. Исправляем.

Докачка в HTTP работает через HTTP-заголовок Range. В нем указывается с какого байта начать передачу. Можно так же указать, сколько байт нужно передать. Это передается в запросе. Например:

Range: bytes=33-
Range: bytes=33-100

Первый вариант просит отдать весь файл, начиная с 33 байта (включительно). Второй вариант просит отдать с 33 байта по 100. Чтобы браузер понял, что докачка поддерживается, сервер отдает в ответе заголовок:

Accept-Ranges: bytes

Докачка не работает из-за того, что HTTP-заголовок Range попадает в Apache после просирования и последний думает, что его просят отдать кусок PHP-файла. А он это делать не хочет и отдает ошибку: HTTP/1.1 416 Requested Range Not Satisfiable.

Исправляется это довольно легко — просто зануляйте при проксировании Range (это нужно добавить туда, где у вас описано проксирование на Apache в конфиге nginx):

proxy_set_header   Range   "";

Тогда Apache (или PHP-скрипт) ничего знать про него не будут и спокойно сделают свое дело. А nginx после Accel-Redirect отдаст ответ в соответствии с запрошенным Range.

Если жизненно необходимо видеть в скрипте запрошенный Range, то его можно либо передать через дополнительный заголовок при проксировании (proxy_add_header), либо последовать совету отсюда.

3. Даже так не работает докачка в IE 9.

Да. На сколько я понял методом простого тыка, IE требует поддержки ETag сервером для работы докачки. Игорь Сысоев (разработчик nginx) отказался внедрять в nginx поддержку ETag. Поэтому из коробки работать докачка в IE 9 не будет точно. Если это жизненно необходимо, пробуйте смотреть сюда (Ctrl+F Etag).

4. А как работает докачка в других браузерах?

Везде есть свои особенности. В общих чертах случай докачки выглядит так. Запрос/ответ на скачку (начало скачки):

GET /file.zip HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Server: nginx/1.0.3
Date: Tue, 06 Mar 2012 18:09:47 GMT
Content-Type: application/x-force-download
Content-Length: 13686314
Last-Modified: Thu, 23 Feb 2012 13:50:26 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Disposition: attachment; filename="Supre File Name.zip";
Accept-Ranges: bytes

Запрос/ответ на докачку:

GET /file.zip HTTP/1.1
Host: example.com
Range: bytes=5393683-

HTTP/1.1 206 Partial Content
Server: nginx/1.0.3
Date: Tue, 06 Mar 2012 18:10:11 GMT
Content-Type: application/x-force-download
Content-Length: 8292631
Last-Modified: Thu, 23 Feb 2012 13:50:26 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Disposition: attachment; filename="Super File Name.zip";
Content-Range: bytes 5393683-13686313/13686314

На практике ни один браузер не ограничивается таким набором заголовков. Все хотят защитить пользователя от скачки битого файла. Это может получиться так: начал закачку, поставил на паузу; в это время на сервере скачиваемый файл обновился; продолжил закачку с нужно места и получил кусок обновленного файла. В результате скачается «битый» файл — половина старая версия, половина — новая.

Opera. Она шлет указание на то, что «если файл не менялся со времени Last-Modified в первом ответе, дай мне указанный кусок; иначе — отдай мне весь файл заново». Делается это с помощью заголовка If-Range в запросе:

GET /file.zip HTTP/1.1
User-Agent: Opera/9.80 (Windows NT 6.1; U; ru) Presto/2.10.229 Version/11.61
Host: example.com
Accept text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, ..., */*;q=0.1
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
If-Range: Thu, 23 Feb 2012 13:50:26 GMT
Range: bytes=5393683-

Firefox. Он делает немного иначе. Используется заголовок If-Unmodified-Since с указанием того же времени из Last-Modified. В этом случае сервер либо вернет запрошенный кусок (если файл не менялся), либо отдаст ошибку 412 Precondition Failed. Выглядит это так:

GET /file.zip HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Range: bytes=5878651-
If-Unmodified-Since: Thu, 23 Feb 2012 13:50:26 GMT

Chrome. Этот «пильмень» — самый хитрый. Он вообще не поддерживает докачку, хотя делает вид, что поддерживает. При постановке закачка на паузу он просто шлет некие TCP-пакеты для поддержания соединения. Когда сервер понимает, что его дурят, он разрывает соединение. Если дождаться этого момента и нажать в Хроме «Продолжить», он сделает умный вид, будто файл полностью скачался и все ОК, но при этом он останется в том размере, в котором успел скачаться до паузы.

Вся эта информация получена методом наблюдения. Возможно в какой-то другой ситуации Хром повел бы себя иначе, но я через WireShark наблюдал именно такую картину.

IE. Про версию 9 уже сказано выше. Она требует работы Etag для поддержки докачки. В данном контексте ETag — это просто контрольная сумма файла. Когда Etag файла передается в первом ответе от сервера, IE даст возможность прервать скачку. При попытке докачки он отдаст заголовки:

GET /file.zip HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: falcongaze.com
Range: bytes=3494051-
Unless-Modified-Since: Mon, 05 Mar 2012 08:31:06 GMT
If-Range: "1448453-f77f31-4ba7abf5a8680"
Connection: Keep-Alive

В добавок к If-Range он передает нестадартный заголовок Unless-Modified-Since — вероятено IIS его обрабатывает (только не понятно, зачем он, если есть стандартный If-Unmodified-Since; может для поддержки старых версий IIS?).

Собственно в первом ответе Etag выглядит примерно так (если он поддерживается сервером, например, Apache):

...
ETag: "1448453-f77f31-4ba7abf5a8680"

Дополнительная информация:

]]>
https://valera.ws/2012.03.06~accel-redirect-apache-dokachka/feed/ 0
Фокусы с nginx https://valera.ws/2009.01.26~nginx-magic/ https://valera.ws/2009.01.26~nginx-magic/#comments Mon, 26 Jan 2009 14:23:05 +0000 http://valera.ws/?p=276 Читать далее Фокусы с nginx ]]> Вчерашний вечер я посвятил возне с http-сервером nginx в качестве фронтэнда к apache. Как известно, nginx — легковесный надежный HTTP-сервер, написанный Игорем Сысоевым (сотрудником Rambler). Он отлично подходит для выдачи статических страниц, особенно под нагрузкой. Обычно настраивается связка nginx+apache, в которой nginx обслуживает все входящие на сервер запросы, статические файлы отдает своими силами, а запросы на динамическое содержимое проксирует на apache.

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

Моя конфигурация

Для начала, что собственно требовалось сделать? Сервер настраивался для Хаброметра. Он должен был выдавать статику (лого и css) и динамику (собственно страницы сайта и png-хаброметры). При этом, надо было учесть, что хаброметр создается на лету в том случае, если не лежит в кэше (а кэш чистится каждые 2 часа при запросе новых данных). Страницы сайта также необходимо кэшировать. Вот такая была задача.

Реализацию решено было сделать следующим образом. nginx при обработке запроса должен следовать следующим правилам:

  1. Если запрошена статика, то просто вернуть ее (вся статика в папочке stuff).
  2. Если запрошена страница сайта, нужно проверить кэш; если в кэше файл не найден, передать запрос бэкэнду apache. Кэш страниц должен чиститься с заданной частотой (для разных страниц частота разная).
  3. Если запрошен информер, то нужно проверить кэш на наличие файла. Если файла там нет, передать запрос бэкэнду.

Для кэширования хаброметров выбрана файловая система. Все сгенерированные информеры складываются в каталог /image_cache/ и он чистится каждые 2 часа при обновлении исходных данных. Рисуются информеры и кладутся в этот каталог PHP-скриптом при соответствующем запросе.

Для кэширования страниц сайта выбран memcache, т.к. с ним легко и удобно работать (как из nginx, так и из PHP) и он сам может чистить кэшированные странички через заданный интервал времени, что ФС делать не может без дополнительных скриптов. Да и работать memcache будет побыстрее, т.к. все добро складируется в оперативке.

Получилась следующая конфигурация сервера:

# cat /etc/nginx/nginx.conf

user www-data;
worker_processes 2;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  2048;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    access_log  /var/log/nginx/access.log;
    sendfile        on;
    keepalive_timeout  65;
    tcp_nodelay        on;
    gzip  on;
    add_header Habrometr "hacker_mode_enabled;)";
    server {
        listen       80;
        server_name  habrometr.server.valera.ws habrometr.ru www.habrometr.ru;
        access_log  /var/log/nginx/habrometr.access.log;
        location / {
            root   /home/habrometr/public_html;
            index  index.html index.htm;
            if (-f $document_root/image_cache${uri}) {
                rewrite ^.*$ /image_cache/$uri last;
                break;
            }

            set $memcached_key "habrometr$uri";
            memcached_pass localhost:11211;
            # если в memcached не найден ресурс, передаем запрос на апач
            error_page 404 502 504 = @backend;
            add_header Content-Type "text/html; charset=UTF-8";
            gzip on;
            gzip_proxied any;
            gzip_types application/octet-stream;
    }
    location @backend {
        set $proxy_uri http://habrometr.ru:99999$request_uri;
        proxy_pass $proxy_uri;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X_Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 20;
    }
    location /image_cache/ {
        root   /home/habrometr/public_html;
        expires modified +2h; # кэш истекает через 2 часа после модификации файла
    }
    location /stuff/ {
        root   /home/habrometr/public_html;
        expires 30d;
    }
    location ~ /\.ht {
        deny  all;
    }
}

Учитывая приведенный выше сценарий, вся конфигурация должна быть понятна. Замечу лишь, что http://habrometr.ru:99999 — это apache, которому будут перенаправляться запросы. Порт я, конечно, изменил, в реальности обычно используют 8080 или что-то типо того.

Фокусы

А теперь о том, что нетривиального в этой конфигурации (во всяком случае для новичка в этой области).

Версия

Во-первых, сервер у меня работает на Debian 4.0. Весь софт я естественно ставил из стандартных репозиториев. Поставил оттуда и nginx. Установленный nginx оказался версии 0.4 при наличии последней версии 0.7 со значительным списком изменений.

Выяснилось, что версия 0.4 не умеет делать много из того, что было нужно. В частности:

  1. флаг modified не сузествует для директивы expire, а мне это необходимо было для указания времени устаревания кэша информеров (2 часа после создания: expire modified +2h);
  2. proxy_pass не умел использовать переменные, а мне требовалась эта возможность;
  3. memcached не использовал переменную $memcached_key для определения ключа, т.е. нельзя было задать ключ нужного формата.

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

Перед тем, как описать процесс установки, замечу, что по умолчанию при сборке из исходников все файлы nginx складывает в каталог /usr/local/nginx. Его, конечно, можно изменить (—prefix=). Но обратите внимание, что установленных из пакетов nginx раскидывает свои файлы по соответствующим каталогам системы (/etc, /var/log, /var/run и т.д.), что лично мне определенно нравится больше, чем /usr/local/nginx/*. По этому я откомпилировал nginx из сырцов с настройками на системные каталоги, а потом вместо make install просто вручную заменил старый бинарный файл сервера в каталоге /usr/sbin на новый (/usr/sbin/nginx). Больше значимых файлов после сборки для сервера нет. Конфиг, естественно, остается тот же самый.

Итак, установка nginx на Debian etch из исходников поверх установленного пакета старой версии.

# wget http://sysoev.ru/nginx/nginx-0.7.31.tar.gz
# tar xzf nginx-0.7.31.tar.gz
# cd nginx-0.7.31
# apt-get install libpcre3 libpcre3-dev libpcrecpp0
# /etc/init.d/nginx stop;
# ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module
--without-mail_pop3_module --without-mail_imap_module
--without-mail_smtp_module --prefix=/var/lib/nginx
--sbin-path=/usr/sbin --conf-path=/etc/nginx/
--error-log-path=/var/log/nginx --http-log-path=/var/log/nginx
--pid-path=/var/run --lock-path=/var/lock
# make
# cd objs
# cp -f ./nginx /usr/sbin
# /etc/init.d/nginx start;

После этого уже должен быть запущен и обслуживать запросы свежий сервер nginx, который умеет все нужные нам штуки.

Документы из memcached

Когда nginx отдает напрямую файлы, он передает заголовок Content-type в соответствии с типом данного файла. Когда nginx проксирует apache, Content-type приходит от apache. Но когда nginx забирает документа из memcached, то Content-type не устанавливается. А значит, используется дефалтный. А дефалтный у нас по конфигу default_type application/octet-stream;, и это правильно. В этом случае при отдаче документа из кэша будет неправильно передаваться тип и некоторые браузеры предложат сохранить бинарный файл, вместо того чтобы открыть HTML-страницу. Чтобы ситуацию исправить, в случае отдачи из memcached устанавливаем заголовки (и, кстати, сжатие тоже) дополнительно:

set $memcached_key "habrometr$uri";
memcached_pass localhost:11211;
error_page 404 502 504 = @backend;
add_header Content-Type "text/html; charset=UTF-8";
gzip on;
gzip_proxied any;
gzip_types application/octet-stream;

При этом из memcached мы получаем только HTML в UTF-8.

Мухи отдельно, котлеты отдельно.

В качестве отдельной магии хотелось бы выделить сам способ выделения хаброметров по имени файла и обслуживания их по-особому. В секции location / выделяем эти файлы:

if (-f $document_root/image_cache${uri}) {
    rewrite ^.*$ /image_cache/$uri last;
    break;
}

Если файл находим в кэше, то просто возвращаем его пользователю, сообщая что файл можно закешировать до времени следующего обновления (модификация файла + 2 часа):

location /image_cache/ {
    root   /home/habrometr/public_html;
    expires modified +2h;
}

Обратите внимание на наличие флага last у rewrite и директивы break; за ней. Без использования этих двух директив мне не удалось заставить nginx 0.4 (на 0.7 я не проверял) сразу перейти в секцию location /image_cache/, т.е. после обнаружения файла он переходил к проскированию, что неверно.

]]> https://valera.ws/2009.01.26~nginx-magic/feed/ 2