As emocionantes aventuras de um sysadmin linux na procura pelo uptime perfeito!

Anunciando o HoardD, ferramenta para envio de métricas para o Graphite

Posted: fevereiro 10th, 2012 | Author: coredump | Filed under: Linux e Open Source, Programação

HoardD é um projeto usando Node.js para enviar métricas de servidores para o Graphite. Você pode ler o anúncio no meu blog em inglês: http://coredump.io/blog/2012/02/10/announcing-hoardd/

intel!

No Comments »

FLISOL-DF 2011: Escalabilidade e Alta Disponibilidade

Posted: abril 13th, 2011 | Author: coredump | Filed under: Linux e Open Source | Tags: , ,

Essa é a palestra sobre Escalabilidade e Disponibilidade que apresentei no FLISOL-DF 2011.

Teve até um número de pessoas legal para o horário, agradecimentos à coordenação que conseguiu me encaixar com tamanha rapidez na grade!

Eu vou ter de mexer em algumas coisas para diminuir tamanho e alguns assuntos, mas no mais, foi bem legal.

intel

No Comments »

Aprendendo a amar o histograma do varnish (varnishhist)

Posted: março 13th, 2011 | Author: coredump | Filed under: Linux e Open Source | Tags: , ,

Post rápido sobre uma ferramenta com má fama :)


Screenshot do varnishhistO comando varnishhist mostra um histograma dos requests sendo servidos pelo cache, desde o momento do varnish pegar o request do kernel até entregar a resposta ao kernel novamente.

É uma ferramenta interessante para ver como está a saúde de seu cache, mas meio chatinha de se entender de cara. Além disso existem opções na linha de comando para adicionar e remover informações. Por exemplo, pode-se usar o -b para checar apenas o status dos requests aos backends, e -c para checar apenas os requests dos clientes.

A grande questão que normalmente faz os iniciantes coçarem a cabeça é exatamente como interpretar o histograma. Não tema, é bem simples depois que se entende:

  • No topo esquerdo são mostradas a escala do gráfico e o número de requests sendo tratados no histograma. No caso do exemplo acima a escala é de 1:15, ou seja, cada símbolo | ou # representa 15 requests, e um total de 2000 requests já passaram desde que o programa iniciou.
  • Os símbolos | e # querem dizer respectivamente um cache hit e um cache miss. Trocando em miúdos: o request foi servido a partir do cache do varnish ou teve de ser buscado em algum backend.
  • A escala do eixo X é logarítmica, e da esquerda para a direita mostra o tempo que cada request demorou do início ao fim, em segundos. Então 1e0 quer dizer 1 segundo de demora, enquanto 1e-3 quer dizer 0.001 segundos (1 milisegundo).

Então, quanto mais para a esquerda estiverem os requests, mais rápido eles estão sendo servidos. Um grande número de # significa que seu cache não está fazendo o que devia (cachear!). O histograma bom é aquele parecido com o do exemplo: a maioria dos requests está no canto esquerdo do gráfico (rápidos) e são hits. É normal que hits sejam rápidos e misses sejam lentos, mas dependendo da velocidade dos backends você pode acabar com muitos misses no lado esquerdo do histograma.

intel!

No Comments »

Cluster corosync/pacemaker para balanceamento web (nginx, varnish ou apache) (or how to replace wackamole with heartbeat!)

Posted: dezembro 23rd, 2010 | Author: coredump | Filed under: Linux e Open Source | Tags: , , , , , , , , ,

Objetivo:

Construir um cluster com N máquinas rodando nginxvarnish para fornecer balanceamento de carga e cacheamento de aplicações e sites web, de forma a garantir disponibilidade e escalabilidade. O cluster será formado por N máquinas, todas ativas ao mesmo tempo em um pool de IPs que será utilizado num DNS round-robin ou balanceador via hardware para os endereços das aplicações internas. Caso um número N-1 de máquinas do cluster se torne indisponível, os IPs das máquinas indisponíveis devem ser realocados para evitar a perda de serviços.

Qual é o objetivo real aqui: servir conteúdo estático ou aplicações dinâmicas que possam ser cacheadas e balanceadas (ou seja, partes que não sejam dinâmicas ou que sejam geradas dinâmicamente mas não personalizadas o bastante para serem individuais para um determinado usuário). Usando o DNS round-robin se garante que cada IP vai receber mais ou  menos o mesmo número de requisição, não é exato mas é razoável o bastante. O problema de DNS RR é que caso um dos servidores caia aquele IP fica inacessível, e é aí que entra o cluster como esse que estou descrevendo. Caso um dos nós fique indisponível o IP dele é associado a outro nó, configurado de forma idêntica, que vai cuidar das requisições dele. Essa configuração pode ser aplicada inclusive para outros serviços que seguem uma mesma teoria.

Do ponto de vista de escalabilidade, você pode se precaver contra picos de acesso mantendo uma grande pool de IPs associados com um determinado nome, vamos supor, 12 IPs, e apenas 2 máquinas no cluster. Esses 12 endereços estarão distribuidos entre os nós de forma uniforme. No caso de um pico, basta adicionar máquinas no cluster e os endereços serão redistribuidos, sem necessidade de mudanças externas. As possibilidades são muitas.

Para isso funcionar direito, todas as máquinas do cluster tem de ter a mesma configuração e acessar os mesmos recursos. Isso pode ser feito via NFS, shared storage via NAS ou SAN, algum gerenciador de configuração como Chef ou Puppet, não importa. O que importa é que as configurações não saiam de sincronia.

Lembre-se também que conteúdo personalizado para usuários (carrinhos de compras, sites de administração de aplicações, áreas de postagens de comentários) tem de passar por servidores fora dessa estrutura ou ter alguma forma de persistência de sessão, mas isso é um problema comum a qualquer tipo de balanceamento de carga.

Arquitetura de cluster:

corosync faz parte da nova geração de ferramentas da alta disponibilidade para Linux. Podemos chamar isso tudo de heartbeat 3, mas na verdade é mais complexo que isso:  São vários projetos funcionando em uníssono para prover as funcionalidades antes disponibilizadas pelo heartbeat, e o heartbeat é agora uma das opções disponíveis para desempenhar o mesmo serviço que o corosync desempenha (comunicação entre os nós). O pacemaker por outro lado já é outra parte do sistema que cuida de gerenciar os serviços rodando, monitoramento e catalogação dos mesmos. Como eu disse, complexo. A complexidade aumentou mas as possibilidades também: os clusters não estão limitados mais a dois nós e podem ser configurados como ativos-passivos, ativos-ativos, ativos+n-ativos e quase todas as outras configurações imaginável.

Nginx + Varnish:

Faz um tempo que escolhi usar nginx+varnish na minha estrutura de balanceamento. O nginx é colocado na frente do varnish para prover algumas funcionalidades não presentes ou que seriam complexas ou custosas de se fazer no varnish, como reescrita de algumas URLS, compressão de conteúdo em gzip, adição de alguns headers de expiração (Expires:). Além disso, o nginx é utilizado para servir conteúdo multimídia como imagens, áudio e vídeo diretamente do disco sem passar pelo cache, aumentando o throughput e liberando o varnish para servir apenas conteúdo de menor tamanho. O nginx também possui um módulo especializado para servir arquivos de vídeo flv diretamente do disco que ajuda na nossa infraestrutura.

Execução:

Inicialmente eu tinha pensado em usar o wackamole, que faz redistribuição de IPs como eu queria, mas o projeto está parado a 2 anos e meio e aparentemente anda meio brigado com as interfaces no Linux 2.6. Depois de tentar resolver durante alguns dias um bug bizarro resolvi partir para a solução corosync. A instalação e configuração do Nginx e do Varnish fogem um pouco do objetivo desse post, existem vários outros na internet mostrando como montar um balanceador de carga com o nginx na frente do varnish (que eu prefiro) ou o varnish na frente do nginx (que eu acho uma solução menos escalável). A configuração inicial foi feita baseada no guia “Cluster from scratch on Fedora 11” disponível no site do pacemaker.

A primeira coisa a se fazer é garantir que os nós do cluster se encontrem corretamente na rede. É importante que os nomes estejam resolvendo corretamente noDNS e também que o arquivo /etc/hosts aponte corretamente para os ips. Certifique-se que o /etc/hosts inclua os shortnames dos servidores, o comando ping nomedoservidor (sem sufixo dns) tem de responder pelo ip correto. Além disso, uname -n deve retornar o nome do servidor também sem sufixo dns (ou seja, sem .algo), caso isso não seja o caso edite o arquivo /etc/hostname e execute:

# hostname $(cat /etc/hostname)

Os seguintes pacotes foram instalados (usando o repositório lenny-backports):

# aptitude install corosync pacemaker

E o aptitude vai se encarregar de instalar o resto das dependências, incluindo cluster-gluecluster-agents. Com os nós do cluster se falando e o software instalado, é hora de configurar o corosync. O corosync usa um endereço multicast para se comunicar entre os nós, favor prestar atenção com possíveis conflitos de porta/ips já existentes na rede. As únicas configurações alteradas do arquivo /etc/corosync/corosync.conf padrão do Debian foram: bindnetaddruse_logd,use_mgmtdconsensus.

Esta configuração tem de ser copiada para todos os nós do cluster. Depois de feito isso, inicie o corosync no primeiro nó, cheque o syslog por algum erro mais gritante e depois inicie os outros nós do cluster caso esteja tudo bem. Existem duas properties chatas que tem de ser mudadas de cara. Toda a configuração do cluster pode ser feita através de um imenso XML (ugh) ou através do comando crm que manipula o XML (legal):

# crm configure property stonith-enabled=false
# crm configure property no-quorum-policy=ignore

STONITH é o acrônimo para Shoot The Other Node In The Head, um serviço que não vamos usar nessa configuração mas é usado para serviços que tem de garantir que uma segunda cópia não rode ao mesmo tempo e para isso usam técnicas para desligar, bloquear ou ‘atirar na cabeça do outro nó’. O Debian tem essaproperty habilitada por padrão e o cluster não sobe sem o STONITH estar configurado corretamente ou a property estar desligada. O quórum já é uma propriedade interessante: ela garante que o cluster só seja considerado ‘consistente’ se tiver mais da metade dos nós ativos (isso é obviamente impossível em um cluster com 2 máquinas em que uma falha…), neste caso desabilitamos o quórum porque não importa o número de máquinas inativas, o cluster sempre vai estar ativo.

Para checar o funcionamento, use o comando crm_mon ou crm status (os dois são a mesma coisa, mas o crm_mon tem um loop que fica mostrando as informações em tempo real enquanto o crm status é one shot). A saída tem de ser algo assim:

# crm status
============
Last updated: Thu Dec 23 11:20:23 2010
Stack: openais
Current DC: node01 - partition with quorum
Version: 1.0.9-74392a28b7f31d7ddc86689598bd23114f58978b
3 Nodes configured, 3 expected votes
0 Resources configured.
============

Online: [ node01 node02 node03 ]

Depois das máquinas adicionadas e online no cluster, basta adicionar os resources que serão compartilhados, neste caso os 3 endereços IP. Usando o crm novamente (linhas quebradas para caber aqui, mas pode digitar tudo em uma linha só):

# primitive vip-1 ocf:heartbeat:IPaddr2 params ip="X.X.X.X" \ 
cidr_netmask="XX" op monitor interval="30s"

Repita isso mais duas vezes mudando vip-1 para vip-2vip-3 respectivamente para cada IP. Lembre-se que o que está sendo adicionado aqui são os IPs virtuais, não os IPs dos nós. Os parâmetros são auto-explicativos: cidr_netmask é a máscara de rede em formato CIDR do endereço, op monitor interval=“30s”vai monitorar o serviço para checar se ele está funcionando a cada 30 segundos (no caso, monitorar o IP). ocf:heartbeat:IPaddr2 é uma definição de Resource Agent, existem vários outros para vários outros serviços.

Com isso os IPs vão cada um para um nó do cluster. Isso acontece com outros serviços também. Nas palavras do project manager do corosync/pacemaker:

<beekhof> it tries to spread them out
<beekhof> regardless of what type they are
<beekhof> unless you use location constraints to tell pacemaker which ones they prefer

Isso também garante que caso mais VIPs sejam adicionados eles sejam balanceados o melhor possível entre os nós do cluster.

Checando com o crm status:

# crm status
============
Last updated: Thu Dec 23 11:43:39 2010
Stack: openais
Current DC: node01 - partition with quorum
Version: 1.0.9-74392a28b7f31d7ddc86689598bd23114f58978b
3 Nodes configured, 3 expected votes
3 Resources configured.
============

Online: [ node01 node02 node03 ]

 vip-1  (ocf::heartbeat:IPaddr2):       Started node01
 vip-2  (ocf::heartbeat:IPaddr2):       Started node02
 vip-3  (ocf::heartbeat:IPaddr2):       Started node03

A partir de agora caso um dos nós do cluster se torne indisponível o VIP rodando nele vai automaticamente ser movido para algum outro nó do cluster.

Os comandos crm configure showcrm configure show xml são interessantes para ver como as coisas são feitas.

intel.

No Comments »

MongoDB: sharding e mapReduce()

Posted: novembro 14th, 2010 | Author: coredump | Filed under: Linux e Open Source, Programação | Tags: , , ,

Uma das coisas interessantes que me atraiu a testar o MongoDB foram as capacidades de sharding e replicação do mesmo. Sharding é uma técnica muito usada atualmente para lidar com escalabilidade de massas de dados, consiste basicamente em dividir os dados de uma aplicação entre vários bancos: por exemplo, numa aplicação com 1000 usuários o sharding faria que os usuários com nomes de A a J ficassem em um servidor, e de K a Z em outro servidor. Claro que tem mais coisas envolvida, recomendo a leitura do excelente artigo Sharding for startups que discute como particionar os dados e como fazer aplicações que suportam sharding.

O MongoDB é um banco de dados não SQL que tem ganhado espaço. Foursquare por exemplo usa o Mongo com uma massa de dados razoável (3 shards de 60Gb, de acordo com uma informação já um pouco desatualizada). Nas versões mais recentes, o MongoDB também ganhou suporte nativo a sharding e uma replicação mais robusta.

Bancos NoSQL como  Mongo tem grandes diferenças com os RDB existentes. A mais importante, SQL não existe, então algumas coisas como SELECT SUM() ou SELECT … GROUP BY que se está acostumado a usar tem de ser implementados de outra forma. As vezes é melhor implementar todo esse tratamento na aplicação, mas no caso do MongoDB ele possui uma implementação de Map/Reduce nativa, exatamente para se fazer esse tipo de tratamento de dados. Um detalhe interessante na performance deste sistema é que no caso de bancos com shards o trabalho é dividido entre os mesmos.

Para testar o ganho de performance e se a divisão era feita corretamente, fiz o seguinte teste: habilitei sharding em uma collection com dados de logs com 19 milhões de registros, cada registro com tamanho entre 150 e 220 bytes. Usei as seguintes funções para o Map/Reduce:

Função de mapeamento (map):

function m() {
	emit(this.c, {duration: this.d,
			size: this.s,
			conn: 1});
}

Função de redução (reduce):

function r(key, val) {
	var total_duration = 0;
	var total_size = 0;
	var total_conn = 0;
	for (var i = 0; i < val.length; i++) {
		total_duration += val[i].duration;
		total_size += val[i].size;
		total_conn += 1;
	}
	return {duration: total_duration,
			size: total_size,
			total_conn : total_conn };
}

O balanceamento dos dados entre os dois shards que eu criei demorou quase três horas, nesse tempo o banco continua disponível exceto por updates em dados que estão no processo de serem movidos para outro shard. Depois de terminado esse processo eu tinha dez milhões de registros no servidor original e os outros nove milhões e qualquer coisa no segundo shard.

O processo de Map/Reduce com apenas um banco demorou 15 minutos para completar, agindo sobre todos os registros. Depois do sharding, esse tempo caiu pela metade indo para 7 minutos. Se a melhora for sempre nesta proporção a adição de shards implica diretamente na divisão do tempo do processamento pelo número de shards envolvidos. De acordo com a equipe de desenvolvimento do MongoDB eles estão trabalhando agora para melhorar o paralelismo da operação de Map/Reduce mas aparentemente isso depende da engine que eles usam para interpretar o javascript das funções.

O sharding do MongoDB é bem implementado, mas tem de se ter cuidado na hora de escolher a chave pela qual ele vai fazer a divisão dos dados. Uma chave mal escolhida pode fazer com que os shards fiquem mal balanceados e acabe com todo o ganho de performance que teria sido conseguido. Um exemplo seria se no meu teste eu tivesse acabado com um milhão de records em um dos shards, e os outros dezoito no outro: os resultados estariam disponíveis no shard com menos data muito antes do outro terminar.

intel

No Comments »