Pull to refresh

Умпа-лумпы Интернет или как программировать DNS

Reading time 7 min
Views 13K
Если ты занимаешься администрированием сетей и тебе необходимо управлять большим, постоянно меняющимся количеством серверов, или если ты разрабатываешь приложение использующее динамическую балансировку нагрузки, или если ты разворачиваешь многоязыковый веб-сайт тебе никак не обойти внимание вопросы, связанные с правильной настройкой DNS.

Как и в любом деле в Интернет есть свои невидимые и незаметные работники, ежедневно, ежечасно… ежемиллисекундно выполняющие свои незамысловатые рутинные операции. Это конечно же маршрутизаторы, коммутаторы и другие устройства доставляющие пакеты данных из одной точки в другую. В них есть собственные регулировщики загрузки, перенаправляющие потоки другим путем в случае образования заторов. Но откуда они все знают куда доставить эти пакеты? Знающий человек скажет — «так там указан IP-адрес» и будет прав. Но обычный веб-серфер, как правило и представления не имеет об IP адресах, но тем не менее несколько миллиардов простых веб-серферов вполне себе успешно «бороздят» Интернет. Такую возможность им, да и «лицо» современной сети Интернет определяет другой «умпа-лумп» — служба DNS.

Большинство пользователей никогда не узнают о ее существовании, да что пользователи, многие системные администраторы, не говоря уже о программистах, представляют ее возможности достаточно поверхностно. Думаю, что не ошибусь, если скажу что и «крутые перцы» редко используют «продвинутые» приемы связанные с DNS. А между тем, многие функции, предоставляемые службой доменных имен, являются необходимыми элементами при создании высоконадежных и масштабируемых систем. Самые известные этих функций — это балансировка нагрузки и обеспечение отказоустойчивости. Более сложные — это геобалансировка, маршрутизация трафика. Да, да, именно маршрутизация с использованием средств DNS, но на этом мы сейчас останавливаться не будем. Мы начнем с простых вещей.



Что же наши «умпа-лумпы» DNS могут предложить для балансировки нагрузки? Наиболее очевидный и легко реализуемый способ, это так называемая балансировка round-robin. Она основана на том факте, что в DNS можно добавить несколько записей типа A (или AAA) для одного и того же имени. После чего DNS сервер будет отдавать их по-очереди, отправляя клиента то на один, то на другой сервер. Настроить такую балансировку несложно с использованием панели управления доменом любого провайдера или DNS сервера. Задача переходит из разряда «администрирование» в разряд «программирование» как только нам необходимо добавлять и убирать сервера в ферму автоматически. Например, если это сервера Amazon и у них динамический адрес. И уже точно, без написания скриптов не справиться с задачей обеспечения отказоустойчивости (failover).



Чтобы управлять сервером DNS он должен предоставлять определенное API. Самым простым «API» можно считать команду nsupdate, которая входит в состав практически всех дистрибутивов *nix и имеется в составе cygwin под Windows. Она может принимать команды из файла, и соответственно, ее работу можно запрограммировать, сгенерировав или подготовив заранее такой файл с командами. Например для динамического обновления записей о серверах мы можем настроить использование TSIG в BIND и далее:

# nsupdate -k файл_ключа файл_с_командами
Файл ключа выглядит так:
key domain.tld {
   algorithm hmac-md5;
   secret "PR3IxM1WocUm5uJ6-+yQeSmHbohhPR97K4RuNH2Vg7bYzM3R9Z27Li1w==";
};

Файл с командами так:
    server 192.168.100.1
    zone domain.tld
    update delete server1.domain.tld 3600 A
    update add server1.domain.tld 3600 A 192.168.100.99
    send

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

Другая разновидность API для управления DNS — это публичные сервисы DDNS (Dynamic Domain Name Services). Среди них можно назвать EasyDNS, DynDNS, NameCheap, Zoneedit и т. д. Интересующиеся могут поднять исходники универсального клиента обновлений ddclient и посмотреть в деталях как это реализуется. Все эти API представляют собой по сути протоколы обращения к определенным управляемым серверам DNS для добавления/обновления/удаления динамический адресных записей. Сервисы DDNS решают одну простую задачу — дать возможность людям с динамическим адресом, выдаваемым провайдером найти свой компьютер по имени. Вы регистрируетесь в сервисе, назначаете своему компьютеру в одном из предлагаемых доменов. Далее в клиентском ПО, на своем компьютере указываете это имя, регистрационное имя и пароль выбранного сервиса. После чего программа самостоятельно определяет ваш реальный IP адрес и регистрирует в DNS сервиса соответствующую адресную запись. При смене адреса провайдером, программа автоматически изменит его и в DNS сервиса. Таким образом вы получаете постоянное имя для меняющегося IP.

Если необходим полный контроль над DNS, например, если вы создаете сложный, высоконадежный распределенный сервис или являетесь провайдером хостинга и вам необходимо динамически перераспределять пространство IP-адресов, существуют полноценные API управления DNS: Dynect от DynDNS или PowerDNS Express от PowerDNS. Я остановлюсь на первом — Dynect, так как мне приходится, в рамках одного из наших проектов весьма плотно с ним работать.

Dynect — это API для управления DNS серверами компании «Dynamic Network Services Inc.», известной по своей торговой марке DynDNS. Я буду на нее ссылаться как на «Dyn». Сервера DNS Dyn размещены по всему миру и представлены в нескольких дата-центрах в каждом из географических регионов (к сожалению, пока без России, но это не создает больших проблем). Но при этом все они (или группа, зависит от прав) управляются как одно целое через Dynect. Такое распределение дает серьезное расширение возможностей службы имен с точки зрения надежности, геобалансировки и геотаргетирования.

