Задать вопрос

Связка Nginx+Apache. Странное поведение virtual hosts — почему?

Здравствуйте.
Понимаю, что проблема банальная до безобразия - но мозг отказывается понимать ее.

Есть стандартнейшая связка NGINX + Apache2+mod_php4
В Nginx конфиг примерно такой:
server {
       listen 80 ;
       server_name *.myhost.ru;

       location ~* ^/.+\.(css|gif|js|pdf|png|txt|zip|jpg|ico)$ {
               root /code/host/content;
               break;
       }

      location / {
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $remote_addr;
           proxy_pass http://127.0.0.1:8080/;
      }
}


То есть все поддомены проксируются на 127.0.0.1:8080
На 8080 стоит Апач у которого куча конфигов в site-enabled/ типа такого:

<VirtualHost *:8080>
        ServerName sub1.myhost.ru
        ServerAlias www.sub1.myhost.ru
        SuexecUserGroup www www

        DocumentRoot /code/host/content/bs/sub1

        <Directory />
        Options +FollowSymLinks +ExecCGI
        Order allow,deny
        Allow from all
        </Directory>

        php_admin_value open_basedir none
        AddType application/x-httpd-php .php .phtml .html .htm
</VirtualHost>

, но почему-то при обращении к какому-либо поддомену открывается только основной сайт. При этом если обратиться к sub.myhost.ru:8080 то он нормально открывается. proxy_set_header Host $host вроде стоит. Что ему еще надо. При том что точно такие - же (вроде бы) конфиги применяются на других сайтах - все работает. Тут нет.

В чем может быть затык?
  • Вопрос задан
  • 7999 просмотров
Подписаться 7 Оценить Комментировать
Решения вопроса 3
rumkin
@rumkin
Это говорит о том, что apache не видит хоста из-за nginx, видимо, читает его не из HTTP-заголовка, а из значения указанного прокси-сервером.
Не специалист по nginx, но, судя по всему, эта строка proxy_pass http://127.0.0.1:8080/; должна содержать имя хоста, попробуйте так proxy_pass http://$host:8080/;
Ответ написан
Комментировать
@kaasius
Попробуйте отдебажить ситуацию - вкрутите в энджи логирование запросов для этого server и посмотрите, попадает ли туда ваш субдомен.
Ответ написан
Комментировать
@inkvizitor68sl
Linux-сисадмин с 8 летним стажем.
ports.conf у апача проверьте.
Для вашего случая где-то там должны быть такие строки:
NameVirtualHost *:8080
Listen 8080

Ну или в самом apache2.conf
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
nowm
@nowm
Я сначала немного «теории» опишу, а в конце скажу, что в ваших конфигах нужно поправить.

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

Так вот. Суть связки Apache + Nginx в том, что Apache обрабатывается входящие запросы только от одного сервера. Обычно они оба ставятся на одной машине, и в этом случае Apache должен принимать запросы только от 127.0.0.1. Он не должен заботиться о том, чтобы обрабатывать имена dev1.site, dev2.site и так далее. Эта задача лежит на Nginx, который будет перенаправлять внешние запросы соответствующим образом.

Для этого Apache конфигурируется таким образом, чтобы каждый его виртуальный хост слушал свой порт. Например, я строю всю систему таким образом, чтобы у меня было два сервера: task.site и dev.site. Для этого я делаю так:

/etc/apache2/ports.com:
NameVirtualHost 127.0.0.1:8080
NameVirtualHost 127.0.0.1:8090
Listen 127.0.0.1:8080
Listen 127.0.0.1:8090


Это значит, что у меня будет два инстанса, один из которых будет обслуживать имя dev.site, а другой — task.site (при этом, сам Apache как бы будет не в курсе, какой он домен обслуживает. Он знает только что запрос на порт 8080 нужно так обработать, а на 8090 — ещё как-то). Всё, что теперь от апача понадобится — сделать виртуальные хосты, чтобы определить папки, из которых будут вызываться скрипты для каждого из сайтов.

/etc/apache2/sites-enabled/default.conf:
<VirtualHost 127.0.0.1:8090>
	. . .
	DocumentRoot /usr/share/redmine/public
	. . .
</VirtualHost>
<VirtualHost 127.0.0.1:8080>
	. . .
	DocumentRoot /var/www/dev.site
	. . .
</VirtualHost>


Всё. Если запрос будет на 127.0.0.1:8080, то будут выполняться скрипты из папки /var/www/dev.site. Если на 127.0.0.1:8090, то — /usr/share/redmine/public. Сам Apache вообще не в курсе, какие он домены обслуживает, так как снаружи его не видно.

Далее вступает в работу Nginx. Он отвечает за то, чтобы определить по имени сайта, куда отправлять запрос.

/etc/nginx/sites-available/default.conf:
server {
	listen 80;
	server_name dev.site;
	server_name_in_redirect off;

	location / {
		proxy_pass http://127.0.0.1:8080/;
		proxy_redirect off;
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		client_max_body_size 10m;
		proxy_connect_timeout 90;
	}
}

server {
	listen 80;
	server_name task.site;
	server_name_in_redirect off;
	
	location / {
		proxy_pass http://127.0.0.1:8090/;
		proxy_redirect off;
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		client_max_body_size 10m;
		proxy_connect_timeout 90;
	}
}


Как видно из листинга, он для dev.site делает proxy_pass http://127.0.0.1:8080/, а для task.siteproxy_pass http://127.0.0.1:8090/.

Теперь о том, что нужно поправить в ваших конфигах. В апаче нужно все сервера раскидать по отдельным портам. Сначала нужно прописать NameVirtualHost и Listen для каждого из них. При этом, обязательно нужно указывать не *:8080 или *:8081, *:8082, а конкретно 127.0.0.1:8080, 127.0.0.1:8081 и т.п., потому что «*» позволяет делать прямые запросы с любой машины, а «127.0.0.1» разрешает принимать соединения только с localhost.

То же самое — в блоках VirtualHost. Вместо <VirtualHost *:8080> нужно писать <VirtualHost 127.0.0.1:8080>

Из блока VirtualHost нужно выкинуть ServerName и ServerAlias, так как всё это будет обрабатываться на уровне Nginx.

В итоге вы получите такую ситуацию, что напрямую у вас sub.myhost.ru:8080 открываться не будет, так как Apache принимает запросы только от localhost. Он будет открываться только по запросу от Nginx, который будет определять какой сервер куда проксировать.

В вашем конфиге Nginx вместо server_name *.myhost.ru; нужно писать полное имя хоста. И для каждого хоста нужно создать отдельный блок server {}.

В конфигах Nginx ещё не забудьте приписать обработку статики (хотя у вас она и так прописана). Я из листинга этот момент выкинул, но его вообще нужно добавить, потому что одна из прелестей Nginx — как раз обработка статики. В каждый блок server {} можно добавить что-нибудь вроде:

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|js|pdf)$ {
	root /var/www/dev.site;
}


И далее, если вы хотите добавить подсервер: создаёте виртуальный хост апача на свободном порту, а в Nginx добавляете для него блок server {}.

В данный момент у вас просто небольшая каша получилась в конфигах.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы