«Идеальный» www кластер. Часть 1. Frontend: NGINX + Keepalived (vrrp) на CentOS

от автора

Этом цикле статей «Идеальный www кластер», я хочу передать базовые основы построения высокодоступного и высокопроизводительного www решения для нагруженных web проектов для неподготовленного администратора.
Статья будет содержать пошаговую инструкцию и подойдет любому человеку кто освоил силу copy-past
Ошибки найденые вами, помогут в работе и мне и тем кто будет читать эту статью позже! Так что любые улучшение и правки приветствуются!

На frontend мы будем использоваться связку из двух службы:

keepalived — реализации протокола VRRP (Virtual Router Redundancy Protocol) для Linux. Демон keepalived следит за работоспособностью машин и в случае обнаружения сбоя — исключает сбойный сервер из списка активных серверов, делегируя его адреса другому серверу.

nginx [engine x] — это HTTP-сервер и обратный прокси-сервер, а также почтовый прокси-сервер, написанный Игорем Сысоевым. Уже длительное время он обслуживает серверы многих высоконагруженных российских сайтов, таких как Яндекс, Mail.Ru, ВКонтакте и Рамблер. Согласно статистике Netcraft nginx обслуживал или проксировал 15.08% самых нагруженных сайтов в октябре 2013 года.

Основная функциональность HTTP-сервера

  • Обслуживание статических запросов, индексных файлов, автоматическое создание списка файлов, кэш дескрипторов открытых файлов;
  • Акселерированное обратное проксирование с кэшированием, простое распределение нагрузки и отказоустойчивость;
  • Акселерированная поддержка FastCGI, uwsgi, SCGI и memcached серверов с кэшированием, простое распределение нагрузки и отказоустойчивость;
  • Модульность, фильтры, в том числе сжатие (gzip), byte-ranges (докачка), chunked ответы, XSLT-фильтр, SSI-фильтр, преобразование изображений; несколько подзапросов на одной странице, обрабатываемые в SSI-фильтре через прокси или FastCGI, выполняются параллельно;
  • Поддержка SSL и расширения TLS SNI.

Другие возможности HTTP-сервера

  • Виртуальные серверы, определяемые по IP-адресу и имени;
  • Поддержка keep-alive и pipelined соединений;
  • Гибкость конфигурации;
  • Изменение настроек и обновление исполняемого файла без перерыва в обслуживании клиентов;
  • Настройка форматов логов, буферизованная запись в лог, быстрая ротация логов;
  • Специальные страницы для ошибок 3xx-5xx;
  • rewrite-модуль: изменение URI с помощью регулярных выражений;
  • Выполнение разных функций в зависимости от адреса клиента;
  • Ограничение доступа в зависимости от адреса клиента, по паролю (HTTP Basic аутентификация) и по результату подзапроса;
  • Проверка HTTP referer;
  • Методы PUT, DELETE, MKCOL, COPY и MOVE;
  • FLV и MP4 стриминг;
  • Ограничение скорости отдачи ответов;
  • Ограничение числа одновременных соединений и запросов с одного адреса;
  • Встроенный Perl.

Важно! Для приведенного ниже решения, у нас должно быть 2 сетевых интерфейса на каждой из нод keepalived
мы должны точно указать нашу маску и понимать где в нашей сети находится broadcast, если этого не сделать, то будем очень долго пытаться понять почему у нас все работает не так как мы хотим!

# Моя приватная сеть
[root@nginx-frontend-01 ~]#

nano /etc/sysconfig/network-scripts/ifcfg-eth2
DEVICE=eth2 BOOTPROTO=static ONBOOT=yes IPADDR=10.100.100.56 NETWORK=10.100.100.0 NETMASK=255.255.255.0 BRAODCAST=10.100.100.255 

# Публичная сеть
[root@nginx-frontend-01 ~]#

 nano /etc/sysconfig/network-scripts/ifcfg-eth3 
DEVICE=eth3 BOOTPROTO=static ONBOOT=yes IPADDR=72.x.x.1 NETMASK=255.255.255.248 BRAODCAST=72.x.x.55 GATEWAY=72.x.x.49 

То есть в моей публичной сети, маска /29 и значит мой broadcast x.x.x.55, если бы была сеть /24, то можно было бы указать x.x.x.255
Если это перепутать, то вы отгребете кучу проблем

# Устанавливаем keepalived

yum install keepalived -y 

# Это очень плохо, этого делать не нужно! только в тестовых целях и на свой страх и риск, я предупредил. Выключаем selinux

sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux 

# Настраиваем keepalived на первой ноде nginx-frontend-01, очень важно, знак коментария "!" а не "#"

mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.old && nano /etc/keepalived/keepalived.conf 

[root@nginx-frontend-01 ~]#

 nano /etc/keepalived/keepalived.conf 
