Equilibrio de carga geográfico con DNS

El equilibrio de carga geográfico distribuye el tráfico entre servidores distribuidos globalmente basándose en la ubicación del cliente, optimizando la latencia y mejorando la experiencia del usuario. El enrutamiento geográfico basado en DNS dirige a los clientes al datacenter más cercano o apropiado en función de su ubicación geográfica. Esta guía cubre la implementación de GeoIP con BIND, enrutamiento geográfico con PowerDNS, enrutamiento basado en latencia estilo Route53, mecanismos de failover y estrategias de prueba.

Tabla de contenidos

  1. Descripción general del equilibrio de carga geográfico
  2. BIND con módulo GeoIP
  3. Enrutamiento geográfico con PowerDNS
  4. Enrutamiento estilo AWS Route53
  5. Enrutamiento basado en latencia
  6. Failover y verificación de salud
  7. Prueba del enrutamiento geográfico
  8. Monitoreo y registro
  9. Solución de problemas

Descripción general del equilibrio de carga geográfico

Estrategias de equilibrio de carga geográfico:

  1. Basado en GeoIP: Enrutamiento basado en base de datos de geolocalización de IP de cliente
  2. Basado en latencia: Enrutamiento al servidor más cercano por latencia de red
  3. Geolocalización: Enrutamiento basado en límites geográficos
  4. Ponderado: Enrutamiento basado en distribución de porcentaje
  5. Failover: Enrutamiento a ubicaciones alternativas en caso de falla primaria

Beneficios:

  • Latencia reducida para usuarios finales
  • Mejor experiencia del usuario
  • Cumplimiento mejorado para residencia de datos
  • Distribución de carga entre regiones
  • Mitigación de DDoS a través de distribución

BIND con módulo GeoIP

Instalar BIND con soporte GeoIP:

sudo apt update
sudo apt install bind9 bind9-utils bind9-dnsutils bind9-libs

# Install GeoIP database
sudo apt install libgeoip1 geoip-bin

# Download maxmind GeoIP database (GeoLite2)
cd /usr/share/GeoIP
sudo wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
sudo tar xzf GeoLite2-Country.tar.gz

Configurar BIND con GeoIP (BIND 9.10+):

cat > /etc/bind/named.conf.acl <<'EOF'
# Define GeoIP ACLs
geoip {
    country-db "/usr/share/GeoIP/GeoLite2-Country.mmdb";
};

acl us-clients {
    geoip country "US";
};

acl eu-clients {
    geoip country "DE" "FR" "GB" "IT" "ES";
};

acl asia-clients {
    geoip country "JP" "CN" "IN" "SG" "AU";
};

acl other-clients {
    any;
};
EOF

Configurar enrutamiento geográfico a nivel de zona:

cat >> /etc/bind/named.conf.local <<'EOF'
zone "example.com" {
    type master;
    file "/etc/bind/zones/db.example.com";
    allow-transfer { 10.0.0.0/8; };
};

# Use CNAME or view-based routing for geographic distribution
view "us-view" {
    match-clients { us-clients; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.us";
    };
};

view "eu-view" {
    match-clients { eu-clients; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.eu";
    };
};

view "asia-view" {
    match-clients { asia-clients; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.asia";
    };
};

view "other-view" {
    match-clients { other-clients; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com";
    };
};
EOF

Crear archivos de zona específicos por región:

# US zone file
cat > /etc/bind/zones/db.example.com.us <<'EOF'
$TTL 300
@   IN  SOA ns1.example.com. admin.example.com. (
        2024010101  ; serial
        3600        ; refresh
        1800        ; retry
        604800      ; expire
        300         ; minimum
    )

    IN  NS  ns1.example.com.
    IN  NS  ns2.example.com.

ns1 IN  A   10.0.1.1
ns2 IN  A   10.0.1.2

@   IN  A   192.168.1.100  ; US datacenter
www IN  A   192.168.1.100  ; US datacenter
api IN  A   192.168.1.101  ; US API server
EOF

# EU zone file
cat > /etc/bind/zones/db.example.com.eu <<'EOF'
$TTL 300
@   IN  SOA ns1.example.com. admin.example.com. (
        2024010101  ; serial
        3600        ; refresh
        1800        ; retry
        604800      ; expire
        300         ; minimum
    )

    IN  NS  ns1.example.com.
    IN  NS  ns2.example.com.

ns1 IN  A   10.0.2.1
ns2 IN  A   10.0.2.2

@   IN  A   192.168.2.100  ; EU datacenter
www IN  A   192.168.2.100  ; EU datacenter
api IN  A   192.168.2.101  ; EU API server
EOF

Validar configuración de BIND:

sudo named-checkconf /etc/bind/named.conf
sudo named-checkzone example.com /etc/bind/zones/db.example.com

Reiniciar BIND:

sudo systemctl restart bind9
sudo systemctl status bind9

Probar enrutamiento geográfico:

# Test from different regions
nslookup example.com 127.0.0.1

# Test with specific client IP simulation (using dig)
dig @127.0.0.1 +subnet=1.2.3.4/32 example.com

Enrutamiento geográfico con PowerDNS

Instalar PowerDNS con backend GeoIP:

sudo apt install pdns-server pdns-backend-mysql pdns-tools

# Install GeoIP library
sudo apt install libgeoip1 geoip-bin

Configurar PowerDNS con scripting Lua para GeoIP:

cat > /etc/powerdns/pdns.lua <<'EOF'
-- GeoIP-based routing script
function preresolve(dq)
    if dq.qname:equal("example.com.") then
        local geoip = newGeoIP()
        local client_country = geoip:country(dq.remoteaddr:getaddr())
        
        if client_country == "US" then
            if dq.qtype == pdns.A then
                dq:addAnswer(pdns.A, "192.168.1.100")
                return true
            end
        elseif client_country == "GB" or client_country == "DE" or client_country == "FR" then
            if dq.qtype == pdns.A then
                dq:addAnswer(pdns.A, "192.168.2.100")
                return true
            end
        else
            if dq.qtype == pdns.A then
                dq:addAnswer(pdns.A, "192.168.3.100")  -- Default
                return true
            end
        end
    end
    return false
end
EOF

Habilitar backend Lua en configuración de PowerDNS:

cat >> /etc/powerdns/pdns.conf <<'EOF'
# Enable Lua backend
launch=lua
lua-script=/etc/powerdns/pdns.lua

# GeoIP settings
geoip-database-files=/usr/share/GeoIP/GeoLite2-Country.mmdb
EOF

Reiniciar PowerDNS:

sudo systemctl restart pdns
sudo systemctl status pdns

Enrutamiento estilo AWS Route53

Implementar enrutamiento basado en latencia y geolocalización estilo Route53 con herramientas de código abierto:

Usando dnsdist (distribuidor de consultas DNS):

# Install dnsdist
sudo apt install dnsdist

cat > /etc/dnsdist/dnsdist.conf <<'EOF'
-- Define backend servers by region
newServer({address="192.168.1.100:53", name="us-primary"})
newServer({address="192.168.1.101:53", name="us-secondary"})
newServer({address="192.168.2.100:53", name="eu-primary"})
newServer({address="192.168.2.101:53", name="eu-secondary"})
newServer({address="192.168.3.100:53", name="asia-primary"})

-- Load balancing policies
setServFailLimitParams(3, 60)  -- Allow 3 failures in 60 seconds

-- Configure latency-based routing
function latency_routing()
    -- Route based on server latency (dnsdist tracks this)
    return true
end

-- Default pool configuration
setPoolPolicy("roundrobin")

-- Add rules for geographic routing using client IP
addAction(AndRule({
    NetmaskGroupRule("1.0.0.0/8"),   -- US address space
    QTypeRule("A")
}), PoolAction("us-pool"))

addAction(AndRule({
    NetmaskGroupRule("2.0.0.0/8"),   -- EU address space
    QTypeRule("A")
}), PoolAction("eu-pool"))

addAction(AndRule({
    NetmaskGroupRule("3.0.0.0/8"),   -- Asia address space
    QTypeRule("A")
}), PoolAction("asia-pool"))

-- Bind to listen port
setLocal("0.0.0.0:53")

-- Enable stats
setConsoleInputKey("setKey(\"YOUR_SECRET_KEY\")")
controlSocket("127.0.0.1:5199")
EOF

Enrutamiento basado en latencia

Implementar enrutamiento basado en latencia con verificación de salud:

cat > /etc/powerdns/latency-routing.lua <<'EOF'
-- Latency-based routing

local latency_map = {
    ["192.168.1.100"] = 10,   -- US primary: 10ms
    ["192.168.1.101"] = 15,   -- US secondary: 15ms
    ["192.168.2.100"] = 50,   -- EU primary: 50ms
    ["192.168.2.101"] = 55,   -- EU secondary: 55ms
    ["192.168.3.100"] = 80,   -- Asia primary: 80ms
}

function selectLatencyServer(servers)
    local best_server = nil
    local min_latency = 999999
    
    for server, latency in pairs(latency_map) do
        if latency < min_latency then
            min_latency = latency
            best_server = server
        end
    end
    
    return best_server
end

function preresolve(dq)
    if dq.qname:equal("example.com.") and dq.qtype == pdns.A then
        local best_server = selectLatencyServer(latency_map)
        dq:addAnswer(pdns.A, best_server)
        return true
    end
    return false
end
EOF

Failover y verificación de salud

Implementar failover DNS con verificación de salud:

cat > /etc/powerdns/failover-routing.lua <<'EOF'
-- DNS failover with health checking

local server_status = {
    ["192.168.1.100"] = true,   -- Primary US
    ["192.168.1.101"] = true,   -- Secondary US
    ["192.168.2.100"] = true,   -- Primary EU
    ["192.168.3.100"] = true,   -- Primary Asia
}

