Setting Up A Vagrant Environment For Elixir And Phoenix (part 1)

Posted on Tue 13 June 2017 in Web Development

Introduction

In this series of articles, we will go through a step by step guide to setting up a vagrant environment for Elixir web development using the Phoenix framework, and then using docker to wrap the web application in a container. Finally, we will deploy the container to Amazon Web Services (AWS) using Kubernetes.

Installing Vagrant

Vagrant is a tool to help manage virtual machine environments. This allows a team to ensure that their development environments are consistent - using the same configuration and same dependencies, and ideally closely mirroring the production environment (i.e. your developers are working on macs, but the production app runs in a Linux environment). Vagrant also allows us to easily have a separate virtual machine for each project (for example, if you're a web development consultancy working on different projects for different clients).

Creating a Virtual Machine

For the purposes of this series of tutorials, we will be using an Ubuntu environment, but the general process should be the same for other environments.

First of all, create a directory for your vagrant environment:

mkdir VagrantEnv
cd VagrantEnv
vagrant init ubuntu/xenial64

I modify the Vagrantfile so that there is a shared folder between my host and my guest. In addition, since I will be setting up a Phoenix app, I also set up port forwarding:

Shared folder:

config.vm.synced_folder "./src", "/home/ubuntu/phoenix_app"

Port forwarding:

config.vm.network "forwarded_port", guest: 4000, host: 8080
vagrant up

You should see some output similar to the following:

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'ubuntu/xenial64' is up to date...
==> default: A newer version of the box 'ubuntu/xenial64' is available! You currently
==> default: have version '20170523.1.0'. The latest is version '20170610.0.0'. Run
==> default: `vagrant box update` to update.
==> default: Clearing any previously set forwarded ports...
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...

You can end the vagrant session by first exiting the vagrant environment with

exit
logout
Connection to 127.0.0.1 closed.

Then, you can halt the virtual machine:

vagrant halt

and you should see the following:

==> default: Attempting graceful shutdown of VM...

Installing Elixir

The above steps installed Vagrant and checked that an Ubuntu VM was set up correctly. Now, we're going to install Elixir.

Elixir is a language that runs on the Erlang VM. It's a functional language with dynamic typing and the syntax is not too dissimilar from Ruby.

Start up the Vagrant environment as above, and then to install Elixir, run the following:

wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb

This command adds the Erlang Solutions repository to the list of repositories used by apt-get. After running the above command, run

sudo apt-get update

In the output of the above command, you should hopefully see some references to the Erlang Solutions repositories:

...omitted...
Get:11 http://packages.erlang-solutions.com/debian xenial Release [2,537 B]
Get:13 http://security.ubuntu.com/ubuntu xenial-security/multiverse Sources [1,144 B]
Get:15 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages [304 kB]
Get:14 http://packages.erlang-solutions.com/debian xenial Release.gpg [836 B]
Get:16 http://packages.erlang-solutions.com/debian xenial/contrib amd64 Packages [42.8 kB]
...omitted...

Run the following command to install Erlang:

sudo apt-get install esl-erlang

Once that's finished, confirm that Erlang is correctly installed by starting the Erlang interpreter:

ubuntu@ubuntu-xenial:~$ erl
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V9.0  (abort with ^G)
1> 1 + 1.
2
2> io:put_chars("Hello World\n").
Hello World
ok
3> q().
ok
4> ubuntu@ubuntu-xenial:~$

Once Erlang has been installed correctly, you can install Elixir by running:

sudo apt-get install elixir

Finally, verify that the Elixir interpreter was installed correctly:

ubuntu@ubuntu-xenial:~$ iex
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.4.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 1 + 1
2
iex(2)> IO.puts "Hello World"
Hello World
:ok
iex(3)> :init.stop
:ok
iex(4)> ubuntu@ubuntu-xenial:~$

Installing Phoenix

Now that Elixir has been installed correctly, it's time to install Phoenix.

For full installation details, you can visit: http://www.phoenixframework.org/docs/installation

First, I installed Hex in order to manage the packages / dependencies for my Phoenix apps:

mix local.hex
Are you sure you want to install archive "https://repo.hex.pm/installs/1.4.0/hex-0.16.0.ez"? [Yn]
* creating .mix/archives/hex-0.16.0

Install Phoenix with the following command:

mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
Are you sure you want to install archive "https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez"? [Yn]
* creating .mix/archives/phoenix_new

Install inotify-tools - this is a library that acts as an interface to inotify. This is used by Phoenix for code reloading.

sudo apt-get install inotify-tools

PostgreSQL can be installed in a similar way:

sudo apt-get install postgresql postgresql-contrib

Once that was installed, I logged in to PostgreSQL, and set a password for the postgres user:

sudo -u postgres psql
\password postgres
<entered the password>
<confirmed the password>
\q

Lastly, install NodeJS. Whilst NodeJS is an optional dependency for Phoenix, it's useful enough to install by default (for myself anyway, your requirements may vary, so if you know that you don't need NodeJS, feel free to skip this step).

I get the version from Nodesource, so the commands I use are:

curl --silent https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
VERSION=node_6.x
DISTRO="$(lsb_release -s -c)"
echo "deb https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee /etc/apt/sources.list.d/nodesource.list
echo "deb-src https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee -a /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install nodejs

These commands were taken from https://github.com/nodesource/distributions

Set Up A Phoenix Project

Now that the dependencies have been installed, it's time to start a new Phoenix project:

mix phoenix.new phoenix_app

When prompted, I just accept the defaults.

Once that's completed, you should hopefully see output similar to:

We are all set! Run your Phoenix application:

    $ cd phoenix_app
    $ mix phoenix.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phoenix.server

Before moving on, configure your database in config/dev.exs and run:

    $ mix ecto.create

So from the project directory, I ran:

mix ecto.create

There should be a lot of output as the source files are compiled. Once that's completed, you can start the server up with:

mix phoenix.server

Then after a few moments, if you switch away from your Vagrant environment and visit http://localhost:8080 from your browser (please note, if you used a different host port in your Vagrant file, the port number will be different), you should see the default Phoenix project page.

Conclusion

Congratulations, Elixir and Phoenix should now be set up in a Vagrant environment. In the next article, we will go through the process of dockerizing your Phoenix application.