Ars Longa, Vita Brevis

Как настроить связку nginx+PHP в Windows и не иметь головной боли.

Принято считать, что замечательный Web-сервер nginx работает только под Unix-подобными операционными системами. Верно, но лишь отчасти. nginx превосходно собирается и в Cygwin. Возникает вопрос: а зачем это надо, собирать nginx под Windows? Ответ: я знаю достаточно много web-разработчиков, работающих в силу тех или иных причин под Windows. И в работе встречаются ситуации, когда на рабочей машине нужно создать конфигурацию, максимально похожую на конфигурацию сервера (production или development в данном случае не важно).

Начну с примера: есть сайт, который позволяет зарегистрированным пользователям скачивать что-либо (музыку, программы, фильмы — не суть важно). Одним из самых распространённых решений в этом случае является использование нескольких серверов. Во многих конфигурациях, которые я видел, "web-мордой" занимается Apache, а на обслуживании закачек стоит nginx+PHP+FastCGI (PHP занимается вопросами авторизации, ведения статистики и т.п.). Сразу оговорюсь, что есть альтернативное решение, когда nginx используется в качестве reverse proxy перед Apache — nginx проксирует запросы к PHP-скриптам Apache, а PHP-скрипт, если ему надо отдать что-то статическое, делает X-Accel-Redirect. Подробнее описано в статьях "Использование X-Accel-Redirect с Nginx для реализации контролируемых скачиваний" и "Использование Nginx Как Reverse-Proxy Сервера На Загруженных Сайтах".

Отвлекусь от темы и скажу пару слов про альтернативную конфигурацию: она работает на "ура", если сайт отдаёт в основном статический контент. Если же большая часть запросов идёт к PHP, то логичнее поставить nginx за Apache (в противном случае будет тратиться время на проксирование запроса). Еще один минус у такой конфигурации — использование менеджеров закачек (всякие там ReGet, FlashGet) "ложит" Apache. Можно, конечно, ввести ограничения на одновременное количество заказчек, а можно настроить PHP работать в паре с nginx.

Возвращаясь к теме разговора: а теперь предположим, что это всё нужно реализовать локально (для тестирования). Вот в таких ситуациях nginx для Windows очень хорошо помогает :-)

В Cygwin сборка nginx проходит практически без проблем (хотя более старые версии приходилось немного патчить руками), на выходе получаем nginx.exe :-)
Я не буду вдаваться в детали конфигурирования nginx, потому что есть замечательная статья "Настройка Nginx для поддержки PHP при помощи FastCGI", просто отмечу некоторые детали, упрощающие жизнь.

Итак, у нас есть PHP, Cygwin и nginx. К сожалению, nginx не поддерживает PHP как модуль (подобно Apache), поэтому nginx и PHP могут работать только по протоколу FastCGI (возможно, и по обычному CGI, я не пробовал — в любом случае, производительность при работе через CGI будет значительно ниже).

Для определённости будем полагать, что nginx у нас сконфигурирован следующим образом (я привожу только часть, относящуюся к FastCGI):

location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  disk:/path/to$fastcgi_script_name;
    include        conf/fastcgi_params;
}

Ключевой момент: в fastcgi_param должен быть путь, который поймёт интерпретатор PHP. Если интерпретатор собран в Windows, то путь тоже должен быть в формате Windows (например, c:/web/mysite.com/scripts). Хвостового слэша быть не должно, потому что $fastcgi_script_name именно с него и начинается. То есть если пользователь запросил http://mysite.com/test.php, то $fastcgi_script_name будет равен /test.php.

Файл conf/fastcgi_params:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx;
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

Для удобства, установим nginx как сервис Windows. Для этого можно использовать, например, cygrunsrv:

[-]
View Code Bash
cygrunsrv --install nginx --path /path/to/nginx.exe --disp nginx --neverexits

