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

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

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

Хочу отметить, что эта инструкция родилась в процессе миграции web-систем компании Acronis в высокодоступный кластер. Надеюсь мои заметки будут полезны и для Вас!.

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

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

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

Другими словами, у нас 2 сервера на которых прописано по одному публичному адресу. Если любой из этих серверов падает, то адрес упавшего подхватывается вторым.
Демоны keepalived общаются по протоколу VRRP, посылая друг другу сообщения на адрес 224.0.0.18.
Если сосед не прислал свое сообщение, то по истечению периода он считается умершим и оба адреса обслуживает оставшаяся нода. Как только упавший сервер начинает слать свои сообщения в сеть, все возвращается на свои места

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
BROADCAST=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
BROADCAST=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
! Именное обозначение этого сервера
        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

# Это очень важный шаг
Установка этой переменной позволяет отдельным локальным процессам выступать от имени внешнего (чужого) IP адреса

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 https://nginx.org/packages/rhel/6/noarch/RPMS/nginx-release-rhel-6-0.el6.ngx.noarch.rpm

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

yum update -y
yum install nginx 

# Удаляем хосты поумолчанию, по умолчанию там стандартное приветствие 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+ https://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  5;
    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	https://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      https://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 

https://habrahabr.ru/company/acronis/blog/198934/

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

Войти с помощью: 

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