Стань заметным в интернете
+7 (812) 347-70-60
09.01.2017

Создание отказоустойчивого кластера

В 2016 году исполнилось 10 лет с момента появления концепции "облачных" вычислений. За прошедшее время в IT-отрасли не только определились основные "игроки", но и сам термин устоялся, а провайдеры услуг уже не боятся испугать клиентов предложением "облачных" услуг.

Создание отказоустойчивого кластера

Тезис 1


В 2016 году исполнилось 10 лет с момента появления концепции "облачных" вычислений в 2006 году. За прошедшее время в IT-отрасли не только определились основные "игроки", такие как Amazon (EC2), Google (GCE), Microsoft (Azure), появились OpenSource-решения (OpenStack, OwnCloud, Seafile), но и сам термин устоялся, а провайдеры услуг уже не боятся испугать клиентов предложением "облачных" услуг. Примером, могут служить разнообразные хостинги предлагающие своим клиентам VPS, и даже частные "облака" на базе OpenStack. Несколько проигрывая в надёжности относительно классической виртуализации (XEN, VMWare, Hyper-V), они предлагают услуги хостинга дешевле.


Тезис 2


Большинство клиентов не имеет сайтов исполняющих задачи требующие надёжности 99,671% предоставляемой в ЦОД уровня Tier-1 (это задачи характерные скорее для рынка биржевых товаров, ценных бумаг, валют и др. финансовых инструментов в которых минуты простоя влекут для клиента несоизмеримый ущерб). Для большинства клиентов важнее не потерять основную массу посетителей сайта за сколько-то значимый % времени в течении суток. Отсюда напрашивается необходимость резервирования сайта с использованием технологий доступных на рынке (в том числе OpenSource-решений).


Тезис 3


Объединяя первые два тезиса можно заключить, что принципиально возможным является создание относительно дешёвого отказоустойчивого кластера с использованием общедоступных технологий и VPS предоставляемых разными провайдерами услуг на базе "облачных" вычислений. При этом мы можем выбирать VPS разной надёжности по шкале Tier и различающихся по географии ЦОД.


Необходимое достаточное


Наиболее простым и дешёвым является обеспечение отказоустойчивости web-сервера через поднятие на каждом VPS собственного NS-сервера с указанием сокращённого времени жизни зоны. Такая схема достаточно распространена и применяется, например на online.sbis.ru. Схема обеспечивает что через максимум 1 мин. все посетители не сумевшие попасть на сайт уйдут на работающий VPS. На практике же не наблюдается даже такая задержка, посетитель не может заметить что одна (и даже две) ноды перестали работать.


Стек технологий



Обеспечение отказоустойчивости и консистентности СУБД


БД для всех нод кластера едина и представлена в виде кластера Percona Xtra DB 5.6. Кластер MySQL настроен по схеме Multi-Master репликации. Это означает что изменение в БД на любой из нод будут синхронизированы и внесены в БД двух других. 3 ноды являются технологически минимально необходимым числом нод для кластера MySQL, независимо от применяемого решения (MySQL/Percona/MariaDB и т.д.). Также, кластер MySQL требует чтобы БД помещалась в памяти целиком. Это необходимо для успешной репликации. Увеличение производительности при возрастании нагрузки на кластер MySQL м.б. достигнуто увеличением числа нод и подключением к каждой из Master-нод по 1-2 Slave-нод. Хорошим показателем нагрузки на каждую из нод является число тредов MySQL в мониторинге Zabbix. Увеличение их числа свидетельствует либо о росте нагрузки, либо об увеличении числа медленных запросов к БД.

cluster_lsyncd.png


Обеспечение отказоустойчивости и консистентности файловых систем


На каждой ноде кластера запускается демон Lsyncd который через подсистему Inotify в ядре Linux следит за событиями изменения локального дерева файлов, собирая эту информацию каждые 10 сек. При обнаружении изменения он отправляет новые или изменённые файлы на соседние ноды через rsync. Передача происходит с параметром update, т.е. файл на получателе будет заменён только если отправляемый оказался новей. Это даёт возможность избежать коллизий и лишних операций.

cluster_inotify.png


Подготовка системы


