Modern web technology stacks are constantly trying to get faster, trying to meet the never-ending requirements of the community and cater to the ever-growing user base.
It is hard to choose a technology that strikes a good balance between developer productivity and also is able to provide a good throughput, latency, uptime, extendability etc.
Elixir I feel is, productive for developers but also provides speed and concurrency to build robust software. But I have found deployment methodologies for Elixir - Phoenix applications not to be as mature as the runtime. Deciding on using a language solely based on the speed and productivity is not always ideal, we also have to consider other factors such as:
- What mechanisms are available for deployment?
- How easy/intuitive is it to deploy/rollback the applications?
- What is the cost/effort that goes into deployment?
- How actively maintained is the library we are planning to use for deployment?
- How easy is it to change our deployment strategies?
In this blog series, we will cover some of the ways to deploy an Elixir - Phoenix application.
|Elixir on Heroku||https://www.icicletech.com/blog/elixir-phoenix-deployment-on-heroku|
|Elixir on Google Cloud||Coming Soon|
|Elixir on DigitalOcean (Using edeliver)||Coming Soon|
|Elixir deployment using nanobox||Coming Soon|
But first, let us see a quick introduction to Elixir and Phoenix.
Introduction to Elixir - Phoenix
Phoenix is a web development framework written in Elixir (dynamic, and functional language designed for building scalable and maintainable applications.) which implements the MVC design pattern.
Leveraging on the Erlang VM, which is known for running low-latency, distributed and fault-tolerant systems. Phoenix provides the best of both worlds - developer productivity and high application performance.
It also comes pre-built with powerful tools such as channels that makes implementation of real-time features easy, along with pre-compiled templates (views) makes rendering really fast.
Let us start this series by looking at how easy it is to deploy an Elixir application to Heroku
Elixir on Heroku
Heroku is a polygot platform that supports various languages such as Ruby, Scala, Go, Python and Elixir. The main advantage Heroku brings to the table is the simplicity of deployment, in a sense that it allows developers to build, run and scale applications in a similar manner across several languages.
Heroku requires you to have Git installed. Before we deploy the application to Heroku we need to initialize our Git Repository and commit the files to it.
Initializing a Git Repository
We can initialize
gitby running the following commands in the root of your project directory.
$ git init $ git add . $ git commit -m "Initial commit"
Installing Heroku CLI (Formerly called toolbelt)
You can signup for a Heroku account, if you already have an account handy you can install the Heroku CLI which basically is a tool used to create and manage applications on Heroku via the terminal. For this tutorial we will be working with a free account, this will give us access to one web dyno and one worker dyno, as well as a PostgreSQL and Redis.
Now that we have Heroku CLI is installed we can login to Heroku via the same. To log in to Heroku open a terminal and execute
heroku login, enter the email ID and password that you used to signup for Heroku. If the login was successful you will see a confirmation saying
Logged in as YOUR-EMAIL-ID.
Creating Elixir Application On Heroku
Let us now create the Phoenix application on Heroku which will run our project. In the root of the project run the following command.
$ heroku create --buildpack "https://github.com/HashNuke/heroku-buildpack-elixir.git"
This should give you an output similar to the one below.
Creating app... done, ⬢ pure-peak-67829 Setting buildpack to https://github.com/HashNuke/heroku-buildpack-elixir.git... done https://pure-peak-67829.herokuapp.com/ | https://git.heroku.com/pure-peak-67829.git
NOTE the name of the Phoenix application that was created in my case was
pure-peak-67829. This will vary for you. As Heroku creates a new and unique name for each application that it creates.
--buildpackis a convenient way of packaging framework and/or runtime support. In our case, we are installing the Erlang runtime, Elixir and also fetching the required dependencies.
Adding Phoenix Static Buildpack
NOTE this step is only needed if you have static assets in your projects, for API based Phoenix applications or for the ones that have been created with
--no-brunchthis step is not needed.
To add the required
buildpack. Paste the following in the terminal.
$ heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
If the above command was successful then you should see an output similar to the one below.
Buildpack added. Next release on pure-peak-67829 will use: 1. https://github.com/HashNuke/heroku-buildpack-elixir.git 2. https://github.com/gjaldon/heroku-buildpack-phoenix-static.git Run git push heroku master to create a new release using these buildpacks.
Configuring Phoenix For Heroku
Since Heroku requires us to pass in sensitive information via Environment variables we will have to make some changes to our code before we can actually deploy to Heroku.
First let us change
config/prod.secret.exsto load our secret key from Heroku's environment variable and also configure our
Repoto load values from Heroku's environment variables.
use Mix.Config config :demo_heroku_deployment, DemoHerokuDeploymentWeb.Endpoint, load_from_system_env: true, # Don't forget to replace pure-peak-67829 with the name of your Phoenix application that was created. url: [scheme: "https", host: "pure-peak-67829.herokuapp.com", port: 80], cache_static_manifest: "priv/static/cache_manifest.json", secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE") config :demo_heroku_deployment, DemoHerokuDeployment.Repo, adapter: Ecto.Adapters.Postgres, url: System.get_env("DATABASE_URL"), pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), ssl: true config :logger, level: :info
Next we have to decrease the timeout for the WebSocket transport in
lib/demo_heroku_deployment/channels/user_socket.exbecause Heroku has an idle timeout of 55 seconds. And this ensures that all Idle connections are killed before the idle timeout is reached.
defmodule DemoHerokuDeploymentWeb.UserSocket do use Phoenix.Socket ... ## Transports transport :websocket, Phoenix.Transports.WebSocket, timeout: 50_000 ... end
The last step of the configuration involves creating a
Procfilein the root of the project.
web: MIX_ENV=prod mix phx.server
Setting Up Environment Variables On Heroku
First let us configure the Phoenix application to use Postgres as the database by adding an add-on.
$ heroku addons:create heroku-postgresql:hobby-dev Creating heroku-postgresql:hobby-dev on ⬢ pure-peak-67829... free Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pg:copy Created postgresql-adjacent-12021 as DATABASE_URL Use heroku addons:docs heroku-postgresql to view documentation
This not only adds Postgres but also sets up the
DATABASE_URLwhich we had created earlier.
Let us now configure the
POOL_SIZENumber of connections that we have to the database
$ heroku config:set POOL_SIZE=18 Setting POOL_SIZE and restarting ⬢ pure-peak-67829... done, v5 POOL_SIZE: 18
Let us now set up the
SECRET_KEY_BASE(application secret) with :
$ mix phx.gen.secret frVmI4tvRSLJBSH09i657N5v1UwUfzOEyzD6cSDZIF8Nqhm4KQA6F3yUzrcu74hK $ heroku config:set SECRET_KEY_BASE="frVmI4tvRSLJBSH09i657N5v1UwUfzOEyzD6cSDZIF8Nqhm4KQA6F3yUzrcu74hK" Setting SECRET_KEY_BASE and restarting ⬢ pure-peak-67829... done, v3 SECRET_KEY_BASE: frVmI4tvRSLJBSH09i657N5v1UwUfzOEyzD6cSDZIF8Nqhm4KQA6F3yUzrcu74hK
Deploying To Heroku
Let us commit all our changes up until now.
$ git add . $ git commit -am "Preparing app for Heroku" $ git push heroku master
Once the deploy is done we can setup the database with
$ heroku run "POOL_SIZE=2 mix ecto.migrate" Running POOL_SIZE=2 mix ecto.migrate on ⬢ pure-peak-67829... up, run.4676 (Free) 12:10:30.567 [info] == Running DemoHerokuDeployment.Repo.Migrations.AddTest.change/0 forward 12:10:30.570 [info] == Migrated in 0.0s
NOTE the above command might fail if you don't have a migration.
And we can see our Phoenix application in the browser by typing.
$ heroku open
Drawbacks of Elixir on Heroku
Heroku has some drawbacks as well, viz:
- Heroku limits the number of connections.
- Since each dyno has it's own network interface (Router) clustering of applications becomes hard with standard or built-in tools and we will have to resort to Redis to achieve this.
- State of the application that is stored in memory will be lost every 24 Hours as each dyno will be restarted once in 24 Hours.
- It also does not allow SSH access thus making it impossible to use Elixir's Remote Shell which helps in debugging a lot of production scenarios.
If your requirement needs any of the above then it would be best to deploy your Elixir - Phoenix applications to a cloud platform such as EC2 or Digital Ocean etc. which we will cover in the subsequent parts of this tutorial series.