воскресенье, 6 июля 2014 г.

Трёхслойный пирог: haproxy + nginx + apache

В одном из наших проектов вызрела надобность горизонтального расширения количества веб-серверов. Первое что пришло на ум - это установка балансировщика нагрузки. Но вот "на беду" параллельно с этим встала задача побыстрее отдавать статический контент. Для решения этой разумеется выбран nginx, хотя до сих пор не парились и работали только с apache. Итак, какие заморочки придумали.
Наши сайты (а у нас их около 70, мы не хостинг, это всё наши)  решили поделить на три кучи:
1. То, что будет балансироваться;
2. То, что не будет балансироваться и обрабатываться только apache;
3. То, что не будет балансироваться и будет обрабатываться nginx.
Решили начать с того, чтобы поднять nginx. Здесь все оказалось просто, документации куча, но есть нюанс... Логика работы такая - всё обрабатывается nginx, а php передаётся в  apache. Однако,  существует такая штука, как динамически создаваемые файлы. Так вот, в этом случае nginx подумает, что обращение за файлом должен обрабатывать он, но файла нет и клиент увидит 404. Решение есть и не одно. Привожу пример конфига nginx:
Первый вариант:
В секции http конфига nginx указываем
http { 
...
#этим мы укажем, что у нас существует некий сервер (apache), который висит на 127.0.0.1
#сам nginx у нас висит на 127.0.0.1:8080 так как есть ещё балансер на внешнем интерфейсе
  upstream nextserver {
    server 127.0.0.1;
  }
#эта секция нужна, чтобы в apache передать реальные адреса клиентов
  set_real_ip_from 50.ххх.ххх.59; # внешний адрес балансера
  real_ip_header   X-Forwarded-For;
  port_in_redirect off;

...
# остальные настройки, а также...
# Load config files from the /etc/nginx/conf.d directory
  include /etc/nginx/conf.d/*.conf;

} # end of http

В каталоге /etc/nginx/conf.d мы держим настройки для каждого домена, который будет обслуживаться nginx. Каждый домен в своём файле. Пример:
server {
    server_name  .domain.net;
    access_log /home/www/domain.net/logs/access_log;
    error_log  /home/www/domain.net/logs/error_log;


    location / {
        root   /home/www/domain.net/httpdocs;

#Проба на файл, если файла нет, оправляемся искать в локацию nextserver, то есть на apache
        try_files $uri $uri/ @nextserver;
    }

 
include /etc/nginx/conf.d/common-config;

}

А в файле /etc/nginx/conf.d/common-config хранится неизменяемая часть, одинаковая для всех доменов:
listen 127.0.0.1:8080;
index index.html index.php;

#Локация nextserver используется для передачи данных в apache так же как передаются данные для обработки php
  location @nextserver {
            proxy_pass   http://nextserver;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   Host $http_host;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  location ~ \.(php|htm)$ {
            proxy_pass   http://127.0.0.1;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    Host $http_host;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
  }

Собственно всё. Есть второй вариант обработки не найденного файла. Для этого вносим некоторые изменения в файл /etc/nginx/conf.d/common-config, после чего он выглядит так:
listen 127.0.0.1:8080;
index index.html index.php;
#как только получили код 404 отправляемся за ним на apache
error_page 404 = @notfound;

location @notfound {
            proxy_pass   http://127.0.0.1;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    Host $http_host;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
}

location ~ \.(php|htm)$ {
            proxy_pass   http://127.0.0.1;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    Host $http_host;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
}

А файл описания домена выглядит ещё проще
server {
    server_name  .domain.net;
    access_log /home/www/domain.net/logs/access_log;
    error_log  /home/www/domain.net/logs/error_log;

    location / {
        root   /home/www/domain.net/httpdocs;
    }

    include /etc/nginx/conf.d/common-config;
}


Теперь apache. Задача перенастройки apache свелась к тому, чтобы в конфигурациях виртуальных серверов заменить адрес на 127.0.0.1. Затем нужно получить от апача реальный адрес клиента. В nginx мы уже провели подготовку, но этого мало. Проблема решается установкой модуля mod_extract_forwarded, который берет данные из заголовка X-Forwarded-For и передает их в REMOTE_ADDR. Конфигурационный файл модуля примитивен:
LoadModule extract_forwarded_module modules/mod_extract_forwarded.so
MEForder refuse,accept
MEFrefuse all
#Разрешает локальный адрес и адрес балансера
MEFaccept 127.0.0.1 50.ххх.ххх.59
MEFaddenv on
MEFdebug off

И наконец, самое вкусное: haproxy. Впечатления балансер оставляет самое положительное. Работает очень быстро, настройки простые и гибкие. Используем версию 1.5.1, так как там есть пара удобных функций.
global #общие настройки
    log         127.0.0.1 local2 #куда пишем логи
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     10000 #максимальное число коннектов к балансеру
    user        apache #по-умолчанию балансер работает от пользователя haproxy
    group       apache
    daemon


defaults #настройки по-умолчанию
    mode        http
    log         global
    option      dontlognull
    option      httpclose
    option      httplog
    option      forwardfor #это нужно, чтобы заполнить X-Forwаrded-For и передать бекэнду
    option      redispatch
    option      httpchk HEAD / HTTP/1.0
    cookie serv insert
    timeout connect 5000 # default 5 second time out if a backend is not found
    timeout client 10000 # 10 sec
    timeout server 90000 # 10 sec
    maxconn     10000
    retries     3


frontend  main 50.ххх.ххх.59:80 #вешаем балансер на внешний интерфейс
#читаем файлы с именами доменов и в зависимости от того какой домен в каком файле
#используем то или иное правило балансировки
    acl host2apache     hdr_beg(host)   -i -f /etc/haproxy/unbalanced-apache
    acl host2nginx      hdr_beg(host)   -i -f /etc/haproxy/unbalanced-nginx
    acl url_static      path_beg        -i /mrtg /munin

    use_backend nginx           if url_static
    use_backend nginx           if host2nginx
    use_backend apache          if host2apache
    default_backend             balanced #по-умолчанию коннект пойдет на балансировку


#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such

# небалансированный бекэнд для доменов из списка
#---------------------------------------------------------------------
backend apache
    balance     roundrobin
    server      static 127.0.0.1:80 check

backend nginx
    balance     roundrobin
    server      static 127.0.0.1:8080 check
#
#---------------------------------------------------------------------
# round robin balancing between the various backends

# серверы, на которые пойдет распределение нагрузки
#---------------------------------------------------------------------
backend balanced
    balance     roundrobin
    server  web1 50.ххх.ххх.214:80 check
    server  web2 50.ххх.ххх.210:80 check
    server  web3 50.ххх.ххх.211:80 check


Вот такое решение трёхслойного пирога с вишенкой. Извращение, конечно, но работает.

Комментариев нет:

Отправить комментарий