In today's episode, we're going to deploy the BEAM Toolbox to Heroku using "heroku-buildpack-elixir" from HashNuke. We'll also take a quick diversion at the start to bring our application up to date with the latest version of Phoenix, which introduces a nice configuration layer. Let's get started.
I've tagged the
beam_toolbox repository with
before_episode_067 if you want
to follow along.
Alright, so the first thing we're going to do is update our version of phoenix.
mix deps.update phoenix
The episode's resources section links to one of the more interesting recent additions to Phoenix, which is the ExConf project. It provides a nice DSL for managing application configuration - in this case, for specifying that the development environment should run on port 4000, but the production environment should look for the PORT environment variable, which is populated by Heroku and is necessary for your application to be visible to the outside world.
In order to use ExConf with Phoenix, we'll need to define a BeamToolbox.Config module, and then a module underneath it for each of our three environments: dev, prod, and test.
Start off by making the
Then open up
defmodule BeamToolbox.Config do use Phoenix.Config.App config :router, port: System.get_env("PORT") config :plugs, code_reload: false config :logger, level: :error end
This is our base configuration to be used for fallback. Next, we'll define our
dev environment's config. Open up
defmodule BeamToolbox.Config.Dev do use BeamToolbox.Config config :router, port: System.get_env("PORT") || "4000", ssl: false config :plugs, code_reload: true config :logger, level: :debug end
Here, we've defined the port we'll use in dev - either the environment variable
PORT, or we'll fall back to 4000. We've also enabled phoenix's code reloading
in development, which should speed up our development cycle. Next, open up
defmodule BeamToolbox.Config.Prod do use BeamToolbox.Config config :router, port: System.get_env("PORT") config :plugs, code_reload: false config :logger, level: :error end
Here, we require the PORT environment variable. Finally, open up
defmodule BeamToolbox.Config.Test do use BeamToolbox.Config config :router, port: 4001, ssl: false config :plugs, code_reload: true config :logger, level: :debug end
Here, we'll run it on port 4001 when testing. Phoenix already knows how to look in this config to find these configuration variables, but ExConf is entirely usable outside of Phoenix, and it's something I've missed in Elixir until now. Kudos to Chris McCord and anyone else involved for building it. I know Jose gave some suggestions on how it should work early on as well.
Finally, our router is hardcoded to specify port 4000 presently. Let's remove
that. Open up
Something else we've done incorrectly so far is we aren't really supervising
anything. We want to make it so that when you run our application, it Just
Works, and if any parts of it die they're restarted appropriately. To deal with
this, we'll move a couple of things from
lib/beam_toolbox.ex into the
application's supervisor. (Open up
lib/beam_toolbox.ex and move stuff)
# Apparently I can't start an HTTPotion in a supervisor because it returns # something that isn't compatible with being a supervision child, so we'll just # start it and fail to supervise it... BeamToolbox.GitHub.start() # Now we'll add our router and cadfaerl to the supervision tree children = [ worker(:cadfaerl, [:github, 2000]), worker(BeamToolbox.Router, , function: :start) ]
Alright, so now when our app starts, it will start our router, which starts the BeamToolbox.GitHub HTTPotion-based module, and then supervises our cache and our router. This concludes all the preparation work, and we can move on to deploying to heroku.
Deploying to Heroku
So, it's almost sad to say, but deploying to Heroku isn't even hard work. HashNuke built a heroku buildpack that takes care of everything for us. Let's create a new project on heroku, and tell it to use this buildpack:
heroku create --buildpack "https://github.com/HashNuke/heroku-buildpack-elixir.git"
Alright, so now our application is ready on heroku. Next, we'll need to
configure the buildpack. This is done with an
elixir_buildpack.config file in
the root of the project. Create that file, and put this in there:
# Erlang version erlang_version=17.0 # Elixir version (here I'm just specifying a commit because if you specify # master it's rebuilt each push) elixir_version=(commit 0883c753356a08) # Rebar version rebar_version=(tag 2.2.0) # Do dependencies have to be built from scratch on every deploy? always_build_deps=false
Alright, that's really all it takes to configure this buildpack. Next, the only
real thing to take care of is adding a Procfile so heroku knows what to run for
the web processes. Open up a new file called
web: MIX_ENV=prod mix run --no-halt
This just sets our mix environment to prod on heroku, then runs our app and tells it not to halt after starting up. Now, we just need to commit all of this:
git add . git commit -m"Upgrade phoenix and add heroku deployment" git push origin episode_067 git push heroku episode_067:master
Now heroku will use their binary for erlang 17.0, build elixir this one time for us, and then compile our application and deploy the slug. Once it's all done, we should be able to visit in the browser, and it'll be running:
(visit the url that heroku gives us, plus /pages/home. Then click Amrita.)
Alright, so that's all there is to it. Most of our time was spent upgrading Phoenix so that we could have heroku give us the port - this buildpack makes actually deploying an elixir application to heroku trivial.
This is really exciting, and now we can start making beam toolbox a bit more functional. See you soon!