Failover per DNS mit ISPConfig

Ich arbeite mit DNS Round Robin. Dieses hat zum Vorteil, dass der Traffic ca. zu 50% jeweils auf die IPs aufgeteilt wird.

admin@dns:~# dig cache.domain.ch

;; QUESTION SECTION:
;cache.domain.ch.			IN	A

;; ANSWER SECTION:
cache.domain.ch.		299	IN	A	1.1.1.1
cache.domain.ch.		299	IN	A	2.2.2.2

Jedoch kann man damit leider kein Failover betreiben. Sollte eine IP nicht erreichbar sein und der Client empfängt die nicht funktionierende IP, schlägt der Aufbau fehl.

Aus diesem Grund habe ich eine ISPConfig Extension entwickelt, welche in regelmässigen Abständen die Erreichbarkeit eines Services überprüft und im Fehlerfall den Record auf inactive setzt.

Hierzu nehme ich mir die TXT Records zu Hilfe. In einem TXT Record speichere ich dann die zu überprüfenden Services in einem JSON String:

admin@dns:~#dig _failover.cache.domain.ch TXT

;; QUESTION SECTION:
;_failover.cache.domain.ch.		IN	TXT

;; ANSWER SECTION:
_failover.cache.domain.ch.	3599	IN	TXT	"[{"name":"cache.domain.ch.","data":"1.1.1.1","check":"443","ttl":"300"},{"name":"cache.domain.ch.","data":"2.2.2.2","check":"443", "ttl":"300"}]"

Mit dem folgenden Script hole ich mir dann die Daten, überprüfe den Service und setze den Record dann im Fehlerfall auf deaktiviert.

<?php
/*
 * ISPConfig Extensions for DNS Failover
 *
 * (c) Kai Tobias Burwieck <kai@burwieck.ch>
 *
 */

// ISPConfig URL for REMOTE API
$soap_location = 'https://<ispconfig server>/remote/index.php';
$soap_uri = 'https://<ispconfig server>/remote/';

// ISPConfig Login
$username = '<ispconfig user>';
$password = '<ispconfig password>';

$client = new SoapClient(
                  null, 
                  array(
                    'location' => $soap_location,
                    'uri'      => $soap_uri,
                    'trace' => 1,
                    'exceptions' => 1
                  ));

try {
    //* Login to the remote server
    if($session_id = $client->login($username,$password)) {

        // get Records for failover RR
        $records = $client->dns_txt_get($session_id, array('name' => '_failover.%'));
        if (count($records)) {
           foreach ($records as $record) {
              $data = json_decode($record['data'], true);
              if ($data) {
                foreach ($data as $row) {
                   $available = check($row);
                   $exist = $client->dns_a_get(
                              $session_id,
                              array('name' => $row['name'], 'data' => $row['data'])
                           );
                   if ($exist) {
                      $rr = $exist[0];
                      $rr['active'] =  $available ? "Y" : "N";
                      if($exist[0]['active'] !== $rr['active']) {
                         echo $rr['name'] . ' (' . $rr['data'] . ') becomes ' . ($available ? 'available' : 'unavailable') . "\n";
                         $rr['ttl'] = array_key_exists('ttl', $row) ? (int)$row['ttl'] : $rr['ttl'];
                         $client->dns_a_update($session_id, $rr['sys_userid'], $rr['id'], $rr);
                      }
                   } else {
                      throw new Exception('Record for ' . $row['name'] . ' (' . $row['data'] . ') not found. Please first create Records first');
                   } 
                }
              } else {
                throw new Exception('invalid json: ' . $record['data']);
              } 
           }
        }
    }
  
} catch(SoapFault $e) {
        echo ('SOAP Error: '.$e->getMessage());
        exit(1);
} catch(Exception $e) {
        echo('ERR: ' . $e->getMessage());
        exit(1);
}
exit(0);


/*
 * check ip
 * example: array('check' => '443', 'data' => '1.2.3.4') 
 *
 * @param array $data
 * 
 * @return bool if given resource is available
*/
function check(array $data) {
   if (array_key_exists('check', $data) && array_key_exists('data', $data)) {
       $connection = @fsockopen($data['data'], $data['check'], $errno, $errstr, 1);
       $available = is_resource($connection);
       if ($available) {
          fclose ($connection);
       }
       return $available;
    }
    throw new Exception('key check neither ip found in '.json_encode($data));
}

Sollte nun ein Service nicht erreichbar sein, gibt der DNS Server spätestens nach 5 Minuten nur noch den verfügbaren Service.

Nun nur noch den Cron hinzufügen:


* * * * * /usr/bin/php /usr/local/ispconfig/server/DNS-Failover.php 2>&1

simsalabim.
Nun extistiert ein Health Check für DNS Round Robin A Records.

You must be logged in to post a comment.