# yum install php-xmlseclibs php-xmlrpc php-bcmath php-dba php-pecl-sqlite
# localedef ru_RU.UTF-8 -i ru_RU -fUTF-8
# mv -f /etc/php.d/20-sqlite3.ini.disabled /etc/php.d/20-sqlite3.ini
# mv -f /etc/php.d/30-mysqli.ini.disabled /etc/php.d/30-mysqli.ini
# mv -f /etc/php.d/30-xmlreader.ini.disabled /etc/php.d/30-xmlreader.ini
# yum install memcached
# chkconfig memcached on

Установка актуальной Bitrix-Env


# cd /tmp ; wget http://repos.1c-bitrix.ru/yum/bitrix-env.sh
# chmod a+x bitrix-env.sh
# ./bitrix-env.sh && rm -rf bitrix-env.sh

Замена MySQL в Bitrix-Env на Percona Cluster


# service mysqld stop
# rpm --nodeps -e mysql-server mysql mysql-libs
# yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm
# yum install Percona-XtraDB-Cluster-56
# service mysql start
# mysql_upgrade

Настройка Percona Server


Конфиги по умолчанию находятся здесь:

  • /usr/share/doc/Percona-XtraDB-Cluster-server-56-5.6.30/my-default.cnf
  • /usr/share/doc/Percona-XtraDB-Cluster-server-56-5.6.30/wsrep.cnf

Синхронизация файловых систем

# yum -y install lsyncd

В /etc/hosts прописываем на всех нодах статику:

<ip1>  node01 # node01.domain.tld
<ip2>  node02 # node02.domain.tld
<ip3>  node03 # node03.domain.tld

В /etc/sysctl.conf прописываем на всех нодах:

# lsyncd inotify limit
fs.inotify.max_user_watches = 16777216 # сколько файлов может отслеживать один пользователь; 8192 default
fs.inotify.max_queued_events = 65536   # максимальное число событий в очереди; 16384 default

В /etc/lsyncd.conf пишем:

settings{
  logfile    = "/var/log/lsyncd/lsyncd.log",
  statusFile = "/var/log/lsyncd/lsyncd.status",
  statusInterval = 5, --<== чтобы видеть что происходит без включения подробного лога
  nodaemon = false,
}

-- node01 (node.domain.tld)
sync{
-- метод синхронизации
    default.rsyncssh,
-- задержка обработки очереди после поступления события
    delay = 3,
-- отслеживаемый каталог
    source = "/home/bitrix/www/static/",
-- целевая нода, доступ по ssh
    host = "node01",
-- целевой каталог на удалённом хосте
    targetdir="/home/bitrix/www/static/",
-- файлы и каталоги, исключаемые из синхронизации
    exclude = {
            "/.htaccess",
            "/assets/",
            "/fonts/",
            "/images/",
            "/not_image/",
    },
-- Опции для rsync на удалённой стороне
    rsync = {
          binary = "/usr/bin/rsync",
          ipv4 = true,
          ipv6 = false,
          acls = true,
          perms = true,
          owner = true,
          archive = false,
          compress = false,
          update = true,
          sparse = true,
          _extra = { "-ausS", "--temp-dir=/tmp" },
          verbose = false,
    },
}

Тестируем (и смотрим /var/log/lsyncd/lsyncd.log):

# lsyncd -nodaemon /etc/lsyncd.conf

NS-сервер


Ставим NS в chroot:

# yum install bind bind-chroot

В /etc/resolv.conf пишем (помним про dhcp):

nameserver 127.0.0.1
nameserver 8.8.8.8

/etc/hosts дополняем:

# Percona cluster
<ip1>  node01.domain.tld
<ip2>  node02.domain.tld
<ip3>  node03.domain.tld

# NS continuity
<ip1>  ns1.domain.tld
<ip2>  ns2.domain.tld
<ip3>  ns3.domain.tld

Заполняем chroot, проставляем права:

# cd /var/named/chroot/var/log && mkdir named && chown named. named
# cd /var/named/chroot/etc/
# cp /etc/named.rfc1912.zones /var/named/chroot/etc/named.rfc1912.zones
# cp /etc/named.root.key /var/named/chroot/etc/named.root.key
# cp /var/named/named* /var/named/chroot/
# chown :named named.rfc1912.zones
# chown :named named.root.key

