The only thing we'll need for this guide is a working Phoenix application. For those of us who need a simple application to deploy, please follow the Up and Running guide.
You can just:
$ mix phx.new my_app
The main goal for this guide is to get a Phoenix application running on Fly.io.
Let's separate this process into a few steps, so we can keep track of where we are.
Follow the instructions here to install Flyctl, the command-line interface for the Fly.io platform.
We can sign up for an account using the CLI.
$ fly auth signup
Or sign in.
$ flyctl auth login
Fly has a free tier for most applications. A credit card is required when setting up an account to help prevent abuse. See the pricing page for more details.
To tell Fly about your application, run fly launch in the directory with your source code. This creates and configures a Fly.io app.
$ fly launch
This scans your source, detects the Phoenix project, and runs mix phx.gen.release --docker for you! This creates a Dockerfile for you.
The fly launch command walks you through a few questions.
personal). Organizations are a way of sharing applications and resources between Fly.io users.The fly launch command also created a fly.toml file for you. This is where you can set ENV values and other config.
You may also have some secrets you'd like to set on your app.
Use fly secrets to configure those.
$ fly secrets set MY_SECRET_KEY=my_secret_value
When you want to deploy changes to your application, use fly deploy.
$ fly deploy
Note: On Apple Silicon (M1) computers, docker runs cross-platform builds using qemu which might not always work. If you get a segmentation fault error like the following:
=> [build 7/17] RUN mix deps.get --only => => # qemu: uncaught target signal 11 (Segmentation fault) - core dumped
You can use fly's remote builder by adding the --remote-only flag:
$ fly deploy --remote-only
You can always check on the status of a deploy
$ fly status
Check your app logs
$ fly logs
If everything looks good, open your app on Fly
$ fly open
Elixir supports getting a IEx shell into a running production node.
There are a couple prerequisites, we first need to establish an SSH Shell to our machine on Fly.io.
This step sets up a root certificate for your account and then issues a certificate.
$ fly ssh establish $ fly ssh issue
With SSH configured, let's open a console.
$ fly ssh console Connecting to my-app-1234.internal... complete / #
If all has gone smoothly, then you have a shell into the machine! Now we just need to launch our remote IEx shell. The deployment Dockerfile was configured to pull our application into /app. So the command for an app named my_app looks like this:
$ app/bin/my_app remote Erlang/OTP 23 [erts-11.2.1] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help) iex(my_app@fdaa:0:1da8:a7b:ac4:b204:7e29:2)1>
Now we have a running IEx shell into our node! You can safely disconnect using CTRL+C, CTRL+C.
Elixir and the BEAM have the incredible ability to be clustered together and pass messages seamlessly between nodes. This portion of the guide walks you through clustering your Elixir application.
There are 2 parts to getting clustering quickly setup on Fly.io.
libcluster
libcluster
The widely adopted library libcluster helps here.
There are multiple strategies that libcluster can use to find and connect with other nodes. The strategy we'll use on Fly.io is DNSPoll.
After installing libcluster, add it to the application like this:
defmodule MyApp.Application do
use Application
def start(_type, _args) do
topologies = Application.get_env(:libcluster, :topologies) || []
children = [
# ...
# setup for clustering
{Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]}
]
# ...
end
# ...
end
Our next step is to add the topologies configuration to config/runtime.exs.
app_name =
System.get_env("FLY_APP_NAME") ||
raise "FLY_APP_NAME not available"
config :libcluster,
topologies: [
fly6pn: [
strategy: Cluster.Strategy.DNSPoll,
config: [
polling_interval: 5_000,
query: "#{app_name}.internal",
node_basename: app_name
]
]
]
This configures libcluster to use the DNSPoll strategy and look for other deployed apps using the $FLY_APP_NAME on the .internal private network.
We need to control the naming of our Elixir nodes. To help them connect up, we'll name them using this pattern: [email protected]. To do this, we'll generate the release config.
$ mix release.init
Then edit the generated rel/env.sh.eex file and add the following lines:
ip=$(grep fly-local-6pn /etc/hosts | cut -f 1) export RELEASE_DISTRIBUTION=name export RELEASE_NODE=$FLY_APP_NAME@$ip
After making the change, deploy your app!
$ fly deploy
For our app to be clustered, we have to have multiple instances. Next we'll add an additional node instance.
There are two ways to run multiple instances.
Let's first start with a baseline of our single deployment.
$ fly status ... Instances ID VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED f9014bf7 26 sea run running 1 total, 1 passing 0 1h8m ago
Let's scale up to 2 instances in our current region.
$ fly scale count 2 Count changed to 2
Checking the status, we can see what happened.
$ fly status ... Instances ID VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED eb4119d3 27 sea run running 1 total, 1 passing 0 39s ago f9014bf7 27 sea run running 1 total, 1 passing 0 1h13m ago
We now have two instances in the same region.
Let's make sure they are clustered together. We can check the logs:
$ fly logs ... app[eb4119d3] sea [info] 21:50:21.924 [info] [libcluster:fly6pn] connected to :"my-app-1234@fdaa:0:1da8:a7b:ac2:f901:4bf7:2" ...
But that's not as rewarding as seeing it from inside a node. From an IEx shell, we can ask the node we're connected to, what other nodes it can see.
$ fly ssh console $ /app/bin/my_app remote
iex(my-app-1234@fdaa:0:1da8:a7b:ac2:f901:4bf7:2)1> Node.list [:"my-app-1234@fdaa:0:1da8:a7b:ac4:eb41:19d3:2"]
The IEx prompt is included to help show the IP address of the node we are connected to. Then getting the Node.list returns the other node. Our two instances are connected and clustered!
Fly makes it easy to deploy instances closer to your users. Through the magic of DNS, users are directed to the nearest region where your application is located. You can read more about Fly.io regions here.
Starting back from our baseline of a single instance running in sea which is Seattle, Washington (US), let's add the region ewr which is Parsippany, NJ (US). This puts an instance on both coasts of the US.
$ fly regions add ewr Region Pool: ewr sea Backup Region: iad lax sjc vin
Looking at the status shows that we're only in 1 region because our count is set to 1.
$ fly status ... Instances ID VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED cdf6c422 29 sea run running 1 total, 1 passing 0 58s ago
Let's add a 2nd instance and see it deploy to ewr.
$ fly scale count 2 Count changed to 2
Now the status shows we have two instances spread across 2 regions!
$ fly status ... Instances ID VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED 0a8e6666 30 ewr run running 1 total, 1 passing 0 16s ago cdf6c422 30 sea run running 1 total, 1 passing 0 6m47s ago
Let's ensure they are clustered together.
$ fly ssh console $ /app/bin/my_app remote
iex(my-app-1234@fdaa:0:1da8:a7b:ac2:cdf6:c422:2)1> Node.list [:"my-app-1234@fdaa:0:1da8:a7b:ab2:a8e:6666:2"]
We have two instances of our application deployed to the West and East coasts of the North American continent and they are clustered together! Our users will automatically be directed to the server nearest them.
The Fly.io platform has built-in distribution support making it easy to cluster distributed Elixir nodes in multiple regions.
Open the Dashboard for your account
$ fly dashboard
Deploy your application
$ fly deploy
Show the status of your deployed application
$ fly status
Access and tail the logs
$ fly logs
Scaling your application up or down
$ fly scale count 2
Refer to the Fly.io Elixir documentation for additional information.
Working with Fly.io applications covers things like:
See Troubleshooting
Visit the Fly.io Community to find solutions and ask questions.
© 2014 Chris McCord
Licensed under the MIT License.
https://hexdocs.pm/phoenix/fly.html