Banco de Dados

7 jan, 2011

Usando MapReduce e balanceamento de carga em nuvens

Publicidade

A computação em nuvens foi projetada para fornecer recursos ou
serviços on demand pela Internet, geralmente na escala e com o nível de
confiabilidade de um datacenter.

O MapReduce é um modelo de programação
criado para processar grandes volumes de dados paralelamente dividindo o
trabalho em um conjunto de tarefas independentes. Trata-se de um estilo
de programação paralela que é suportado por algumas nuvens do estilo de
capacidade on-demand como o Google BigTable, Hadoop e Sector.

Neste
artigo, é usado um algoritmo de balanceamento de carga que segue a
abordagem da técnica Randomized Hydrodynamic Load Balancing (tratada
mais detalhadamente nas seções a seguir).

A virtualização é usada para
reduzir o número real de servidores físicos e os custos e, mais
importante, a virtualização é usada para obter uma utilização eficiente
de CPU de uma máquina física.

Para aproveitar ao máximo este
artigo, é necessário ter uma ideia geral dos conceitos de computação em
nuvens, da técnica Randomized Hydrodynamic Load Balancing e do modelo de
programação Hadoop MapReduce.

Um conhecimento básico de programação
paralela será útil e qualquer conhecimento de programação em Java ou em
outras linguagens orientadas ao objeto será uma boa ferramenta de
suporte.

Para este artigo, o algoritmo do MapReduce foi implementado em um sistema usando:

  • Hadoop 0.20.1.
  • Eclipse IDE 3.0 ou superior (ou Rational Application Developer 7.1).
  • Ubuntu 8.2 ou superior.

Antes de mergulhar no algoritmo do MapReduce, iremos configurar os
conceitos básicos da arquitetura em nuvens, do balanceamento de carga,
do MapReduce e da programação paralela ? que sejam suficientes pelo
menos para este artigo.

Arquitetura em nuvens: os conceitos básicos

A Figura 1 mostra uma imagem detalhada de todo o sistema, plataformas,
software, e de como são usados para alcançar o objetivo definido neste
artigo.

Figura 1. Arquitetura em nuvens

É possível ver que o Ubuntu 9.04 e 8.2 são usados para os sistemas
operacionais; Hadoop 0.20.1, Eclipse 3.3.1 e Sun Java 6 para as
plataformas. A linguagem Java para programação; e HTML, JSP e XML como
as linguagens de script.

Esta arquitetura em nuvens tem nós
mestres e nós escravos. Nesta implementação, é mantido um servidor
principal que obtém solicitações do cliente e as identifica de acordo
com o tipo de solicitação. O nó mestre está presente no servidor
principal e, os nós escravos, no servidor secundário.

As
solicitações de procura são encaminhadas para no NameNode do Hadoop,
presente no servidor principal, como é possível ver na Figura 2. Em
seguida, o NameNode do Hadoop cuida da operação de procura e indexação
iniciando um grande número de processos de Mapeamento e Redução.

Uma vez
que a operação MapReduce para uma chave de pesquisa específica é
concluída, o NameNode retorna o valor de saída ao servidor que, por sua
vez, o retorna ao cliente.

Figura 2. Funções Map e Reduce realizam procura e indexação

Se a solicitação for para um software específico, as etapas de
autenticação são executadas com base no ID de arrendatário do cliente,
prazos de pagamento, elegibilidade para uso de um software específico e
período de lease para o software. Em seguida, o servidor atende a esta
solicitação e permite que o usuário obtenha uma combinação de software
selecionada.

O recurso de multiarrendamento de SaaS é fornecido
aqui, no qual uma única instância do software atende a diversos
arrendatários. Para cada solicitação específica de arrendatário, haverá
uma fina linha de isolamento gerada com base no ID do arrendatário.
Essas solicitações são atendidas por uma única instância.

Quando
uma solicitação do cliente específica de arrendatário desejar procurar
arquivos ou consumir qualquer outro software de vários arrendatários, as
ofertas usam o Hadoop na instância do sistema operacional fornecido
(PaaS).

Além disso, para armazenar seus dados — talvez um banco de
dados ou arquivos — em nuvens, o cliente terá que usar algum espaço de
memória do datacenter (IaaS). Todas essas movimentações são
transparentes para o usuário final.

Técnica Randomized Hydrodynamic Load Balancing: os conceitos básicos

O balanceamento de carga é usado para assegurar que nenhum dos
recursos existentes está inativo enquanto outros estão sendo utilizados.
Para balancear a distribuição de carga, é possível migrar a carga dos nós de origem (que têm superávit carga de trabalho) para nós de destino de cargas comparativamente mais leves.

Quando você aplica o balanceamento de carga durante o tempo de execução, ele é chamado de balanceamento de carga dinâmico ? isso pode ser feito de maneira direta ou iterativa, de acordo com a seleção do nó de execução:

  • Nos métodos iterativos, o nó de destino final é determinado por meio de diversas etapas de iteração.
  • Nos métodos diretos, o nó de destino final é selecionado em uma etapa.

Para este artigo, é usado o método Randomized Hydrodynamic Load
Balancing, um método híbrido que se beneficia de ambos os métodos,
direto e iterativo.

MapReduce: os conceitos básicos

Os programas do MapReduce são projetados para calcular grandes
volumes de dados de maneira paralela. Isso exige a divisão da carga de
trabalho em um grande número de máquinas. O Hadoop oferece uma maneira
sistemática de implementar este paradigma de programação.

O
cálculo usa um conjunto de pares de chave/valor de entrada e produz um
conjunto de pares de chave/valor de saída. O cálculo envolve duas
operações básicas: Map e Reduce.

A operação Map, escrita pelo
usuário, usa um par de entradas e produz um conjunto de pares de
chave/valor intermediário. A biblioteca do MapReduce agrupa todos os
valores intermediários associados à mesma Chave nº 1 intermediária e os
transmite à função Reduce.

A função Reduce, também escrita pelo
usuário, aceita uma Chave nº 1 intermediária e um conjunto de valores
para essa chave. A função mescla esses valores para formar um conjunto
de valores possivelmente menor. Geralmente, apenas um valor de saída de 0
ou 1 é produzido a cada chamada Reduce.

Os valores intermediários são
fornecidos à função Reduce do usuário por um iterador (um objeto que
permite que um programador cruze todos os elementos de um conjunto,
independentemente de sua implementação específica). Isso permite
identificar listas de valores que são grandes demais para a memória.

Considere
o exemplo do problema de Contagem de Palavras. Será contado o número de
ocorrências de cada palavra em um grande conjunto de documentos. As
funções Mapper e Reducer são conforme é mostrado na Listagem 1.

Listagem 1. Funções Map e Reduce em um problema de Contagem de Palavras

mapper (filename, file-contents):
for each word in file-contents:
emit (word, 1)

reducer (word, values):
sum = 0
for each value in values:
sum = sum + value
emit (word, sum)

A função Map emite cada palavra mais uma contagem associada de
ocorrências. A função Reduce soma todas as contagens emitidas para uma
palavra específica.

A funcionalidade básica, quando construída sobre
clusters, pode facilmente ser convertida em um sistema de processamento
paralelo de alta velocidade.

O cálculo em grandes volumes de
dados já foi feito anteriormente, geralmente em uma configuração
distribuída. O que torna o Hadoop exclusivo é seu modelo de programação
simplificado ? que permite ao usuário rapidamente escrever e testar
sistemas distribuídos – sua distribuição eficiente e automática de
dados e trabalho entre máquinas e, por sua vez, a utilização de
paralelismo subjacente dos núcleos de CPU.

Vamos tentar esclarecer um pouco mais. Conforme foi discutido anteriormente, em um cluster do Haddop, você tem os seguintes nós:

  • O NameNode (o mestre da nuvem).
  • Os DataNodes (ou os escravos).

Os
nós do cluster têm arquivos locais de entrada pré-carregados. Quando o
processo do MapReduce é iniciado, o NameNode usa o processo JobTracker para atribuir tarefas que foram executadas por DataNodes, por meio de processos TaskTracker.

Diversos processos Map serão executados em cada DataNode e os
resultados intermediários serão fornecidos ao processo combinador que
gera, por exemplo, a contagem de palavras em um arquivo de uma máquina
(como no caso de um problema de Contagem de palavras).

Os valores são
ordenados aleatoriamente e enviados para processos Reduce, que geram a
saída final para o problema em questão.

Como o balanceamento de carga é usado

O balanceamento de carga é útil na distribuição uniforme de carga
entre nós livres no caso de um nó estar carregado acima do nível limite.
Embora o balanceamento de carga não seja tão significativo na execução
de um algoritmo no MapReduce, ele se torna essencial na identificação de
arquivos grandes para processamento e quando o uso de recursos de
hardware é crítico.

Como destaque, o balanceamento aprimora a utilização
de hardware em situações de recursos críticos, com uma leve melhoria no
desempenho.

Foi implementado um módulo para balancear o uso de
espaço em disco em um cluster do Hadoop Distributed File System quando
alguns nós de dados tornarem-se cheios ou quando novos nós vazios
ingressarem no cluster.

O balanceador (ferramenta Class Balancer) foi
iniciado com um valor do limite; este parâmetro é uma fração entre 0 e
100 por cento com um valor padrão de 10 por cento. Isso define a meta de
balanceamento do cluster – quando menor o valor do limite, mais
balanceado será o cluster. Além disso, mais tempo demorará a execução do
balanceador.

Observação: O valor do limite pode ser tão pequeno que
impossibilita o balanceamento do estado do cluster, porque os
aplicativos podem estar gravando e excluindo arquivos simultaneamente.

Um
cluster é considerado balanceado se, para cada nó de dados, a diferença
da proporção entre o espaço usado no nó e a capacidade total do nó
(conhecida como utilização do nó) e da proporção entre o espaço usado no cluster e a capacidade total do cluster (utilização do cluster) não for superior ao valor do limite.

O
módulo move blocos dos nós de dados que estão sendo muito utilizados
para aqueles pouco utilizados, de maneira iterativa; em cada iteração,
um nó move ou recebe não mais que a fração limite da sua capacidade e
cada iteração é executada por não mais que 20 minutos.

Nesta implementação, os nós são classificados como de alta utilização, média utilização e pouca utilização.
Dependendo da classificação de utilização de cada nó, a carga era
transferida entre nós e o cluster era balanceado. O módulo funcionava
assim:

  • Primeiro, ele adquire detalhes do ambiente:
  1. Quando a carga aumenta em um DataNode até o nível do limite, é enviada uma solicitação ao NameNode.
  2.  O NameNode possuía informações sobre os níveis de carga dos vizinhos mais próximos ao DataNode específico.
  3. As cargas são comparadas pelo NameNode e, em seguida, os detalhes sobre os nós vizinhos mais livres são enviados ao DataNode específico.
  • Em seguida, os DataNodes começam o trabalho:

  1. Cada DataNode compara sua própria quantidade de carga com a soma da quantidade de carga dos seus vizinhos mais próximos.
  2. Se
    o nível de carga do DataNode for superior à soma da carga dos seus
    vizinhos, os nós de destino da carga (vizinhos diretos E outros nós)
    serão escolhidos aleatoriamente.
  3. Os pedidos de carregamento são, em seguida, enviados aos nós de destino.
  • Por último, a solicitação é recebida:
  1. Os buffers são mantidos em cada nó para os pedidos de carregamento recebidos.
  2. Uma interface de transmissão de mensagens (MPI) gerencia este buffer.
  3. Um encadeamento principal atenderá à fila em buffer e entregará as solicitações que receber.
  4. Os nós entram na fase de execução do balanceamento de carga.

Avaliando o desempenho

Foram fornecidos diferentes conjuntos de arquivos de entrada, cada um
de tamanho diferente, e eles executaram as tarefas do MapReduce em
clusters de um ou dois nós.

Os tempos de execução correspondentes foram
medidos e chegamos à conclusão de que executar o MapReduce em clusters
é, de longe, o método mais eficiente para um grande volume de arquivos
de entrada.

Os gráficos na Figura 3 ilustram nossos resultados de desempenho a partir da execução em vários nós.

Figura 3. O balanceamento de carga do MapReduce funciona de maneira mais eficiente em clusters

Concluindo

Nosso experimento com o Hadoop MapReduce e o balanceamento de carga leva a duas conclusões inevitáveis:

  • Em
    um ambiente de nuvens, a estrutura do MapReduce aumenta a eficiência do
    rendimento para grandes conjuntos de dados. Em contraste, tal melhoria
    do rendimento não seria necessariamente percebida em um sistema que não
    fosse um sistema em nuvens.
  • Quando um conjunto de dados é
    pequeno, o MapReduce e o balanceamento de carga não efetuam uma melhoria
    considerável no rendimento de um sistema em nuvens.

Por isso, considere a combinação do processamento paralelo de estilo
MapReduce e do balanceamento de carga ao planejar processar uma grande
quantidade de dados em seu sistema em nuvens.

Recursos

Aprender

Obter produtos e tecnologias

Discutir

  • O grupo Developer Cloud em My developerWorks é a comunidade para o Smart Business Development and Test on the IBM Cloud.

***

artigo publicado originalmente no developerWorks Brasil, por Kirpal A. Venkatesh, Kishorekumar e R. Revathy

Kirpal A. Venkatesh é engenheiro de
sistemas do Global Business Service na IBM, profissional em tecnologias
da Microsoft e adepto entusiasta e inovador da tecnologia de computação
em nuvens.

Kishorekumar
é arquiteto de TI do Global Business Service na IBM. Ele tem mais de 13
anos de experiência no desenvolvimento de softwares com um sólido foco
na integração de softwares na plataforma Rational. Um apaixonado
divulgador da tecnologia de computação em nuvens, Kishore é participante
frequente do developerWorks. É possível seguir suas atividades por meio
do seu perfil do MydW e do seu blog.

R. Revathy é estudante do último ano da Faculdade de Engenharia da Anna University em Guindy, Chennai, Índia.