function checkServerHealth(server)
    -- In real implementation, query HTTP health endpoint
    -- For now, use status map
    return server_status[server] or false
end

function getHealthyServers(region)
    local healthy = {}
    
    if region == "us" then
        if checkServerHealth("192.168.1.100") then
            table.insert(healthy, "192.168.1.100")
        elseif checkServerHealth("192.168.1.101") then
            table.insert(healthy, "192.168.1.101")
        end
    elseif region == "eu" then
        if checkServerHealth("192.168.2.100") then
            table.insert(healthy, "192.168.2.100")
        end
    elseif region == "asia" then
        if checkServerHealth("192.168.3.100") then
            table.insert(healthy, "192.168.3.100")
        end
    end
    
    return healthy
end

function preresolve(dq)
    if dq.qname:equal("example.com.") and dq.qtype == pdns.A then
        local region = determineRegion(dq.remoteaddr)
        local servers = getHealthyServers(region)
        
        if #servers > 0 then
            dq:addAnswer(pdns.A, servers[1])
            return true
        else
            -- Failover to global server
            dq:addAnswer(pdns.A, "192.168.3.100")
            return true
        end
    end
    return false
end
EOF

Prueba del enrutamiento geográfico

Probar enrutamiento DNS desde diferentes ubicaciones:

# Test from local (simulated US)
dig @127.0.0.1 example.com
# Expected: 192.168.1.100

# Test with geolocation simulation
# Use VPN or proxy to simulate different locations
curl -s "https://api.ipify.org?format=json"

# Test with specific client subnet
dig +subnet=1.2.3.4/32 @ns1.example.com example.com

# Test with DNS performance tools
dnsbench -h ns1.example.com -q example.com -c 100

Probar comportamiento de failover:

# Simulate server down
sudo iptables -A OUTPUT -d 192.168.1.100 -j DROP

# Test DNS response (should failover)
nslookup example.com ns1.example.com

# Remove rule
sudo iptables -D OUTPUT -d 192.168.1.100 -j DROP

Monitoreo y registro

Habilitar registro de consultas DNS:

# BIND logging configuration
cat >> /etc/bind/named.conf <<'EOF'
logging {
    channel query_log {
        file "/var/log/bind/query.log" versions 3 size 100M;
        print-time yes;
        print-severity yes;
        print-category yes;
    };
    
    category queries {
        query_log;
    };
};
EOF

Monitorear consultas DNS:

# Real-time query monitoring
tail -f /var/log/bind/query.log

# Count queries by client
grep -o "client.*#" /var/log/bind/query.log | sort | uniq -c | sort -rn

# Count queries by domain
grep "example.com" /var/log/query.log | awk '{print $NF}' | sort | uniq -c

Monitorear salud del servidor:

# Create health check script
cat > /usr/local/bin/dns-health-check.sh <<'EOF'
#!/bin/bash
for server in 192.168.1.100 192.168.2.100 192.168.3.100; do
    response=$(curl -s -m 5 http://$server/health)
    if [ $? -eq 0 ]; then
        echo "$server: UP"
    else
        echo "$server: DOWN"
    fi
done
EOF

chmod +x /usr/local/bin/dns-health-check.sh
/usr/local/bin/dns-health-check.sh

Solución de problemas

Verificar resolución DNS:

# Test specific nameserver
dig @ns1.example.com example.com

# Trace DNS resolution
dig +trace example.com

# Check DNS propagation
nslookup example.com 8.8.8.8
nslookup example.com 1.1.1.1
nslookup example.com ns1.example.com

Verificar base de datos GeoIP:

# Test GeoIP lookup
geoiplookup -f /usr/share/GeoIP/GeoLite2-Country.mmdb 8.8.8.8

# Update GeoIP database
wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
tar xzf GeoLite2-Country.tar.gz

Depurar decisiones de enrutamiento DNS:

# Enable query logging
rndc querylog

# Monitor logs
tail -f /var/log/bind/query.log

# Check BIND configuration
named-checkconf -p /etc/bind/named.conf

# Test recursive lookup
dig +recurse @127.0.0.1 example.com
dig +norecurse @127.0.0.1 example.com

Verificar TTL y caché:

# Check TTL on DNS response
dig example.com | grep -A 5 "ANSWER SECTION"

# Clear DNS cache (if using systemd-resolved)
sudo systemctl restart systemd-resolved

# Check cache with dig
dig example.com +nocmd +noall +answer

Conclusión

El equilibrio de carga geográfico con DNS distribuye el tráfico de manera inteligente entre la infraestructura global, mejorando la experiencia del usuario y la resiliencia del servicio. BIND con GeoIP, PowerDNS con scripting Lua y herramientas de código abierto como dnsdist proporcionan capacidades flexibles de enrutamiento geográfico. Combinadas con verificación de salud, mecanismos de failover y monitoreo cuidadoso, el enrutamiento DNS geográfico asegura servicios de baja latencia y altamente disponibles a nivel mundial.