Cluster de alta disponibilidade Linux com Raspberry Pi Deixe um comentário

Quem já foi a um estabelecimento comercial – supermercado, por exemplo – e presenciou uma interrupção momentânea no fornecimento de energia elétrica, presenciou provavelmente um exemplo de alta disponibilidade. Ou seja, a rápida restauração do serviço – fornecimento de energia elétrica – minimizando nesse caso o tempo no escuro. Esse é o conceito. Para se ter alta disponibilidade é preciso redundância – rede elétrica e gerador de energia – e um mecanismo capaz de detectar falhas no fornecimento da rede elétrica e ligar o gerador automaticamente – chave de transferência automática. Dito isso, a proposta desse post é substituindo a energia elétrica por uma página web, a rede elétrica e o gerador de energia por duas placas Raspberry Pi e o mecanismo de detecção de falhas por softwares – Corosync e Pacemaker – criar um conjunto de dois ou mais computadores, um cluster de alta disponibilidade Linux.

Com isso, minimizamos o tempo fora do ar da página web, pois parafraseando um ditado conhecido “quem tem dois tem um, que tem um não tem nenhum”, que poderia ser o slogan de alta disponibilidade e deveria ser a política de quem trabalha com administração de sistemas e infraestrutura de redes.

Material necessário

Para realizar este projeto, vamos precisar de:

Nos cartões SD é necessário estar gravado o sistema operacional Raspbian, de preferência o Lite. Qualquer dúvida de como isso deve ser feito, você pode consultar este post.

O que são Corosync e Pacemaker?

O Corosync é responsável pela associação, sincronismo e detecção de falhas do membros do cluster de alta disponibilidade Linux – também conhecidos como nodes ou nós, nesse post referidos como nodes. O Pacemaker é um gerenciador de recursos de cluster (cluster resource manager), um sistema que administra recursos – endereço IP e servidor web, por exemplo – disponibilizados pelo cluster.

Cluster de alta disponibilidade

Configurando Corosync e Pacemaker

Para começar precisamos de algum tipo de acesso ao terminal, seja remoto via ssh ou local via teclado. Tudo certo? Então vamos lá!

A convenção seguida é que [TODOS] indica um comando que precisa ser executado em todas as máquinas do cluster de alta disponibilidade Linux. [NODE1] e [NODE2] indica um comando que precisa ser executado individualmente na respectiva máquina.

É uma boa prática atribuir manualmente o endereço IP dos nodes, para isso vamos editar o arquivo. [TODOS]

vim /etc/dhcpcd.conf

E incluir o conteúdo abaixo no final (fique atento aos comentários). [TODOS]

interface eth0
# SUBSTITUA "192.168.0.1" PELO ENDEREÇO IP DO SEU RASPBERRY PI.
static ip_address=192.168.0.1/24
# SUBSTITUA "192.168.0.1" PELO ENDEREÇO IP DO SEU ROTEADOR.
static routers=192.168.0.1

Agora vamos instalar os pacotes, nosso cluster será baseado no Corosync e Pacemaker. [TODOS]

apt-get update
apt-get install pacemaker corosync crmsh

É uma boa prática fazer uma cópia do arquivo de configuração antes de editá-lo. [TODOS]

cp -p /etc/corosync/corosync.conf /etc/corosync/corosync.conf.default

Agora iremos limpar o conteúdo do arquivo. [TODOS]

echo "" > /etc/corosync/corosync.conf

Editá-lo. [TODOS]

vim /etc/corosync/corosync.conf

E incluir o conteúdo abaixo (fique atento aos comentários!). [TODOS]

totem {
    # Especifica a versão do arquivo de configuração.
    # Atualmente, a única versão válida para esta diretiva é 2.
    version: 2

    # Especifica o nome do cluster.
    cluster_name: pacemaker1

    # Quanto tempo antes de declarar um token perdido (ms).
    token: 3000

    # Quantos tokens são retransmitidos antes de formar uma nova configuração.
    token_retransmits_before_loss_const: 10

    # Limita nodeids a 31 bits (números inteiros com sinal positivo).
    clear_node_high_bit: yes

    # Usado para autenticação mútua dos nodes.
    # Necessário criar uma chave compartilhada com "corosync-keygen".
    # Habilitar crypto_cipher, requer também a habilitação de crypto_hash.
    crypto_cipher: aes256
    crypto_hash: sha1

    # Esta diretiva controla o mecanismo de transporte usado.
    # Unicast (udpu), que requer uma lista de membros na seção nodelist {}.
    transport: udpu

    # Define a interface para se comunicar.
    interface {
        # O ringnumber deve ser numerado consecutivamente, começando em 0.
        ringnumber: 0
        # Especifica o endereço onde o Corosync deve se vincular.
        # O bindnetaddr deve ser o endereço IP configurado no sistema ou o endereço de rede.
        # Por exemplo, se o endereço IP configurado no sistema for 192.168.0.1 com a máscara de
        # rede 255.255.255.0, você deve definir bindnetaddr como 192.168.0.1 ou 192.168.0.0.
        # Definindo bindnetaddr como endereço de rede poderemos usar esse mesmo arquivo
        # de configuração para todos os outros nós do cluster.
        # SUBSTITUA O ENDEREÇO DE REDE 192.168.0.0 DE ACORDO COM SUA REDE.
        bindnetaddr: 192.168.0.0
    }
}

logging {
    # Em caso de dúvida, deixe "off".
    fileline: off
    # Em caso de dúvida, deixe "no".
    to_stderr: no
    # Em caso de dúvida, deixe "no".
    to_logfile: no
    # Em caso de dúvida, deixe "yes".
    to_syslog: yes
    # Em caso de dúvida, deixe "daemon".
    syslog_facility: daemon
    # Em caso de dúvida, deixe "off".
    debug: off
    # Em caso de dúvida, deixe "on".
    timestamp: on
    logger_subsys {
        subsys: QUORUM
        debug: off
    }
}

quorum {
    # O serviço votequorum faz parte do projeto Corosync.
    # Esse serviço pode ser opcionalmente carregado nos nodes do cluster Corosync para evitar situações de split-brain.
           # Isso é feito atribuíndo um número de votos esperados e garantindo que somente com a maioria dos votos
    # as operações do cluster possam prosseguir.
    provider: corosync_votequorum
    # votequorum requer um valor de votos esperados para funcionar, isso pode ser fornecido de duas maneiras.
    # O número de votos esperados será calculado automaticamente quando a seção nodelist {} estiver presente em
    # corosync.conf ou quando expected_votes estiver presente na seção de quorum {}.
    # A falta de ambos desativará o voto. Se ambos estiverem presentes ao mesmo tempo, o valor expected_votes
    # substituirá o calculado a partir da lista de nodes.
    expected_votes: 2
           # O cluster de dois nodes (two node cluster) é um caso de uso que requer atenção especial.
    # Com um cluster de dois nodes, cada node com um voto, há dois votos no cluster.
    # Usando o cálculo da maioria simples (50% dos votos + 1) para calcular o quórum, o quórum seria 2.
           # Isso significa que os dois nodes sempre precisariam estar ativos para que o cluster operasse.
    # Ativando two_node: 1, o quorum é definido artificialmente como 1.
        two_node: 1
}

# Dentro da seção nodelist {}, é possível especificar informações sobre os nodes do cluster.
nodelist {
      node {
        # Especifica o endereço IP do node.
        # SUBSTITUA O ENDEREÇO IP 192.168.0.1 PELO ENDEREÇO IP DO PRIMEIRO NODE.
            ring0_addr: 192.168.0.1
      }
      node {
        # Especifica o endereço IP do node.
        # SUBSTITUA O ENDEREÇO IP 192.168.0.2 PELO ENDEREÇO IP DO SEGUNDO NODE.
            ring0_addr: 192.168.0.2
      }
}

Para garantir a autenticidade e a privacidade das mensagens trocadas entre os nodes do cluster, iremos gerar uma chave privada. [NODE1]

corosync-keygen

Se o resultado for algo parecido com.

Corosync Cluster Engine Authentication key generator.
Gathering 1024 bits for key from /dev/random.
Press keys on your keyboard to generate entropy.
Press keys on your keyboard to generate entropy (bits = 928).
Press keys on your keyboard to generate entropy (bits = 1000).
Writing corosync key to /etc/corosync/authkey.

Tudo certo, podemos seguir.

Precisamos enviar o arquivo contendo a chave para o outro node do cluster. [NODE1]

Atenção! Não esqueça de substituir o nome de usuário (pi) e o endereço IP (192.168.0.2), caso necessário.

rsync -avz /etc/corosync/authkey pi@192.168.0.2:/tmp/

Vamos mover o arquivo para o diretório correto. [NODE2]

mv /tmp/authkey /etc/corosync/

Corrigir as permissões. [NODE2]

chown root. /etc/corosync/authkey

E reiniciar os serviços. [TODOS]

systemctl restart corosync
systemctl restart pacemaker

Verificando a instalação do Corosync

Primeiro vamos verificar se a comunicação do cluster está funcionando. [TODOS]

corosync-cfgtool -s

Podemos ver que tudo parece normal, o nosso endereço IP listado como o id (não o endereço 127.0.0.1) e sem falhas para o status.

Printing ring status.
Local node ID 1217522751
RING ID 0
    id    = 192.168.0.1
    status    = ring 0 active with no faults

Em seguida, verificar as APIs de associação e o quórum. [NODE1] ou [NODE2]

corosync-cmapctl | grep members

Se o resultado for algo parecido com.

runtime.totem.pg.mrp.srp.members.1217522751.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.1217522751.ip (str) = r(0) ip(192.168.0.1)
runtime.totem.pg.mrp.srp.members.1217522751.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.1217522751.status (str) = joined
runtime.totem.pg.mrp.srp.members.1217522754.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.1217522754.ip (str) = r(0) ip(192.168.0.2)
runtime.totem.pg.mrp.srp.members.1217522754.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.1217522754.status (str) = joined

Tudo certo.

Verificando a instalação do Pacemaker

Agora que confirmamos que o Corosync está funcionando, podemos verificar o restante da pilha. [NODE1] ou [NODE2]

crm status

O resultado deve ser algo como.

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 09:33:22 2019
Last change: Mon Jul 29 09:31:03 2019 by hacluster via crmd on node2

2 nodes configured
0 resources configured

Online: [ node1 node2 ]

No resources

Antes de fazermos qualquer alteração, é uma boa ideia verificar a validade da configuração do cluster de alta disponibilidade Linux. [NODE1] ou [NODE2]

crm_verify -L -V

Como você pode ver, a ferramenta encontrou alguns erros.

error: unpack_resources:    Resource start-up disabled since no STONITH resources have been defined
   error: unpack_resources:    Either configure some or disable STONITH with the stonith-enabled option
   error: unpack_resources:    NOTE: Clusters with shared data need STONITH to ensure data integrity
Errors found during check: config not valid

A fim de garantir a segurança de seus dados, fencing (STONITH) é habilitado por padrão.
No entanto, ele também sabe quando nenhuma configuração STONITH foi fornecida e relata isso como um problema.
Desativaremos esse recurso por enquanto. [NODE1] ou [NODE2]

crm configure property stonith-enabled=false

Adicionando recurso – endereço IP

Independentemente de onde os serviços do cluster estão sendo executados, os usuários precisam de um endereço IP consistente para contatá-los. Aqui, vou escolher 192.168.0.10 como o endereço flutuante, dar-lhe o nome de FloatIP e dizer ao cluster para verificar se ele está em execução a cada 10 segundos.
Atenção! Não se esqueça de substituir o endereço IP (192.168.0.10) de acordo com sua rede. O endereço escolhido não deve estar em uso na rede. [NODE1] ou [NODE2]

crm configure primitive FloatIP ocf:heartbeat:IPaddr2 params ip=192.168.0.10 nic=eth0 cidr_netmask=24 op monitor interval=10s

Agora, vamos verificar se o recurso IP foi adicionado. [NODE1] ou [NODE2]

crm status

Como você pode ver, o endereço IP (FloatIP) foi iniciado no node1.

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 09:38:10 2019
Last change: Mon Jul 29 09:38:02 2019 by root via cibadmin on node1

2 nodes configured
1 resource configured

Online: [ node1 node2 ]

Full list of resources:

 FloatIP    (ocf::heartbeat:IPaddr2):    Started node1

Para verificar, no node onde o endereço IP foi iniciado, nesse exemplo node1.

ip a

Veremos então dois endereços IP: o endereço IP do node e o endereço IP flutuante.

inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.0.10/24 brd 192.168.0.255 scope global secondary eth0
       valid_lft forever preferred_lft forever

Adicionando recurso – servidor web

Agora que temos um cluster básico mas funcional, estamos prontos para adicionar um serviço real. Mais especificamente um servidor web, pois além de ser usado em muitos clusters é relativamente simples de configurar.

Nosso servidor web será baseado no Apache, vamos a instalação. [TODOS]

apt-get update 
apt-get install apache2

Os serviços devem ser gerenciados pelo cluster nunca pelo sistema operacional. Por isso, iremos parar e desativar o serviço do Apache na inicialização do sistema. [TODOS]

systemctl stop apache2 
systemctl disable apache2

Precisamos criar uma página para o Apache servir. [TODOS]

cat <<-END >/var/www/html/index.html
 <html>
 <body> Site Teste - $(hostname)</body>
 </html>
END

Neste ponto, o Apache está pronto para funcionar e tudo o que precisa ser feito é adicioná-lo ao cluster de alta disponibilidade Linux . Dando-lhe o nome de WebSite e dizendo ao cluster para verificar se ele está em execução a cada 60 segundos. [NODE1] ou [NODE2]

crm configure primitive WebSite ocf:heartbeat:apache params \
configfile=/etc/apache2/apache2.conf \
statusurl="http://localhost/server-status" \
op monitor interval=60s

Após alguns segundos, devemos ver o cluster iniciar o Apache. [NODE1] ou [NODE2]

crm status

O resultado deve ser algo como.

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 10:46:55 2019
Last change: Mon Jul 29 10:46:35 2019 by root via cibadmin on node1

