Brincando com Erlang nodes: cloonix
- 5 minutes read - 1012 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
1 NET=nemo
2DEMOVM=cloonix-image-minimal-qemux86.wic.qcow2
3CONFIGS=~/work/opensource/cloonix_eclero
4
5cloonix_cli ${NET} kil cloonix_net ${NET} cloonix_gui ${NET} cloonix_cli ${NET}
6add kvm bird0 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} & cloonix_cli ${NET}
7add kvm bird1 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} & cloonix_cli ${NET}
8add kvm bird2 ram=800 cpu=2 dpdk=0 sock=1 hwsim=0 ${DEMOVM} &
9
10cloonix_ssh ${NET} bird0 "echo" 2>/dev/null cloonix_ssh ${NET} bird1 "echo"
112>/dev/null cloonix_ssh ${NET} bird2 "echo" 2>/dev/null
12
13sleep 5
14
15cloonix_cli ${NET} add lan bird0 0 lan1 cloonix_cli ${NET} add lan bird1 0 lan1
16cloonix_cli ${NET} add lan bird2 0 lan1
17
18cloonix_cli nemo add snf snf1 sleep 5 cloonix_cli nemo add lan snf1 0 lan1
19
20sleep 5
21
22cloonix_ssh ${NET} bird0 "hostname eclero6" cloonix_ssh ${NET} bird1 "hostname
23eclero8" cloonix_ssh ${NET} bird2 "hostname eclero10"
24
25cloonix_ssh ${NET} bird0 "ifconfig eth0 192.168.7.6" cloonix_ssh ${NET} bird1
26"ifconfig eth0 192.168.7.8" cloonix_ssh ${NET} bird2 "ifconfig eth0
27192.168.7.10"
Após a execução do script, o seguinte ambiente é criado:
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:
1 cloonix_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:
1 Eshell 2 V10.5.6 (abort with ^G) (eclero@eclero6)1> 3 epmdless_dist:add_node('eclero@eclero6', 17012). ok 4 (eclero@eclero6)2> epmdless_dist:add_node('eclero@eclero8', 17012). ok 5 (eclero@eclero6)3> epmdless_dist:add_node('eclero@eclero10', 17012). 6
- conectando os nós:
1 (eclero@eclero6)4> net_adm:ping('eclero@eclero6'). pong (eclero@eclero6)5> 2 net_adm:ping('eclero@eclero8'). pong (eclero@eclero6)6> 3 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(). Port Module Recv Sent Owner Local Address 2 Foreign Address State Type 48 inet*tcp 0 0 <0.59.0> *:17012 _:_ ACCEPTING 3 STREAM 112 inet*tcp 0 0 <0.133.0> *:8000 _:_ ACCEPTING STREAM 192 inet_tcp 4 190625 190584 <0.280.0> 192.168.7.10:50523 192.168.7.6:17012 ???? STREAM 480 5 inet_tcp 130962 130646 <0.431.0> 192.168.7.10:37643 192.168.7.8:17012 ???? 6 STREAM ok
- abrindo o wireshark para checar a rede
Até agora temos um ambiente simulado com três nós onde cada VM Erlang está conectada utilizando o protocolo Erlang Distributed Protocol.
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>
2eclero_decision_server:is_health(). true
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(). false
2
3(eclero@eclero6)15> eclero_health:get(). {ok,[{eclero@eclero10,true},
4{eclero@eclero6,true}, {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>
2eclero_decision_server:is_health(). true (eclero@eclero8)10>
3erlang:disconnect_node('eclero@eclero10'). true (eclero@eclero8)11> nodes().
4[eclero@eclero6]
5
6(eclero@eclero8)12> eclero_decision_server:is_health(). false
Entretanto, se checarmos o estado em outro nó temos, por exemplo ’eclero6'
1 (eclero@eclero6)21>
2eclero_decision_server:is_health(). true
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.