Dynect предоставляет практически полный набор вызовов для управления DNS и DDNS (в том числе и сервисом DynDNS) плюс систему разграничения прав. Последнее важно, так как позволяет стоить на базе Dynect различные публичные сервисы и изолировать записи и пользователей в рамках одного или нескольких доменов. Функции разделены на группы:

  • Общие (General) — служат для управления подключением к API (Sessions), управления ключами TSIG, получения разнообразной информации.
  • Пользователи (Users) — как следует из названия, служат для управления пользователями и их правами.
  • Зоны (Zones) — группа, служащая для управления зонами DNS и записями в них.

Полный список функций представлен на сайте DynDNS и доступен после регистрации в качестве партнера или корпоративного клиента. Каждая из функций имеет как REST, так и SOAP вызов. Имеется еще экспериментальная поддержка XML-RPC, но я ее не пробовал. Моим любимым способом вызова является SOAP/WSDL, так как в этом случае у меня нет необходимости вручную описывать вызовы и проделывать связанные с этим операции поддержанию библиотеки вызовов в актуальном состоянии. В качестве языка программирования я использую Java (и среду Netbeans), но на сайте Dyn есть примеры и для других языков.

Посмотрим, как с использованием Dynect можно обновить запись. Логически мы должны выполнить следующие шаги: зарегистрироваться в Dynect (создать сессию), обновить запись, опубликовать изменения, выйти (закрыть сессию). Приводимый ниже код не полный. Так как с одной стороны, пример демонстрационный. С другой, собственно сам класс обращения к Dynect генерируется Netbeans автоматически по WSDL-файлу и править такой, автоматически сгенерированный код, нет никакой необходимости.

   public update() {
      try {
          if ( userLogin(customerCode, userName, userPassword) {
              if (updateARecord(zone, fqdn, ip))
                 publishZone(zone);
          userLogout();
          }
      } catch ( ErrorResponse ex) { // обработчик исключения
      }
   }

    /**
     * Зарегистрироваться в DynDNS. Регистрация самостоятельно обрабатывает 
     * исключения.
     * @param customerCode
     * @param userName
     * @param userPassword
     * @return 
     */
    public boolean userLogin( String customerCode, String userName, String userPassword ) {
            SessionLoginRequestType sessionRequest;
            SessionLoginResponseType sessionResponse;
            boolean status;

        sessionRequest = new SessionLoginRequestType();            
        sessionRequest.setUserName(userName);
        sessionRequest.setPassword(userPassword);
        ...
        try {
            sessionResponse = DynDNS.sessionLogin(sessionRequest);
            if ( success.equalsIgnoreCase(sessionResponse.getStatus()) ) {
                loginData = sessionResponse.getData();
                status = true;
            } else {
                status = false;
            }
        } catch (ErrorResponse ex) {
            status = false;
        }
        return status;
    }

    /**
     * Выйти из DynDNS
     * @throws ErrorResponse 
     */
    public void userLogout() throws ErrorResponse {
            SessionLogoutRequestType logoutRequest = new SessionLogoutRequestType();
            SessionLogoutResponseType logoutResponse;

        if ( loginData != null ) {
            logoutRequest.setToken(loginData.getToken());
            logoutResponse = DynDNS.sessionLogout(logoutRequest);            
        }
    }

  /**
     * Обновить запись типа A в DynDNS
     * @param zone
     * @param fqdn
     * @param ip
     * @return
     * @throws ErrorResponse 
     */
    public boolean updateARecord ( String zone, String fqdn, String ip ) throws ErrorResponse {
            UpdateARecordRequestType request = new UpdateARecordRequestType();
            UpdateARecordResponseType response;
            RDataA   value = new RDataA();
            boolean status;

        request.setToken(loginData.getToken());
        request.setFqdn(fqdn);
        request.setZone(zone);
        value.setAddress(ip);
        request.setRdata(value);
        response = DynDNS.updateARecord(request);

        status = success.equalsIgnoreCase(response.getStatus());
        return status;
    }

  /**
     * Функция обновляет зону в DynDNS - публикует изменения в DNS.
     * @param zone - обновляемая зона
     * @return 
     * @throws api2.dynect.net.ErrorResponse
     */
    public boolean publishZone ( String zone ) throws ErrorResponse {
            PublishZoneRequestType request;
            PublishZoneResponseType response;
            boolean status;

        request = new PublishZoneRequestType();
        request.setToken(this.loginData.getToken());
        request.setZone(zone);
        response = DynDNS.publishZone(request);
        status = success.equalsIgnoreCase(response.getStatus());

        return status;
    }

Внимательный читатель заметит, что в вызовах фигурирует еще некое customerCode. Да, чтобы получить доступ к Dynect API в полном объеме необходимо вступить с компанией Dyn в определенные отношения, и тогда вам выдадут значение для этого поля. Мы для ДинРУ, на базе Dynect разработали целый рад Интернет-инструментов.

В качестве заключения. Недавно получил сообщение от одной из известных Российских Интернет компаний о проводимых работах с просьбой переписать имя сервера в коде клиента обращения к их ресурсам… Похоже архитекторы этой системы вообще никогда не слышали про DNS… Может хватит пилить Интернет на коленке?
Tags:
Hubs:
+12
Comments 13
Comments Comments 13

Articles