- Naming the Node(s)
- Installing and using
dns_cluster - Scaling our application to multiple VMs
Naming the Node(s)
To make clustering easier, we want our Elixir nodes to be named using our Fly application name and the IPv6 address assigned to the node. Then later, our cluster can form up using DNS settings based on this naming scheme. If you’ve runfly launch already then you should have a rel folder with a rel/env.sh.eex file already.
If not, run this command from your Elixir application:
rel/env.sh.eex file and add ensure the lines looks similar:
Adding dns_cluster
The Phoenix library dns_cluster helps here.
The dns_cluster library lets you easily setup Erlang Clustering using DNS, and Fly.io has built in DNS support!
After installing dns_cluster, add it to the application like this:
dns_cluster_query configuration to the file config/runtime.exs.
fly.toml file:
fly deploy.
This configures dns_cluster to look for other deployed apps using the same $FLY_APP_NAME on the .internal private network.
This assumes that your rel/env.sh.eex file is configured to name your Elixir node using the $FLY_APP_NAME. We did this earlier in the “Naming Your Elixir Node” section.
Before this app can be clustered, we need more than one VM. We’ll do that next!
Running multiple VMs
There are two ways to run multiple VMs.- Scale our application to have multiple Fly Machines in one region.
- Add a Machine to another region (multiple regions).
Scaling in a single region
Let’s scale up to 2 VMs in our current region.Node.list returns the other node. Our two VMs are connected and clustered!
Scaling to multiple regions
Fly.io makes it super easy to run VMs of your applications physically closer to your users. Through the magic of DNS, users are directed to the nearest region where your application is located. Starting back from our baseline of a single VM running insea which is Seattle, Washington (US), I’ll add the region ewr which is NJ (US). I can do this by cloning the existing Fly Machine into my desired region:
The cookie situation
Before two Elixir nodes can cluster together, they must share a secret cookie. The cookie itself isn’t meant to be a super secret encryption key or anything like that, it’s designed to let us create multiple sets of small clusters on the same network that don’t all just connect together. Different cookies means different clusters. For instance, only the nodes that all use the cookieabc will connect together.
For us, this means that in order for my_remote node to connect to the cluster on Fly, we need to share the same cookie value used in production.
The cookie problem
When we build amix release, it generates a long random string for the cookie value. When we re-run the mix release command, it keeps the same cookie value. That is, when we don’t run it in Docker. The Dockerfile we’re using is building a fresh release every time we run it. That’s kind of the point of a Docker container. So our cookie value is being randomly generated every time we deploy. This means after every deploy, we would have to figure out what the new cookie value is so our local node can use it.
The cookie solution
The easiest solution here is to specify the value to use for our cookie. One that we will know outside of the build and that won’t keep changing on us.Making the cookie changes
If we read the Mix.Tasks.Release docs, in the:cookie section we learn that if we provide an ENV named RELEASE_COOKIE, it will be used. If that ENV is not found, it falls back to the randomly generated one.
To generate the cookie string we will use this Elixir command:
RELEASE_COOKIE inside the running app, after generating the cookie, we can either:
- Put it as a secret inside project settings under the
RELEASE_COOKIEname, or - Store it in our
fly.tomlfile like this:
Important IPv6 settings
Theflyctl command attempts to modify your project’s Dockerfile and append the following lines: