Observing Phoenix on Heroku
In this post, I'm going to show you how recent updates to the Heroku command line interface allows one to use Erlang's Observer on Phoenix apps running on Heroku.
I wish I had time to write a lengthy blog post on using Observer, why it wasn't possible on Heroku before, and why it is now, but I don't. Instead, I'm just going to give a brief overview of what's now possible due to recent changes in the CLI.
In order to connect directly to an Erlang node running your Phoenix
application, two things typically are needed. First, your node must be
registered with the Erlang Port Mapper
epmd) that you connect to
locally. Second, your node must be listening on a port that you can connect
The Heroku command line interface has a command called
that forwards a local port to a port on your dyno. Until recently, though,
this command could only forward a single port at a time. What this meant
was that you couldn't simultaneously forward your local
epmd client to
epmd on the dyno and also forward a local port to the port your node
listens on for distributed connections.
Recently, however, with the help of Herokai Joe
Kutner, I was able to land a pull
request in the Heroku
command line interface that updates
heroku ps:forward to accept a
comma-separated list of ports to forward.
In order for this to work, a few changes to a normal Phoenix deploy to Heroku are necessary. You can see these changes in my jclem/phoenix-template repository, or you can generate your own project from the template and deploy it directly to Heroku.
First, the application's Procfile needs to set a few options that allows us to easily make remote connections:
web: elixir --cookie $OTP_COOKIE --name firstname.lastname@example.org --erl '-kernel inet_dist_listen_min 9000' --erl '-kernel inet_dist_listen_max 9000' -S mix phx.server
This is a lengthy Procfile command, so here's the command itself in a more readable form:
$ elixir --cookie $OTP_COOKIE \--name email@example.com \--erl '-kernel inet_dist_listen_min 9000' \--erl '-kernel inet_dist_listen_max 9000' \-S mix phx.server
This new Procfile sets the distributed Erlang cookie to the value of an
OTP_COOKIE environment variable. It also gives your node a predictable name to connect to (
firstname.lastname@example.org) and ensures that the node will listen for remote connections on port
9000 (we set the port min and max to
9000, so it will only be that value).
Once your app is running, you can easily connect directly to your dyno. First, in one shell, forward a couple of ports directly to your dyno:
$ heroku ps:forward 9001:4369,9000
This forwards your local port
9001 to the default port that
epmd will be listening on on the dyno, which is
4369. Next, it forwards your local port
9000 to port
9000 on the dyno, which is where the node is listening for remote connections.
In another shell, you can now start a console, connect to the remote node, and open observer (you'll need to get the value of the
OTP_COOKIE secret on the dyno and use it here):
$ env ERL_EPMD_PORT=9001 iex --cookie $OTP_COOKIE --name email@example.comErlang/OTP 22 [erts-10.4.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]Interactive Elixir (1.9.1) - press Ctrl+C to exit (type h() ENTER for help)iex(firstname.lastname@example.org)1> Node.connect(:"email@example.com")trueiex(firstname.lastname@example.org)2> :observer.start:ok
Once the Observer starts, you can use the "Nodes" dropdown menu to select
your remote node (
email@example.com) and begin observing!