Ключевым здесь является параметр --neverexits — без него Windows будет жаловаться, что процесс был остановлен (из-за того, что nginx fork()'ается). Хотя это неправильное использование --neverexits.

Переходим к настройке PHP. По счастью, в Windows-версии PHP уже есть модуль с поддержкой FastCGI (он называется php-cgi.exe), так что пересобирать PHP не придётся (сборка PHP под Cygwin — неблагодарное занятие из-за парочки глюков в libtool; если у меня будет желание, как нибудь напишу). В статье приводится скрипт для запуска PHP/FastCGI, но мы поступим проще.

Сначало об одной не очень документированной особенности PHP/FastCGI: он имеет тенденцию "незаметно умирать" после некоторого числа запросов. Это связано со значением переменной окружения PHP_FCGI_MAX_REQUESTS, определяющей, какое максимальное количество запросов может "пережить" PHP. Можно, кончено, поставить "космическое" значение, но 2 миллиарда запросов — так ли это много? :-) На отрицательные значения переменной PHP громко ругается. Экспериментально было выяснено, что 0 — это то, что нужно (в документации я это не нашел).

Запускаем PHP:

[-]
View Code Bash
set PHP_FCGI_MAX_REQUESTS=0
php-cgi -b 127.0.0.1:9000 -c path/to/php.ini

А теперь как установить PHP/FastCGI сервисом Windows:

[-]
View Code Bash
cygrunsrv --install php-cgi --path /cygdrive/f/web/php/php-cgi.exe --disp "PHP/FastCGI" --env "PHP_FCGI_MAX_REQUESTS=0" --args "-b 127.0.0.1:9000 -c path/to/php.ini"

Строка "-b 127.0.0.1:9000 -c path/to/php.ini" будет передана интерпретатору в качестве командной строки, поэтому путь path/to/php.ini должен быть путем Windows, если интепретатор собран под Windows.

Собственно, всё :-)

Добавить в закладки
  • del.ici.ous
  • Digg
  • Furl
  • Google
  • Simpy
  • Spurl
  • Y! MyWeb
  • БобрДобр
  • Мистер Вонг
  • Яндекс.Закладки
  • Текст 2.0
  • News2
  • AddScoop
  • RuSpace
  • RUmarkz
  • Memori
  • Закладки Google
  • Писали
  • СМИ 2
  • Моё Место
  • Сто Закладок
  • Ваау!
  • Technorati
  • RuCity
  • LinkStore
  • NewsLand
  • Lopas
  • Закладки - I.UA
  • Connotea
  • Bibsonomy
  • Trucking Bookmarks
  • Communizm
  • UCA

Комментарии к статье "Настройка nginx и PHP/FastCGI в Windows" (20) »

  1. [Июль 18, 2008 2:25 пп] babr:

    Замечательно!
    Великолепно!
    Это именно то, что я искал так долго, и собирал по крупицам! А вы все собрали вместе, и уложили один за другим - спасибо вам преогроменное!

    С уважением, babr

    #1
  2. [Июль 18, 2008 5:14 пп] Vladimir:

    babr, очень рад, что Вам это пригодилось!

    #2
  3. [Июль 19, 2008 3:18 пп] babr:

    Уважаемый автор, у меня к вам вопрос.
    Я пользуюсь вот этой сборкой nginx под Windows: http://www.kevinworthington.com/nginx/win32/ очевидно автор ее так же компилил из под cygwin.
    Т.к. я не имею достаточного опыта работы с cygwin, а мой опыт работы с Unix-системами находится на уровне пользователя графической оболочки KDE, то я решился спросить у вас.

    Ситуация в следующем:
    Когда я в настройках nginx пытаюсь прописать такое:
    server {

    root C:/Apache/htodcs/tos/trunk/src/www;

    }

    То получаю ошибку nginx (в логе то есть):
    2008/07/19 16:08:40 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/favicon.ico” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
    2008/07/19 16:08:40 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
    2008/07/19 16:08:43 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/favicon.ico” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
    2008/07/19 16:08:43 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
    2008/07/19 16:09:32 [error] 3884#0: *2 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “local.timeout”

    То есть очевидно, nginx пытается танцевать относительно какойго-то начального каталога /cygdrive/ , и это жестко защито. Если я переношу файлы сайта из Apache-каталога htdocs в nginx-каталог html - то все замечательно работает. Но такой вариант меня не устраивает по ряду специфических причин.

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

    С уважением, Савунов Василий (babr)

    #3
  4. [Июль 19, 2008 3:25 пп] Vladimir:

    Василий, попробуйте так:

    server {
        root /cygdrive/c/Apache/htdocs/tos/trunk/src/www;
    }
    

    Дело в том, что в Cygwin, как и в Unix, файловая система плоская — нет разделения на диски. Для эмуляции (не знаю, как выразиться точнее) логических дисков используется точка монтирования (каталог) /cygdrive: например, диск C: становится /cygdrive/c, диск D: — /cygdrive/d и т.д. То есть путь C:\Apache\htdocs\tos\trunk\src\www с точки зрения Cygwin будет /cygdrive/c/Apache/htdocs/tos/trunk/src/www.

    #4
  5. [Июль 19, 2008 5:02 пп] babr:

    Vladimir - спасибо вам. Слова “эмуляция” и “точка монтирования” мне понятны вполне, так что ваш ответ полностью отвечает на мой запрос.

    Спасибо что уделили мне время!
    С уважением, Савунов Василий (babr)

    #5
  6. [Июль 19, 2008 5:09 пп] Vladimir:

    Василий, не за что! Надеюсь, что помогло?

    #6
  7. [Июль 19, 2008 5:18 пп] babr:

    Vladimir, спасибо, да, помогло
    :)

    Давно надо уже на Linux мигрировать, но лень все время берет свое. Уж и работал на Linux (ставили коллеги, не я), и более-менее знаю основные команды Linux, и crontab могу настроить, и конфиг nginx поправить могу, а нет - не могу заставить себя сесть, и убить время за установкой Linux-дистрибутива.

    Так что ваша статья как нельзя кстати. Полностью отвечает моим нынешним запросам.
    Бредовая ситуевина выходит - одновременно веду разработку работая и под nginx, и под Apache, запуская для разных проектов php то как FastCGI, то как модуль Apache… :))) Но такова специфика работы, и проектов.

    Спасибо вам еще раз!
    С уважением, Савунов Василий (babr)

    #7
  8. [Июль 19, 2008 5:40 пп] Vladimir:

    Давно надо уже на Linux мигрировать, но лень все время берет свое.

    У меня просто VMware рухнула, придавив Windows, я счел это знаком и перешел на Linux. И жена уже привыкла.

    запуская для разных проектов php то как FastCGI, то как модуль Apache

    Так они же могут вместе жить? PHP как модуль Апача, а php-cgi как FastCGI-модуль в nginx.

    #8
  9. [Июль 19, 2008 6:12 пп] babr:

    С VMWare я даже не стал связываться.

    “Так они же могут вместе жить? PHP как модуль Апача, а php-cgi как FastCGI-модуль в nginx.”
    А они и живут. :)

    #9
  10. [Август 14, 2008 9:12 дп] Valodik:

    Nginx выдает 502 ошибку.
    Вот что написанно в error.log-e

    2008/08/13 23:04:38 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”
    2008/08/13 23:04:43 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”
    2008/08/13 23:05:08 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”

    Буду вам признателен если подскажите, что нужно исправить

    #10
  11. [Август 14, 2008 9:56 дп] Vladimir:

    Valodik, а можно на конфиги краем глаза взглянуть?

    PS - с очень большой вероятностью у Вас не запущен PHP/CGI, либо же он слушает другой порт. Посмотрите выдачу netstat -an

    #11
  12. [Август 14, 2008 8:31 пп] Valodik:

    Вы правы не слушает на порт 127.0.0.1:9000
    Вот результаты netstat -an

    Active Connections
      Proto  Local Address          Foreign Address        State
      TCP    0.0.0.0:80             0.0.0.0:0              LISTENING
      TCP    0.0.0.0:135            0.0.0.0:0              LISTENING
      TCP    0.0.0.0:445            0.0.0.0:0              LISTENING
      TCP    0.0.0.0:1025           0.0.0.0:0              LISTENING
      TCP    127.0.0.1:80           127.0.0.1:1037         ESTABLISHED
      TCP    127.0.0.1:1029         0.0.0.0:0              LISTENING
      TCP    127.0.0.1:1033         127.0.0.1:1034         ESTABLISHED
      TCP    127.0.0.1:1034         127.0.0.1:1033         ESTABLISHED
      TCP    127.0.0.1:1037         127.0.0.1:80           ESTABLISHED
      UDP    0.0.0.0:445            *:*
      UDP    0.0.0.0:500            *:*
      UDP    0.0.0.0:4500           *:*
      UDP    127.0.0.1:123          *:*
      UDP    127.0.0.1:1035         *:*
      UDP    127.0.0.1:1036         *:*
      UDP    127.0.0.1:1900         *:*
    

    А это из nginx.conf

    location ~ \.php$ {
                root           html;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME   $fastcgi_script_name;
                include        fastcgi_params;
            }
    
    #12
  13. [Август 14, 2008 8:55 пп] Vladimir:

    А сам-то php-cgi Вы запустили?

    Выполните эти две команды из консоли (cmd.exe):

    set PHP_FCGI_MAX_REQUESTS=0
    php-cgi -b 127.0.0.1:9000 -c C:\php\php.ini
    

    Вместо C:\php\php.ini должен быть путь к Вашему php.ini.

    #13
  14. [Август 15, 2008 11:24 дп] Valodik:

    Мой php-интерпретатор никак не хочет принимать параметр -b.
    Вот инфо про используемых интерпретаторов

    PHP 4.4.2 (cgi-fcgi) (built: Jan 13 2006 13:53:43)
    Copyright (c) 1997-2006 The PHP Group
    Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies

    PHP 5.0.4 (cgi-fcgi) (built: Mar 31 2005 02:45:43)
    Copyright (c) 1997-2004 The PHP Group
    Zend Engine v2.0.4-dev, Copyright (c) 1998-2004 Zend Technologies

    #14
  15. [Август 15, 2008 5:50 пп] Vladimir:

    Valodik, запустите Ваш php-cgi с параметром --help.

    В моём случае получилось нечто подобное:

    Usage: php [-q] [-h] [-s] [-v] [-i] [-f <file>]
           php <file> [args...]
      -a               Run interactively
      -b <address:port>|<port> Bind Path for external FASTCGI Server mode
      -C               Do not chdir to the script's directory
      -c <path>|<file> Look for php.ini file in this directory
      -n               No php.ini file will be used
      -d foo[=bar]     Define INI entry foo with value 'bar'
      -e               Generate extended information for debugger/profiler
      -f <file>        Parse <file>.  Implies `-q'
      -h               This help
      -i               PHP information
      -l               Syntax check only (lint)
      -m               Show compiled in modules
      -q               Quiet-mode.  Suppress HTTP Header output.
      -s               Display colour syntax highlighted source.
      -v               Version number
      -w               Display source with stripped comments and whitespace.
      -z <file>        Load Zend extension <file>.
    

    Если в выдаче у Вас не будет параметра -b, это означает, что Ваш PHP собран без поддержки FastCGI.

    PS - Вам в любом случае было бы неплохо обновить PHP.

    #15
  16. [Август 18, 2008 4:59 пп] Valodik:

    Обновил php.
    А теперь такая проблема.При обращение к php-файлам nginx выдает

    No input file specified.

    Как решить эту проблему?

    #16
  17. [Август 19, 2008 3:24 дп] Vladimir:

    У Вас проблема с fastcgi_param SCRIPT_FILENAME.

    Допустим, что Ваш сайт живет в C:\mysites\mysites.com (то есть это DOCUMENT_ROOT).

    В этом случае SCRIPT_FILENAME должен быть таким:

    fastcgi_param SCRIPT_FILENAME c:/mysites/mysites.com$fastcgi_script_name;

    #17
  18. [Август 19, 2008 3:57 пп] Valodik:

    Спаибо вам.
    Теперь все работает.
    Проблема была в том,что fastcgi_param не понимает не относительный путь, не путь вида /cygdrive/c/nginx…

    #18
  19. [Август 20, 2008 4:26 пп] Василий:

    Добрый день уважаемый автор.
    Я хочу обратиться к вам за помощью.
    Перестал запускаться nginx, выдавая:
    2008/08/20 17:23:30 [notice] 1072#0: using inherited sockets from “c:\nginx\html

    2008/08/20 17:23:30 [emerg] 1072#0: invalid socket number “c:\nginx\html” in NGI
    NX environment variable, ignoring the rest of the variable

    и все. висит в памяти и ни туда ни сюда. что делать??? я в ужасе.
    А работать надо.

    #19
  20. [Август 22, 2008 12:37 пп] Vladimir:

    Василий, ну никак у меня воспроизвести этот глюк не получается

    #20

RSS лента комментариев к этой записи. TrackBack URL

Оставить комментарий к записи "Настройка nginx и PHP/FastCGI в Windows"

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, Вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя