Brincando com Erlang nodes: cloonix
- 5 minutes read - 1027 wordsEste post faz parte de uma série de outros posts relacionados a como usar Erlang distribution protocol.
Na primeira parte, Brincando com Erlang nodes: eclero criamos uma aplicação básica. Já na segunda parte, Brincando com Erlang nodes: embedded , criamos uma imagem Linux para a aplicação e clero. Na terceira parte, Brincando com Erlang nodes: epmd , apresentamos a solução para o problema do epmd. E agora vem a pergunta, como podemos simular mais nós dentro do ambiente ?
Mas antes vamos atualizar a lista dos requisitos:
Cada nó necessita detectar e ser notificado de qualquer falha dos outros nós- O cluster de nós Erlang deve ser configurado utilizando algum tipo de configuração vinda do ambiente
O ambiente de execução é Linux embarcado rodando em qualquer plataformaDesejável poder rodar em um ambiente embarcado com o mínimo de recursos necessários- Mínimo de 3 nós para a solução funcionar
- Desejo inspecionar os pacotes transmitidos entre os nós do cluster
cloonix
cloonix é um software para criar e simular redes virtuais utilizando kvm para fazer a virtualização dos hosts. Escreve um post sobre como utilizar o cloonix com o Yocto Project e agora vamos utilizar para simular os nós que precisamos.
Simulando o ambiente
Para isso utilizei o script abaixo no qual cria três máquinas virtuais com uma interface de rede cada uma conectadas em uma lan e um sniffer (wireshark) escutando a lan. Assim vamos conseguir inspecionar os pacotes trocados entre todos os hosts
1NET=nemo
2DEMOVM=cloonix-image-minimal-qemux86.wic.qcow2
3CONFIGS=~/work/opensource/cloonix_eclero
4
5cloonix_cli ${NET} kil
6cloonix_net ${NET}
7cloonix_gui ${NET}
8cloonix_cli ${NET} add kvm bird0 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} &
9cloonix_cli ${NET} add kvm bird1 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} &
10cloonix_cli ${NET} add kvm bird2 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} &
11
12cloonix_ssh ${NET} bird0 "echo" 2>/dev/null
13cloonix_ssh ${NET} bird1 "echo" 2>/dev/null
14cloonix_ssh ${NET} bird2 "echo" 2>/dev/null
15
16sleep 5
17
18cloonix_cli ${NET} add lan bird0 0 lan1
19cloonix_cli ${NET} add lan bird1 0 lan1
20cloonix_cli ${NET} add lan bird2 0 lan1
21
22cloonix_cli nemo add snf snf1
23sleep 5
24cloonix_cli nemo add lan snf1 0 lan1
25
26sleep 5
27
28cloonix_ssh ${NET} bird0 "hostname eclero6"
29cloonix_ssh ${NET} bird1 "hostname eclero8"
30cloonix_ssh ${NET} bird2 "hostname eclero10"
31
32cloonix_ssh ${NET} bird0 "ifconfig eth0 192.168.7.6"
33cloonix_ssh ${NET} bird1 "ifconfig eth0 192.168.7.8"
34cloonix_ssh ${NET} bird2 "ifconfig eth0 192.168.7.10"
Após a execução do script, o seguinte ambiente é criado:

Três instâncias do qemu, conectados em uma lan.
O próximo passo é conectar em cada máquina virtual criada para inicializar a aplicação eclero. Fazemos os seguintes comandos em três terminais:
1cloonix_ssh nemo bird0
2
3/usr/lib/eclero/eclero
E em cada terminal temos uma instância do eclero rodando no qual vamos dizer para o ‘epmdless’ onde estão os nós e também iniciar a conexão entre eles.
- configurando o epmdless:
1Eshell V10.5.6 (abort with ^G) 2(eclero@eclero6)1> epmdless_dist:add_node('eclero@eclero6', 17012). 3ok 4(eclero@eclero6)2> epmdless_dist:add_node('eclero@eclero8', 17012). 5ok 6(eclero@eclero6)3> epmdless_dist:add_node('eclero@eclero10', 17012).
- conectando os nós:
1(eclero@eclero6)4> net_adm:ping('eclero@eclero6'). 2pong 3(eclero@eclero6)5> net_adm:ping('eclero@eclero8'). 4pong 5(eclero@eclero6)6> net_adm:ping('eclero@eclero10').
- verificando se os nós estão conectados,
Port 192
ePort 480
mostram duas conexões vindas de dois IPs na porta 17012. Assim confirmamos que o epmdless e os nós estão conectados corretamente1(eclero@eclero10)21> inet:i(). 2Port Module Recv Sent Owner Local Address Foreign Address State Type 348 inet_tcp 0 0 <0.59.0> *:17012 *:* ACCEPTING STREAM 4112 inet_tcp 0 0 <0.133.0> *:8000 *:* ACCEPTING STREAM 5192 inet_tcp 190625 190584 <0.280.0> 192.168.7.10:50523 192.168.7.6:17012 ???? STREAM 6480 inet_tcp 130962 130646 <0.431.0> 192.168.7.10:37643 192.168.7.8:17012 ???? STREAM 7ok
- abrindo o wireshark para checar a rede
Wireshark mostrando os pacotes entre cada nó.
Até agora temos um ambiente simulado com três nós onde cada VM Erlang está conectada utilizando o protocolo Erlang Distributed Protocol.

Ambiente cloonix em execução
Verificando o estado do cluster eclero
Vamos fazer duas verificações para responder duas perguntas:
- O que acontece se todos os nós estão conectados e removemos um nó de maneira não graceful
- O que acontece se todos os nós estão conectados e removemos um nó de maneira graceful (utilizando por exemplo
erlang:disconnect_node/1
)
Para o primeiro caso, em qualquer um dos nós, executamos:
1(eclero@eclero6)9> eclero_decision_server:is_health().
2true
Então, removemos um nó do cluster (por exemplo saindo da aplicação e clero em um dos nós)
1(eclero@eclero6)10> eclero_decision_server:is_health().
2false
3
4(eclero@eclero6)15> eclero_health:get().
5{ok,[{eclero@eclero10,true},
6 {eclero@eclero6,true},
7 {eclero@eclero8,false}]}
Podemos ver que a aplicação detecta o desaparecimento do nó e coloca todo o cluster no estado não saudável (eclero_decision_server:is_health()
é false).
Já para o segundo caso, em qualquer um dos nós, executamos:
1(eclero@eclero8)9> eclero_decision_server:is_health().
2true
3(eclero@eclero8)10> erlang:disconnect_node('eclero@eclero10').
4true
5(eclero@eclero8)11> nodes().
6[eclero@eclero6]
7
8(eclero@eclero8)12> eclero_decision_server:is_health().
9false
Entretanto, se checarmos o estado em outro nó temos, por exemplo ‘eclero6’
1(eclero@eclero6)21> eclero_decision_server:is_health().
2true
Informando que o cluster está operacional.
Aparentemente este cenário pode parecer errado. Mas está correto pois a função disconnect_node/1
força a desconexão de um determinado nó. No caso o nó ‘eclero8’ desconectou o nó ‘eclero10’.
Conclusão
Enfim, a intenção aqui foi escrever sobre como simular algum ambiente utilizando um simulador de rede e que uma aplicação possa ser testada de alguma forma. Com certeza a técnica pode ser utilizada para outros tipos de problemas.
Ter a possibilidade de executar simulações em um cluster Erlang pode ajudar bastante na prototipagem e testes das aplicações. Podendo até mesmo ajudar nos testes quando usamos o common_test.
Durante a execução desta série de posts, tive vários pequenos impasses:
- dependência de resolução de IP para nomes (utilizei o arquivo ‘/etc/hosts’ com todos os hosts)
- dependência, por parte do ERTS, de um shell (bash, sh) para fazer o fork de alguns ports no qual a VM Erlang necessidade (por exemplo: inet_gethost)
- patch do erlinit para o mesmo utilizar o ERTS da aplicação e não depender dos pacotes erlang da distribuição, Add support to use ERTS from release instead of default Erlang directory
- entender como a aplicação epmdless funciona e propor alguma solução para auto descoberta dos nós sem precisar fazer configurações adicionais
Nos próximos posts da série vamos caminhar mais com a solução.