Pull to refresh

Amazon Route 53 и бесперебойная работа сайта

Reading time7 min
Views18K
Amazon
На протяжении последних 3-х недель мне несколько раз приходилось сталкиваться с перебоями в работе моего хостинга, по скольку я веду проект с большой посещаемостью даже кратковременный простой сказывается на количестве звонков с сайта.
При этом всё могло быть гораздо лучше, если вместо стандартной страницы «Сервер не найден» повесить заставку в стиле сайта с текстом «Сайт временно не доступен, позвоните по телефону или оставьте заявку».
Было принято решение воспользоваться DNS сервисом от Amazon для быстрой смены A-записи.



Описание Amazon Route 53

Это надёжный DNS сервис, с достаточно удобным API для редактирования, добавления и удаления записей.
Стоимость услуги составляет 1$ в месяц + 0.50$ за первый миллион запросов и 0.25$ за каждый следующий миллион запросов.
Серьёзным неудобством в некоторых случаях является способ оплаты — оплатить услугу можно только по пластиковой карте, однако есть выход из положения, например можно воспользоваться виртуальной картой Visa от qiwi.
После регистрации и оплаты услуги мы получаем пару ID и ключ, которые в последствии используются для авторизации в API.

Готовим домен

Для подключения домена и настройки всех необходимых записей использовать API не очень удобно.
Я предлагаю воспользоваться сервисом Interstate53.
Сервис представляет собой интерфейс для управления аккаунтом на Route 53 и обеспечивает полный спектр его функций.
Interstate53
Все записи заполняем как раньше — A-записи идут на IP-адрес основного хостинга.

Выбираем хостинг

В моём случае резервный сервер должен быть как можно более дешевым и обладать следующим набором функций:
  • PHP — для работы с Route 53 API
  • cron — для периодической проверки основного хостинга на доступность
  • статический HTML — для показа заставки

Выбор пал на Джино, это единственных хостинг который я нашел, где можно отказаться от поддержки баз данных не отказываясь от PHP. Цена вышла 75 рублей в месяц.
Основной хостинг clodo, хотя это не имеет значения.

Настраиваем основной хостинг

Нас интересует работоспособность сайта в любых условиях, а значит, если зависнет база или apache необходимо также менять A-запись. Для тестирования создадим небольшой PHP скрипт, доступный по IP адресу сервера.
  1.  
  2. $link = mysql_connect('localhost', 'root', 'password') or die('0');
  3. $result = mysql_query('SELECT VERSION();');
  4. if(!$result)
  5.         die('0');
  6. mysql_close($link);
  7. echo 'OK!';
  8.  

В случае неполадок с базой скрипт выдаст 0, если не работает сервер или завис apache, скрипт не выдаст ничего, в случае успеха будет передана надпись «OK!».
На этом настройка основного сервера завершена.

Настраиваем дублирующий хостинг

