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.
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:
config.vm.synced_folder "./src", "/home/ubuntu/phoenix_app"
config.vm.network "forwarded_port", guest: 4000, host: 8080
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
logout Connection to 127.0.0.1 closed.
Then, you can halt the virtual machine:
and you should see the following:
==> default: Attempting graceful shutdown of VM...
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:~$
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:
There should be a lot of output as the source files are compiled. Once that's completed, you can start the server up with:
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.
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.