Connect Nerves devices forming an Erlang cluster
I want to enable my Nerves devices to talk to each other using Distributed Erlang. Here is my memo.
Preparation
Find hostname or IP address for each device
When shelling into a Nerves device, we will see IP address and host name printed by nerves_motd.
DNS Bridge configuration
According to mdns_lite - DNS Bridge configuration documentation, we need to let Erlang/OTP's built-in DNS resolver know about mDNS. Underjord's YouTube video explaining DNS Bridge configuration was helpful.
Start Erlang nodes
- nerves_pack - Erlang distribution documentation explains about how to form an Erlang cluster.
- Let's say we have a device with host name
nerves-mn00.local
and anoter with host namenerves-mn02.local
. - Alternatively IP addresses can be used instead of host names
- Make sure that the same Erlang magic cookie is used
❯ ssh nerves-mn00.local
# make sure the epmd OS process is running by calling epmd -daemon
iex> System.cmd("epmd", ["-daemon"])
# start a node.
iex> Node.start(:"nerves@nerves-mn00.local")
# check if current node. `node/0` does the same.
iex(nerves@nerves-mn00.local)> Node.self()
# configure the magic cookie to form this cluster.
iex(nerves@nerves-mn00.local)> Node.set_cookie(:securecookie)
❯ ssh nerves-mn02.local
iex> System.cmd("epmd", ["-daemon"])
iex> Node.start(:"nerves@nerves-mn02.local")
iex(nerves@nerves-mn02.local)> Node.self()
iex(nerves@nerves-mn02.local)> Node.set_cookie(:securecookie)
Connect a node to another
- Let's connect
nerves-mn02.local
tonerves-mn00.local
iex(nerves@nerves-mn02.local)> Node.connect(:"nerves@nerves-mn00.local")
true
iex(nerves@nerves-mn02.local)> Node.list()
[:"nerves@nerves-mn00.local"]
Invoke a function in another node
- From
nerves-mn02.local
invoke a function that is defined innerves-mn00.local
usingNode.spawn/2
- Let's access to the other device and peek into its firmware infomation by invoking
Toolshed.uname/0
that is normally available in our Nerves IEx shell.
Node.spawn(
# the node name we want to invoke a function in
:"nerves@nerves-mn00.local",
# the function we want to invoke
fn -> Toolshed.Nerves.uname() end
)
iex(nerves@nerves-mn02.local)> Node.spawn(:"nerves@nerves-mn00.local", fn -> Toolshed.Nerves.uname() end)
Nerves nerves-mn00 hello_nerves 0.1.0 (240d82e1-1e6c-5800-0ef9-63cba9efc212) arm
#PID<51667.6840.0>
Yay, we were able to look at the other devices info through Erlang distribution. Isn't it cool?