Создаём зоны (прямую и обратную):

# touch /var/named/data/domain.tld.zone
# chown :named /var/named/data/domain.tld.zone
# chmod 640 /var/named/data/domain.tld.zone

# touch /var/named/data/<ip1-reverse>.zone
# chown :named /var/named/data/<ip1-reverse>.zone
# chmod 640 /var/named/data/<ip1-reverse>.zone

Создаём файл для сбора статистики:

# touch /var/named/data/named_stats.txt
# chown :named /var/named/data/named_stats.txt
# chmod 640 /var/named/data/named_stats.txt

Пишем прямую зону:

$TTL 60
$ORIGIN domain.tld.

@ IN SOA ns1.domain.tld. info.domain.tld. (  ; имя_зоны IN SOA первичный_NS e-mail_администратора_NS
     2016072502 ; Serial
     10800      ; Refresh 3H    (как часто вторичные NS опрашивают первичный)
     3600       ; Retry   1H    (время ожидания после неудачной попытки опроса)
     604800     ; Expire  168H  (максимальное время валидности данных о зоне)
     86400 )    ; Minimum 24H   (минимальное время хранения зоны в кэше вторичного NS)

;;; NS ;;;

              IN NS ns1.domain.tld.
              IN NS ns2.domain.tld.
              IN NS ns3.domain.tld.

;;; MX ;;;

              IN MX 5  mx.domain.tld.
              IN MX 10 mail.domain.tld.

;;; only for NS1 ;;;

              IN A <ip1>    ; domain.tld..
www           CNAME @

;;; A ;;;

ns1           IN A <ip1> ; ns1.domain.tld.
ns2           IN A <ip2> ; ns2.domain.tld.
ns3           IN A <ip3> ; ns3.domain.tld.

node01        IN A <ip1> ; node01.domain.tld.
node02        IN A <ip2> ; node02.domain.tld.
node03        IN A <ip3> ; node03.domain.tld.

;;; TXT ;;;

@             IN TXT "v=spf1 include:_spf.domain.tld -all" 
@             IN SPF "v=spf1 include:_spf.domain.tld -all" 

Пишем named.conf

options {
    listen-on port 53 { any; };
    listen-on-v6 port 53 { none; }; // Отключаем работу на интерфейсе ipv6
    directory "/var/named";
    dump-file "/var/named/data/cache_dump.db";
    statistics-file "/var/named/data/named_stats.txt";
    listen-on { <ip1>; };
    version "dns by oev";                        // Скрываем версию DNS
    allow-query { any; };                        // Обычные запросы разрешаем всем
    allow-recursion { none; };                   // Рекурсивные запросы запрещаем
    auth-nxdomain no;                            // Не позволяет серверу отвечать авторитетно, если запрошенный домен не существует (RFC1035)
    forward only;                                // Собственный поиск несуществующих доменов не производится, отправляем в forwarders
    forwarders { };                              // Иначе будет DDoS, сейчас через DNS ботами управляют сволочи
//   forwarders { <ip1>; <ip2>; };               // ns1.domain.tld, ns2.domain.tld
};

key "rndc-key" {
   algorithm hmac-md5;
   secret "j/BLA+LA/BlAbla/bLAbLA==";
};

controls {
    inet 127.0.0.1 port 953
    allow { 127.0.0.1; } keys { "rndc-key"; };
};

// описание корневых серверов
zone "." IN {
    type hint;
    file "named.ca";
};

zone "domain.tld" IN {
    type master;
    file "data/domain.tld.zone";
    allow-update { none; };
};

zone "<ip1-reverse>.in-addr.arpa" IN {
    type master;
    file "data/<ip1-reverse>.zone";
    allow-update { none; };
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

logging {
    channel default_file {
    file "/var/log/named/default.log" versions 3 size 5m;
    severity dynamic;
    print-time yes;
};

category default { default_file; };
};

Генерируем секрет в rndc.key

cd /var/named/chroot/etc/
rndc-confgen > rndc.key
cp rndc.key /etc/rndc.key

из /etc/rndc.key удаляем options. key и controls из rndc.key добавляем в /var/named/chroot/etc/named.conf

Проверяем на ошибки:

# named-checkconf /var/named/chroot/etc/named.conf