This story was originally posted by me on Medium on 2016-11-28, but moved here since I'm closing my Medium account.
Elixir is shipped with a ton of nice features. One of those is the ability to start the erlang
:observer on a running appliciation and see the process tree, inspect indiviual processes and more.
Just to recap, the observer is started from an iex session that is preloaded with the mix application we want to inspect:
$ iex -S mix Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> :observer.start
Note that it is not necessary to start iex with
-S mix for observer to work.
The observer is a GUI application that shows a lot of useful information about the running application. I use it a lot when developing if a Supervisor or GenServer is not behaving as I would expect.
If you a new to observer, find a post that describes it in more detail, It is time well spent.
I have a habit of running almost every software project inside a Vagrant virtual machine. Doing so allows me to have different versions of the database or other dependencies installed for each project.
I also like that I don’t pollute my host machine with all kinds of software needed for a specific project.
But running my development environment in Vagrant makes it harder to run the observer, since a Vagrant box is headless. I can’t run a GUI inside it.
Luckily Erlang has me covered. I can start a node outside my Vagrant (on my host machine) and connect to a running application inside Vagrant. To do so the two nodes must share a secret, known as a cookie in Erlang terms.
We also need to name each node, so that each of them can be found by name and host.
Start the application inside Vagrant with a name and a cookie:
iex --name firstname.lastname@example.org --cookie 123 -S mix phoenix.server
My project is a Phoenix project, which is why I start it with
mix phoenix.server. The IP address is the one of my Vagrant box as specified in my Vagrantfile:
config.vm.network "private_network", ip: "192.168.33.10"
Then I start a node on my host machine, and from that I can connect to the Vagrant node and start observer:
iex --name email@example.com --cookie 123 Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help) iex(firstname.lastname@example.org)1> Node.connect(:"email@example.com") true iex(firstname.lastname@example.org)2> :obsever.start
In the observer I can choose another node than the local one from the menu and I now see the application list and supervision trees from the remote node as if I had started observer from that node.
But… I soon ran into trouble with a firewall.
I later installed a firewall on my Vagrant box. Mostly to test the firewall setup on the production system. After this I could no longer connect from a remote iex. This was kind of expected since that is pretty much the firewall’s job: Only allow the specified ports to connect.
So how do I configure a firewall to allow incoming node connections?
It turns out that connecting to a remote node requires two ports to be open. The port for discovering the node and the port at which the actual connection is opened at.
The discovery port defaults to 4369 but the other port is chosen at random in some huge range. We can limit this port range by specifying some erlang properties when starting iex.
This limits the range to the single port
iex --name email@example.com \ --cookie 123 \ --erl '-kernel inet_dist_listen_min 9000' \ --erl '-kernel inet_dist_listen_max 9000' \ -S mix phoenix.server
Now I can just allow port 4369 and 9000 in my firewall, and I’m good to connect to my firewalled application inside a Vagrant box!