Для работы с API Route 53 я использовал готовый PHP класс Route53.
  1.  
  2. define('MASTER_HOST', 'xxx.xxx.xxx.xxx');//IP адрес основного сервера
  3. define('SLAVE_HOST', 'xxx.xxx.xxx.xxx');//IP адрес дополнительного сервера
  4. define('ACCESS_KEY', 'my_key');//Ключ для доступа к API
  5. define('SECRET_KEY', 'my_pass');//Пароль для доступа к API
  6. define('ZONE_ID', '/hostedzone/my_zone_id');//ID зоны (можно увидеть после добавлени домена в interstate53.com)
  7.  
  8. include 'r53.php';
  9. $route = new Route53(ACCESS_KEY, SECRET_KEY);
  10. $ns = $route->listResourceRecordSets(ZONE_ID);//Сразу получаем значения ns записей для домена
  11.  
  12. function test(){
  13.         try{
  14.                 $answer = file_get_contents('http://'.MASTER_HOST.'/');
  15.                 if($answer == 'OK!')//Проверяем ответ от основного сервера
  16.                         return true;
  17.                 else
  18.                         return false;
  19.         }catch (Exception $e){
  20.                 return false;
  21.         }
  22. }
  23.  
  24. function update($arr){
  25.         GLOBAL $route;
  26.         if(count($arr) > 0)
  27.         {
  28.                 $route->changeResourceRecordSets(ZONE_ID, $arr);//Обновление ns-записей
  29.         }
  30. }
  31.  
  32. function changeIP($name, $ttl, $from_ip, $to_ip){//Функция генерирует массив в котором одна запись удаляет старую, а вторая добавляет новую
  33.         return Array(
  34.                 "
  35.                 <Change>
  36.                         <Action>DELETE</Action>
  37.                         <ResourceRecordSet>
  38.                                 <Name>$name</Name>
  39.                                 <Type>A</Type>
  40.                                 <TTL>$ttl</TTL>
  41.                                 <ResourceRecords>
  42.                                         <ResourceRecord>
  43.                                                 <Value>$from_ip</Value>
  44.                                         </ResourceRecord>
  45.                                 </ResourceRecords>
  46.                         </ResourceRecordSet>
  47.                 </Change>
  48.                 ",
  49.                 "
  50.                 <Change>
  51.                         <Action>CREATE</Action>
  52.                         <ResourceRecordSet>
  53.                                 <Name>$name</Name>
  54.                                 <Type>A</Type>
  55.                                 <TTL>$ttl</TTL>
  56.                                 <ResourceRecords>
  57.                                         <ResourceRecord>
  58.                                                 <Value>$to_ip</Value>
  59.                                         </ResourceRecord>
  60.                                 </ResourceRecords>
  61.                         </ResourceRecordSet>
  62.                 </Change>
  63.                 "
  64.         );
  65. }
  66.  
  67. $changes = Array();
  68.  
  69. if(!test()){
  70.         if(!test()){// делаем проверку 2 раза для надёжности
  71.                 foreach($ns['ResourceRecordSets'] as $record){
  72.                         if($record['Type'] == 'A' && $record['ResourceRecords']['0'] == MASTER_HOST){
  73.                                 $changes = array_merge($changes, changeIP($record['Name'], $record['TTL'], MASTER_HOST, SLAVE_HOST));
  74.                         }
  75.                 }
  76.                 update($changes);
  77.                 exit(0);
  78.         }
  79. }
  80.  
  81. foreach($ns['ResourceRecordSets'] as $record){
  82.         if($record['Type'] == 'A' && $record['ResourceRecords']['0'] == SLAVE_HOST){
  83.                 $changes = array_merge($changes, changeIP($record['Name'], $record['TTL'], SLAVE_HOST, MASTER_HOST));
  84.         }
  85. }
  86. update($changes);
  87. exit(0);
  88.  


Немного резюмирую код по порядку: проверяем сервер на ответ «OK!» 2 раза для надёжности, если ответ не верный, меняем записи, если ответ верный, возвращаем записи на место.

Остаётся настроить cron

В Джино это делается достаточно просто, кидаем полученный скрипт в папку тестового домена mydomain.jino.ru, в «Задания по расписанию» нажимаем «Новое задание», далее заполняем поля:
Задание: curl -s mydomain.jino.ru/route53/core.php > /dev/null
Комментарий:
Минуты, Часы, Дни, Месяцы, Дни недели: *
Наш скрипт будет выполняться каждую минуту. Это означает, что максимальное время простоя сайта составит 2 минуты 29 секунд в худшем раскладе и 30 секунд (зависит от TTL A-записи) в лучшем.

Итог

В идеале конечно следует держать копию сайта на дополнительном хостинге — заставка с контактами и надписью «сервер не работает» может отпугнуть некоторых людей. Но этот способ тоже имеет право на существование.
Tags:
Hubs:
Total votes 12: ↑11 and ↓1+10
Comments9

Articles