Como implementar um traffic manager com funcionalidade de failover baseado no Amazon Route53

Muitas vezes temos a necessidade de distribuir o trafego de uma aplicação web entre vários servidores, e por diversos motivos nem sempre é possível fazer uso de balanceadores de tráfego (como o F5 big ip por exemplo), seja por que você não tem acesso a um equipamento destes devido ao seu alto custo, seja porque seus servidores estão geograficamente distribuídos e não seria prudente da nossa parte direcionar todo o trafego para um único site para depois reencaminhá-lo para os demais sites.

Num cenário de servidores geograficamente distribuídos, a melhor (e a mais barata) forma de se implementar um Global Service Load Balance continua sendo o bom e velho Serviço de DNS.

Porém para que possamos usar com segurança o DNS Round Robin para função de distribuição de tráfego é necessário contornar uma série de limitações inerentes deste serviço.

A limitação mais critica é que se um determinado servidor deixar de responder por qualquer motivo, um % dos seus usuários não irá conseguir acessar os seus serviços, de forma que para minimizar o impacto para o seu usuário em caso de indisponibilidade de um dos servidores do pool, você precisará encontrar uma forma de garantir que se um determinado servidor apresentar problemas ele será removido rapidamente do pool de IPs que está sendo resolvido pelo DNS, e a unica forma de fazer isso de forma eficiente é automatizando o processo.

Existem diversas empresas (como por exemplo a Dyn.com) que comercializam serviços de DNS que possuem esta funcionalidade de “Traffic Manager”, infelizmente estas empresas cobram seus serviços pelo numero de queries por segundo (qps) que seu domínio recebe, e isso torna o uso da funcionalidade de “Traffic Manager” extremamente custoso pois a eficiência da funcionalidade de failover é inversamente proporcional ao TTL adotado para os registros DNS que ele está gerenciando, ou seja, quanto menor o TTL maior será o seu QPS e maior será o seu custo.

Logo a unica forma de poder contar com essa automação e manter seu custo baixo é implementar a sua própria solução, afinal não é nenhuma engenharia de foguetes :)

É perfeitamente possível implementar uma solução destas utilizando o bind, porém neste caso teríamos que distribuir nossos servidores de DNS em diversas localidades geográficas e dependendo do volume de QPS que você irá receber o dimensionamento dos servidores pode ser problemático.

A minha sugestão para ter uma rede distribuída e redundante de servidores de DNS para suportar a sua plataforma de Traffic Manager é o serviço Route53 da Amazon AWS.

Para facilitar o uso do Route53 para esta finalidade eu disponibilizei no github um pequeno shell script que eu criei:

https://github.com/ebrandi/route53-failover

O script é bastante simples, mas faz o trabalho sujo ;)

Basicamente você vai ter um arquivo texto chamado ips.master que irá listar os ips dos seus servidores, um por linha, no formato Peso:Endereço_IP, se os seus servidores (e os seus links IPs) forem todos iguais entre si, o Peso deve ser sempre 1, caso vc tenha diferenças de recursos entre os seus sites, ajuste o peso de forma a refletir isso.

O script route53-failover.sh deve ser editado para personalizar as variáveis abaixo, que acredito serem auto explicativas:

  • Hostname: nome do servidor, por exemplo www
  • Domain: dominio completo do sevidor, por exemplo fug.com.br
  • ttl: ttl do registro de DNS
  • ZoneID: ID atribuido pelo Route 53 ao seu dominio quando você criou ele pelo painel de controle
  • AccesskeyID: ID de um usuário com permissão de editar seus registros de DNS
  • SecretAPIKey: Senha do usuário acima
  • fail_host: Ultima instância de failover, é um hostname FQDN e pode ser o hostname de uma instância ELB da Amazon AWS, por exemplo wwwservice–frontend-314203742.us-east-1.elb.amazonaws.com. (o ponto no final é obrigatorio)
  • script_path: diretório onde o script foi está instalado
  • test_file: arquivo do web server no qual o script deve buscar uma string para saber se o web server está normal, por exemplo  status
  • test_string: caracteres que devem existir no arquivo de teste colocada entre aspas, por exemplo “Error 200 OK”

Uma editado o script, basta colocá-lo para rodar no cron no intervá-lo desejado, se precisar rodar numa periodicidade inferior a 1 minuto, lembre-se que um script com “while true” e “sleep”será o seu melhor amigo ;)

O funcionamento do script é simples:

  • Lê o arquivo ips.master
  • Desmembra cada linha no componente peso e endereço IP
  • Conecta no IP do servidor
  • Solicita o test_file e procura pela test_string
  • Se a resposta do servidor contiver a test_string, o servidor é considerado Normal, e o IP dele será gravado no arquivo ips.tmp o numero de vezes definido como peso para este IP no ips.master
  • Se a resposta do servidor não contiver a test_string, o servidor é considerado inoperante, e o IP dele não será gravado no arquivo ips.tmp
  • Caso a busca pela test_string falhe para todos os IPs listados no arquivo ips.master, será gerado um arquivo ips.tmp vazio.
  • Após a sequência de teste dos servidores, o script verifica se o arquivo ips.tmp está vazio ou se possui conteúdo, e se o arquivo gerado na execução atual é igual ou não ao gerado na ultima execução (ips.tmp.old).
  • Sempre que os arquivos forem iguais, o script encerra sem proceder com nenhuma atualização, pois não houve diferença no resultado dos testes, ou seja, todos os IPs que estavam UP continuam UP e todos os que estavam DOWN continuam DOWN.
  • Se os arquivos forem diferentes significa que houve alteração no status dos servidores, e o script irá proceder com a atualização do DNS. Caso o arquivo esteja vazio, o processo de atualização é o mesmo para quando ele possui conteúdo  com a diferença que no caso do arquivo vazio o script irá gerar um registro DNS to tipo CNAME, e no caso do arquivo conter IPs válidos ele irá criar registros DNS do tipo A.

Resumidamente é isso, ou seja, sempre que vc instalar um novo servidor insira o IP dele no ips.master, a partir dai o script se encarregará de colocá-lo no pool do DNS  Round Robin quando o web server estiver funcionando e de tirá-lo caso ele pare de funcionar.  Se tudo der errado e todos os seus servidores pararem, o script irá apontar o seu site para um CNAME que pode ser o ELB do seu site backup na Amazon AWS, ou mesmo para um outro servidor que contenha uma página estática de manutenção.

E o melhor,  tudo de forma automática! ;)

É óbvio que o meu script não está otimizado e muita coisa pode ser melhorada, mas acho que o importante é que ele faz o serviço :)

Uma melhoria necessária é prever no script um tratamento para os casos em que a API do Route53 estiver funcionando, pela logica atual o processo de atualização vai falhar, e na próxima execução como o ips.tmp e ips.tmp.old continuarão iguais, ele não irá fazer uma nova submissão.

Outra melhoria é incluir um lock no processo, de forma que vc não rode o script uma segunda vez caso ele ainda esteja em execução.

Eu planejo fazer estes ajustes quando sobrar algum tempo livre, pull requests são bem vindos ;)

Planejo depois com calma fazer uma versão em php para que vire um módulo para o Pfsense, mas não tenho previsão de quando vou ter tempo de fazer isso.

Espero que o script seja útil para vocês, no mínimo para aprender como não se fazer um shell script ;)

[]‘s Edson

Ps. Se forem usar este script em produção eu sugiro que façam seus próprios testes antes, e usem por conta e risco ;)

3 Responses to “Como implementar um traffic manager com funcionalidade de failover baseado no Amazon Route53”

  1. Legal a solução “faça você mesmo”. Alguns comentários:

    1) na inicialização das variáveis, eu permitiria que elas fossem definidas externamente, de forma que o script pudesse ser usado para múltiplos domínios sem copiá-lo múltiplas vezes. Por exemplo:

    Hostname=${Hostname:-”algum-default”}
    Domain=${Domain:-”outro_defalt”}
    TTL=${TTL:-15}

    Isto também facilita pra testar diferentes valores de forma mais rápida. E também permite atualizar o script sem ter que editar os crontabs.

    2) ao enviar a requisição de atualização, eu testaria o valor de retorno ($?) do wget, pra ter certeza que foi possível conectar e tratar os erros de forma mais precisa.

    3) wget –no-check-certificate: deselegante :)
    Não é um grande problema de segurança visto que mesmo que a conexão fosse interceptada, a autenticação HMAC não dá muitas possibilidades para um ataque. Mas é tão fácil de evitar isto :)

    Desculpa por não enviar um pull request, mas como eu não poderia testar as alterações agora, preferi mandar por texto mesmo.

    Sds,
    vmm.

  2. Ola Vinicius, obrigado pelas sugestões de melhoria, quando parar para mexer nele eu ajusto isso também :)

    Como eu mesmo disse no post, o script não está elegante, nem otimizado em relação as chamadas de sistema que faz.

    Como dizem, qualquer “meio script” que funcione é melhor do que um script perfeito que nunca sai da teoria hehehe.

    Por isso publiquei ele no formato atual, o objetivo era apenas dar o caminho das pedras sobre o que pode ser feito.

    Edson

  3. Olá Brandi. Certo. Pessoalmente eu nunca tinha avaliado o route 53. Obrigado por compartilhar o texto.