! Configuration File for keepalived  global_defs {         notification_email {                 root@localhost         }         notification_email_from root@localhost         smtp_server                     localhost         smtp_connect_timeout            30 ! Именное обозначение этого сервера         router_id                       nginx-frontend-01 }  vrrp_instance nginx2 { ! Состояние в котором стартует нода, в этом случае она резерв         state                   BACKUP  ! Наш публичиный интерфейс         interface               eth3  ! Индификатор, в разных vrrp_instance он должен быть разным          virtual_router_id       102  ! Это приоритет этой ноды перед другими, у BACKUP он всегда должен быть ниже чем у MASTER         priority                100          advert_int              1         dont_track_primary  ! Тут можно на всякий случай указать наш broadcast         mcast_src_ip x.x.x.55  ! Пароль можно указать любой, но одинаковый для серверов         authentication {                 auth_type PASS                 auth_pass b65495f9         } ! Этот адрес возмет себе сервер, если MASTER в сети упадет         virtual_ipaddress {                 x.x.x.2/29 dev eth3         }          } vrrp_instance nginx1 { ! Эта нода - мастер, она использует адрес из этой секции и ее заменит другая, если эта упадет         state                   MASTER  ! Наш публичиный интерфейс         interface               eth3  ! Индификатор, в разных vrrp_instance он должен быть разным          virtual_router_id       101  ! Для мастера это значение обязательно выше чем для backup         priority                200          advert_int              1         dont_track_primary  ! Тут можно на всякий случай указать наш broadcast         mcast_src_ip x.x.x.55  ! Пароль можно указать любой, но одинаковый для серверов         authentication {                 auth_type PASS                 auth_pass b65495f8         }         virtual_ipaddress { ! Нода стартует с этим адресом, если эта нода упадет, этот адрес подхватит другая                 x.x.x.1/29 dev eth3                         } ! Для мастера нужно прописать gateway         virtual_routes {                 default via x.x.x.49 dev eth3 metric 2         } } 

# Настраиваем keepalived на второй ноде nginx-frontend-02 очень важно, знак коментария "!" а не "#"

mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.old && nano /etc/keepalived/keepalived.conf 

[root@nginx-frontend-02 ~]#

nano /etc/keepalived/keepalived.conf 
! Configuration File for keepalived  global_defs {         notification_email {                 root@localhost         }         notification_email_from root@localhost         smtp_server                     localhost         smtp_connect_timeout            30 <b>! Именное обозначение этого сервера</b>         router_id                       nginx-frontend-02 }  vrrp_instance nginx1 { ! Состояние в котором стартует нода, в этом случае она резерв         state                   BACKUP  ! Наш публичиный интерфейс         interface               eth3  ! Индификатор, в разных vrrp_instance он должен быть разным          virtual_router_id       101  ! Это приоритет этой ноды перед другими, у BACKUP он всегда должен быть ниже чем у MASTER         priority                100          advert_int              1         dont_track_primary  ! Тут можно на всякий случай указать наш broadcast         mcast_src_ip x.x.x.55  ! Пароль можно указать любой, но одинаковый для серверов         authentication {                 auth_type PASS                 auth_pass b65495f9         } ! Этот адрес возмет себе сервер, если MASTER в сети упадет         virtual_ipaddress {                 x.x.x.1/29 dev eth3 }          } vrrp_instance nginx2 { ! Эта нода - мастер, она использует адрес из этой секции и ее заменит другая, если эта упадет         state                   MASTER  ! Наш публичиный интерфейс         interface               eth3  ! Индификатор, в разных vrrp_instance он должен быть разным          virtual_router_id       102  ! Для мастера это значение обязательно выше чем для backup         priority                200          advert_int              1         dont_track_primary  ! Тут можно на всякий случай указать наш broadcast         mcast_src_ip x.x.x.55  ! Пароль можно указать любой, но одинаковый для серверов         authentication {                 auth_type PASS                 auth_pass b65495f9         } ! Нода стартует с этим адресом, если эта нода упадет, этот адрес подхватит другая         virtual_ipaddress {                 x.x.x.2/29 dev eth3                         }         virtual_routes { ! Для мастера нужно прописать gateway                 default via x.x.x.49 dev eth3 metric 2         } } 

# Добавляем в автозагрузку и запускаем

 chkconfig keepalived on && service keepalived restart 

# Добавляем разрешения фаервола, больше половина проблем, из за того что мы забываем про фаервол!

iptables -A INPUT -i eth3 -p vrrp -j ACCEPT iptables -A OUTPUT -o eth3 -p vrrp -j ACCEPT iptables -A INPUT -d 224.0.0.0/8 -i eth3 -j ACCEPT iptables-save > /etc/sysconfig/iptables

# Это очень важный шаг

echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf && sysctl -p 

# Проверяем

/etc/init.d/keepalived restart && tail -f -n 100 /var/log/messages

# Проверяем как между собой общаются наши ноды keepalived

 tcpdump -vvv -n -i eth3 host 224.0.0.18 

# Мы должны увидеть это

