At Find a Player[1], we've been trying to figure out how we want to cluster our Elixir servers - so that they can share state between each other - recently.
Because we come from ruby (ie, not a high-availability background), when we want to discover other machines in a network we'd traditionally use something like Consul[2] - of which I wrote a ruby library called Diplomat[3] (which admittedly needs some love).
However, after chatting with Gordon Guthrie[4] - a guy who's been working with Erlang for over 13 years - over twitter, he told us to checkout :net_adm.world/0
. Which does pretty much exactly what we want, straight out of the box.
So, if you are building an Elixir app with Autoscaling and AWS, here is how to autoconnect your nodes:
=> 1: Find a Player | 2: Consul | 3: Diplomat | 4: Gordon Guthrie
On each node, make sure that the hostname is set to the private DNS:
HOSTNAME="$(curl http://169.254.169.254/latest/meta-data/hostname)" sudo hostnamectl set-hostname $HOSTNAME echo "127.0.0.1 ${HOSTNAME}" >> /etc/hosts
.hosts.erlang
file
If you have awscli
installed (highly recommended) then you can run this at some point before running the app to build the file:
SERVER_CLASS=tag-for-similar-nodes-in-aws aws ec2 describe-instances \ --query 'Reservations[].Instances[].NetworkInterfaces[].PrivateIpAddresses[].PrivateDnsName' \ --output text \ --filter Name=tag:Class,Values=${SERVER_CLASS} | sed '$!N;s/\t/\n/' | sed -e "s/\(.*\)/'\1'./" > $OTP_ROOT/.hosts.erlang
The above script will find matching instances, print out their private DNS, and produce a file in the format below:
'lorem.example.org'. 'ipsum.example.org'.
vm.args
Make sure the hostname is setup correctly in your vm.args. Here's a one-liner to do it using sed:
sed -r -i "s/\-sname.+/\-name api\@$(hostname)/g" /opt/api/releases/$LATEST_RELEASE/vm.args
In your code, add a module which checks if the .hosts.erlang
file exists, and if so, use it to connect to one of the nodes.
defmodule Api.Discovery do require Logger def discover do # Attempt to connect to any other outside nodes. # This command makes use of the /opt/api/.hosts.erlang file hosts_path = Path.expand("/opt/api/.hosts.erlang") case File.exists?(hosts_path) do true -> Logger.info("Attempting to join cluster") :net_adm.world(:verbose) false -> Logger.warn("Couldn't find #{hosts_path} file - not joining cluster") end end end
text/gemini
This content has been proxied by September (3851b).