2 nodes configured
2 resources configured

Online: [ node1 node2 ]

Full list of resources:

 FloatIP    (ocf::heartbeat:IPaddr2):    Started node1
WebSite    (ocf::heartbeat:apache):    Started node2

Espere um momento, o servidor web (WebSite) não está sendo executado no mesmo node que o endereço IP (FloatIP)!

Por otimização, o Pacemaker geralmente distribuirá os recursos entre os nodes do cluster. No entanto, podemos dizer ao cluster que dois recursos estão relacionados e precisam ser executados no mesmo node. Vamos informar o cluster que o WebSite deve executar apenas no node em que o FloatIP está ativo. [NODE1] ou [NODE2]

crm configure colocation WebSite-with-FloatIP inf: WebSite FloatIP

E verificar se a alteração ocorreu com sucesso. [NODE1] ou [NODE2]

crm status

O resultado deve ser algo como.

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 10:49:52 2019
Last change: Mon Jul 29 10:49:41 2019 by root via cibadmin on node1

2 nodes configured
2 resources configured

Online: [ node1 node2 ]

Full list of resources:

 FloatIP    (ocf::heartbeat:IPaddr2):    Started node1
 WebSite    (ocf::heartbeat:apache):    Started node1

Para ter certeza de que nossa página responda independentemente da configuração de endereço do Apache, precisamos fazer com que o FloatIP seja iniciado antes do WebSite. [NODE1] ou [NODE2]

crm configure order FloatIP-before-WebSite inf: FloatIP WebSite

Para verificar se a alteração ocorreu com sucesso. [NODE1] ou [NODE2]

crm configure show

Aqui podemos ver toda configuração que fizemos até agora.

node 1217522751: node1
node 1217522754: node2
primitive FloatIP IPaddr2 \
    params ip=192.168.0.10 nic=eth0 cidr_netmask=24 \
    op monitor interval=10s
primitive WebSite apache \
    params configfile="/etc/apache2/apache2.conf" statusurl="http://localhost/server-status" \
    op monitor interval=60s
order FloatIP-before-WebSite inf: FloatIP WebSite
colocation WebSite-with-FloatIP inf: WebSite FloatIP
property cib-bootstrap-options: \
    have-watchdog=false \
    dc-version=1.1.16-94ff4df \
    cluster-infrastructure=corosync \
    cluster-name=debian \
    stonith-enabled=false

Testando o cluster de alta disponibilidade linux

Em primeiro lugar, localize o node onde o endereço IP flutuante (FloatIP) e a página web (WebSite) estão sendo executados. [NODE1] ou [NODE2]

crm status

Nesse exemplo, node1.

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 11:35:09 2019
Last change: Mon Jul 29 10:53:09 2019 by root via cibadmin on node1

2 nodes configured
2 resources configured

Online: [ node1 node2 ]

Full list of resources:

 FloatIP    (ocf::heartbeat:IPaddr2):    Started node1
 WebSite    (ocf::heartbeat:apache):    Started node1

Agora abra um navegador e digite o endereço IP flutuante, no meu caso 192.168.0.10.

Teste do Cluster de alta disponibilidade Linux node 1

Confirmando o cluster (crm status), no momento a página web está sendo servida pelo node1.

Legal, agora vamos simular uma falha no node1 e ver o que acontece. [NODE1]

crm cluster stop

Localize novamente o node onde o endereço IP flutuante (FloatIP) e a página web (WebSite) estão sendo executados. [NODE2]

crm status

Agora estão no node2, inclusive podemos ver que o node1 está offline (na visão do cluster).

Stack: corosync
Current DC: node2 (version 1.1.16-94ff4df) - partition with quorum
Last updated: Mon Jul 29 12:08:33 2019
Last change: Mon Jul 29 10:53:09 2019 by root via cibadmin on node1

2 nodes configured
2 resources configured

Online: [ node2 ]
OFFLINE: [ node1 ]

Full list of resources:

 FloatIP    (ocf::heartbeat:IPaddr2):    Started node2
 WebSite    (ocf::heartbeat:apache):    Started node2

Abra um navegador e digite o endereço IP flutuante, no meu caso 192.168.0.10.

Teste do Cluster de alta disponibilidade Linux node 1

Mesmo com o node1 offline a página web continua disponível, mas agora servida pelo node2.

Não se esqueça de iniciar o cluster novamente após os testes. [NODE1]

crm cluster start

Esse é um assunto interessante e extenso, mas com certeza é um bom começo.

Gostou de criar um cluster de alta disponibilidade linux com Raspberry Pi? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial. Não se esqueça de visitar nosso Fórum para dúvidas e compartilhar suas ideias de projetos com a comunidade.

Até a próxima!!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

{"cart_token":"","hash":"","cart_data":""}