x.x.x.55 > 224.0.0.18: VRRPv2, Advertisement, vrid 102, prio 200, authtype simple, intvl 1s, length 20, addrs: x.x.x.2 auth "b65495f9" 07:50:50.019548 IP (tos 0xc0, ttl 255, id 5069, offset 0, flags [none], proto VRRP (112), length 40) x.x.x.55 > 224.0.0.18: VRRPv2, Advertisement, vrid 101, prio 200, authtype simple, intvl 1s, length 20, addrs: x.x.x.1 auth "b65495f9" 

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

# Подключаем официальный репозиторий nginx для CentOS 6

rpm -Uhv http://nginx.org/packages/rhel/6/noarch/RPMS/nginx-release-rhel-6-0.el6.ngx.noarch.rpm

# Обновляем систему и устанавливаем nginx

yum update -y yum install nginx 

# Удаляем хосты поумолчанию

rm -f /etc/nginx/conf.d/default.conf rm -f /etc/nginx/conf.d/virtual.conf rm -f /etc/nginx/conf.d/ssl.conf

# Приводим главный конфиг к подобному виду

 mv  /etc/nginx/nginx.conf /etc/nginx/nginx.conf.old  nano /etc/nginx/nginx.conf 
user  nginx;  # Количество процессов ожидаюищих соединения worker_processes 10;  pid /var/run/nginx.pid;  events { # Максимальное количество обслуживаемых клиентов онлайн 	worker_connections 1024;  # epoll — эффективный метод, используемый в Linux 2.6+ http://nginx.org/ru/docs/events.html         use epoll;  # Рабочий процесс за один раз будет принимать сразу все новые соединения multi_accept on;  } error_log  /var/log/nginx/error.log warn;  http {     include       /etc/nginx/mime.types;     default_type  application/octet-stream;      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                       '$status $body_bytes_sent "$http_referer" '                       '"$http_user_agent" "$http_x_forwarded_for"';      access_log  /var/log/nginx/access.log  main;          connection_pool_size            256;         client_header_buffer_size       4k; 	client_max_body_size 100m;         large_client_header_buffers     8 8k;         request_pool_size               4k;  	output_buffers  1 32k;         postpone_output 1460;  # Все страницы будут ужиматься gzip gzip  on; gzip_min_length 1024; gzip_proxied     any; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain text/xml application/xml application/x-javascript text/javascript text/css text/json;     gzip_comp_level  8;     gzip_disable "MSIE [1-6]\.(?!.*SV1)";          sendfile        on;         tcp_nopush      on;         tcp_nodelay     on;          keepalive_timeout       75 20;  	server_names_hash_max_size 8192;         ignore_invalid_headers  on;         server_name_in_redirect off;          proxy_buffer_size       8k;         proxy_buffers           8 64k;         proxy_connect_timeout   1000;         proxy_read_timeout      12000;         proxy_send_timeout      12000;  # Мы рассказываем где будет храниться кеш, но по умолчанию я его не использую 	proxy_cache_path /var/cache/nginx levels=2 keys_zone=pagecache:5m inactive=10m max_size=50m;  # Передаем backend реальный адрес клиента для mod_rpaf         real_ip_header          X-Real-IP;         proxy_set_header        Host            $host;         proxy_set_header        X-Real-IP       $remote_addr;         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;          allow all;      include /etc/nginx/conf.d/*.conf; } 

# Теперь приведем в порядок наш универсальный vhost

nano /etc/nginx/conf.d/all.conf
upstream web { # Перечисляем все backend между которыми nginx будет балансировать клиентов, говорим количество fail для баны backend ноды и таймаут 	# back01 	server		10.211.77.131 weight=10 max_fails=60 fail_timeout=2s; 	# back02 	server         10.211.77.136 weight=10 max_fails=60 fail_timeout=2s;  } server  {    listen   80; 	location / { 		proxy_pass	http://web; proxy_set_header   Host   $host; proxy_set_header   X-Real-IP  $remote_addr; proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for; } }  

# И наш конфиг для работы сайта по SSL, для кажого сайта должен быть свой конфиг со своим сертификатом

 nano /etc/nginx/conf.d/ssl.conf 
upstream ssl {         # back01         # server                10.211.77.131 weight=10 max_fails=60 fail_timeout=2s;         # back02         server 10.100.100.63 weight=10 max_fails=60 fail_timeout=2s;  } server  {    listen   443;  ssl on; ssl_certificate         /etc/nginx/ssl/GeoTrustCA.crt; ssl_certificate_key     /etc/nginx/ssl/GeoTrustCA.key;  # Увеличиваем безопасность нашего SSL соединения  ssl_ciphers      RC4:HIGH:!aNULL:!MD5:!kEDH; ssl_session_cache shared:SSL:10m; ssl_prefer_server_ciphers on; ssl_protocols           SSLv3 TLSv1;          location / {                 proxy_pass      http://ssl; proxy_set_header   Host   $host; proxy_set_header   X-Real-IP  $remote_addr; proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for; } }

# Запускаем nginx и добавляем его в автозагрузку!

 /etc/init.d/nginx start && chkconfig nginx on 

Продолжение следует, спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/198934/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *