Subscribe to access all episodes. View plans →

#68: Deploying with Gigalixir

Published September 4, 2018

This episode has been revised - watch here.

Elixir 1.5

Phoenix 1.3

Distillery 1.5.3

Gigalixir CLI 1.0.1

View source on GitHub


In this episode we’ll see how you can easily deploy an Elixir application using Gigalixir. Any language that has a buildpack can run on Gigalixir, but it was originally built for Elixir and supports the great features of Elixir like hot upgrades, distributed clustering, and a production observer. And if you’re using the free tier on Gigalixir there’s no daily restart like on the Heroku free tier. This allows for using things like Erlang Term Storage, or ETS, for more than just holding ephemeral state.

Let’s get started. The first thing we’ll want to do is create an account on on Gigalixir. I’ll create an account through their site. There’s also an option to signup using their CLI too.

We’ll create an account. Then a confirmation email is sent. Once we receive it we’re taken back to the site and prompted to sign in. Great, we now that we have an account withe Gigalixir, we need an app to deploy.

Let’s keep it simple and create a new Phoenix application. We’ll call our new application teacher and we’ll install the dependencies.

Then let’s move into the new directory and create our database.

$ mix phx.new teacher
* creating teacher/config/config.exs
* creating teacher/config/dev.exs
* creating teacher/config/prod.exs
...

We are all set! Go into your application by running:

    $ cd teacher

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

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

    $ iex -S mix phx.server

$ cd teacher
$ mix ecto.create
The database for Teacher.Repo has been created
$ mix phx.server
[info] Running TeacherWeb.Endpoint with Cowboy using http://0.0.0.0:4000

And let’s start our application just to make sure everything is working. Great, we see the familiar “Welcome to Phoenix!” page.

Ok, now that we know our app is working locally, let’s get it ready to be deployed. While Gigalixir supports deploying with Mix, you do lose out on features like clustering and hot upgrades. Instead let’s use distillery.

Let’s head over to Hex and grab distillery. Then we’ll open our “Mixfile” and add it to our list of dependencies and we’ll set the runtime to false.

mix.exs

...
defp deps do
  [
  ...
  {:distillery, "~> 1.5", runtime: false},
  ...
  ]
end
...

Then we’ll go to the command line and install our new dependency.

Now we can run $ mix release.init - this will create a rel directory that has our release configuration file. We’ll use the defaults for this example, but you’ll want to review this in order to configure things like your release version and applications included in the release.

$ mix deps.get
...
New:
  distillery 1.5.3
* Getting distillery (Hex package)
$ mix release.init
...
An example config file has been placed in rel/config.exs, review it,
make edits as needed/desired, and then run `mix release` to build the release

Gigalixir uses buildbacks to help build and compile releases. We’ll need to create a .buildpacks file, this is where we’ll include the ones we want to use.

Then I’ll paste in the buildpacks we need. Our first buildpack gigalixir-buildpack-clean-cache cleans our cache. Then heroku-buildpack-elixir sets our version of Elixir and runs mix compile. heroku-buildpack-phoenix-static is optional, if you don’t have any static assets you don’t need to include it. Finally gigalixir-buildpack-distillery runs mix release.

.buildpacks

https://github.com/gigalixir/gigalixir-buildpack-clean-cache.git
https://github.com/HashNuke/heroku-buildpack-elixir
https://github.com/gjaldon/heroku-buildpack-phoenix-static
https://github.com/gigalixir/gigalixir-buildpack-distillery.git

Now let’s configure our app. We wont use the prod.secret.exs file so let’s go ahead and delete it.

$ rm config/prod.secret.exs

Then we’ll open our prod.exs and let’s first configure our TeacherWeb.Endpoint. We’ll need to add server: true and then we’ll set the secret_key_base.

Gigalixir will create a SECRET_KEY_BASE environment variable for us so we just need to include it here. We’ll use ${SECRET_KEY_BASE} in order to access it.

Now we’ll need to configure our Teacher.Repo. I’ll go ahead and paste ours in here. You can see that we’re using Postgres. And the database URL is also set by Gigalixir, so we’re just including the environment variable for it here. Also, the pool size is set to 1. This is because we’re using the free tier of Gigalixir, which only allows 1 connection.

Let’s not forget to go to the bottom and comment out our import of prod.secret.exs since we’re not using it.

config/prod.exs

...

config :teacher, TeacherWeb.Endpoint,
  load_from_system_env: true,
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json",
  server: true,
  secret_key_base: "${SECRET_KEY_BASE}"

config :teacher, Teacher.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: "${DATABASE_URL}",
  database: "",
  ssl: true,
  pool_size: 1
  
...

# import_config "prod.secret.exs"

Our app is almost ready to be deployed. Since Gigalixir uses git to deploy our app we need to create a new Git repo for our app.

We’ll go to the command line and create a new Git repo with $ git init, then let’s add our project and commit our changes.

$ git init
$ git add .
$ git commit -m "deploying to gigalixir"

Now our project is ready to be deployed. I’ll clear the screen and we can deploy our project from the command line using Gigalixir’s command line interface or CLI.

The CLI requires pip in order to install, so you’ll need to have it installed first. If you don’t have pip installed, here are are some instructions to install pip.

With pip installed we can install the Gigalixir CLI.

$ pip install gigalixir
...
Installing collected packages: gigalixir
Successfully installed gigalixir-1.0.1

Now that we have the Gigalixir CLI installed, let’s create an app on Gigalixir and deploy it. First we’ll want to login to our Gigalixir account. Then we’ll create a new app in Gigalixir. And once it’s created, Gigalixir returns a unique name for our app. We can double-check that it was created by listing our apps. Great, we see it along with some information about it.

We see cloud “gcp” - this is because by default Gigalixir runs our app on the Google Cloud Platform. There’s also the option to use AWS, which you can set when you create your app. The region, number of replicas - ours is zero right now because we haven’t deployed anything. The size and our app’s name.

Alright, now lets deploy our app. When we ran $ gigalixir apps:create Gigalixir added a git remote named “gigalixir”. We’ll run $ git push gigalixir master this pushes our code to Gigalixir and triggers a deploy. Once finished, it will take a minute for our new instance to roll out and pass health checks.

We can check on the status of our app with the $ gigalixir ps command. And great, it looks like our app is running.

$ gigalixir login
Email: hello@elixircasts.io
$ gigalixir create
Created app: boring-tart-kob.
Set git remote: gigalixir.
boring-tart-kob
$ gigalixir apps
[
  {
    "cloud": "gcp",
    "region": "us-central1",
    "replicas": 1,
    "size": 0.3,
    "unique_name": "boring-tart-kob"
  }
]
$ git push gigalixir master
Counting objects: 85, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (75/75), done.
...
remote: curl https://boring-tart-kob.gigalixirapp.com/
remote: Updated property [core/account].
To https://git.gigalixir.com/boring-tart-kob.git/
$ gigalixir ps
{
  "cloud": "gcp",
  "pods": [
    {
      "containerStatuses": [
        {
          "lastState": {},
          "ready": true,
          "state": {
            "running": {
              "startedAt": "2018-08-10T21:15:42Z"
            }
          }
        }
      ],
      "deletionTimestamp": null,
      "name": "boring-tart-kob-3412268703-d710l",
      "phase": "Running"
    }
  ],
  "region": "us-central1",
  "replicas_desired": 1,
  "replicas_running": 1,
  "size": 0.3,
  "unique_name": "boring-tart-kob"
}

Let’s go to the URL that was printed in our logs - <our Gigalixir app name>.gigalixirapp.com - and great, we see our app is deployed!

Now that we have a deployed app, let’s make some changes and give our site a simple blog. We’ll run the Phoenix HTML generator to create our blog with a posts table and we’ll give our posts a title and a body.

$ mix phx.gen.html Blog Post posts title body
* creating lib/teacher_web/controllers/post_controller.ex
* creating lib/teacher_web/templates/post/edit.html.eex
* creating lib/teacher_web/templates/post/form.html.eex
* creating lib/teacher_web/templates/post/index.html.eex
* creating lib/teacher_web/templates/post/new.html.eex
* creating lib/teacher_web/templates/post/show.html.eex
* creating lib/teacher_web/views/post_view.ex
* creating test/teacher_web/controllers/post_controller_test.exs
* creating lib/teacher/blog/post.ex
* creating priv/repo/migrations/{timestamp}_create_posts.exs
* creating lib/teacher/blog/blog.ex
* injecting lib/teacher/blog/blog.ex
* creating test/teacher/blog/blog_test.exs
* injecting test/teacher/blog/blog_test.exs

Add the resource to your browser scope in lib/teacher_web/router.ex:

    resources "/posts", PostController


Remember to update your repository by running migrations:

    $ mix ecto.migrate

Then let’s open our router and add our new resource.

lib/teacher_web/router.ex

...
scope "/", TeacherWeb do
  pipe_through :browser # Use the default browser stack

  resources "/posts", PostController
  get "/", PageController, :index
end
...

Let’s also add a link to our blog so we can access it from the UI.

Template path: lib/teacher_web/templates/layout/app.html.eex

...
<li>
  <%= link "Blog", to: post_path(@conn, :index) %>
</li>
...

Then we’ll need to migrate our database.

$ mix ecto.migrate
Compiling 17 files (.ex)
Generated teacher app
[info] == Running Teacher.Repo.Migrations.CreatePosts.change/0 forward
[info] create table posts
[info] == Migrated in 0.0s

And again let’s start up our server to make sure everything is working locally.

$ mix phx.server
[info] Running TeacherWeb.Endpoint with Cowboy using http://0.0.0.0:4000

We see our new “Blog” link. And we’ll create a test post. Great, everything is working locally, so let’s commit our changes.

$ git add .
$ git commit -m "our blog"
14 files changed, 437 insertions(+), 1 deletion(-)
 create mode 100644 lib/teacher/blog/blog.ex
 create mode 100644 lib/teacher/blog/post.ex
 create mode 100644 lib/teacher_web/controllers/post_controller.ex
 create mode 100644 lib/teacher_web/templates/post/edit.html.eex
 create mode 100644 lib/teacher_web/templates/post/form.html.eex
 create mode 100644 lib/teacher_web/templates/post/index.html.eex
 create mode 100644 lib/teacher_web/templates/post/new.html.eex
 create mode 100644 lib/teacher_web/templates/post/show.html.eex
 create mode 100644 lib/teacher_web/views/post_view.ex
 create mode 100644 priv/repo/migrations/{timestamp}_create_posts.exs
 create mode 100644 test/teacher/blog/blog_test.exs

Then let’s redeploy our app to Gigalixir.

$ git push gigalixir master
...

With our app redeployed, let’s go to the browser - and refresh the page. We see our new link. But, when we click it we get an error. This is because when we deployed our app, we never created database.

If we go to the command line we can get the databases for our app with $ gigalixir pg and nothing is there. Let’s fix that. Since we used distillery, we wont be able to use mix commands to create and migrate our database. Not to worry, Gigalixir provides commands to help us.

We’ll run $ gigalixir pg:create and once our database is created we can run our migrations with $ gigalixir ps:migrate.

However, when we run our database migrations we get a message that we don’t have any ssh keys added. Let’s add ours with $ gigalixir account:ssh_keys:add. Then with our SSH key added we should be able to run our database migration.

$ gigalixir pg
[]
$ gigalixir pg:create
Creating new database.
Please give us a few minutes provision the new database.
$ gigalixir ps:migrate
You don't have any ssh keys yet. See `gigalixir add_ssh_key --help`
$ gigalixir account:ssh_keys:add "$(cat ~/.ssh/id_rsa.pub)"
Please allow a few minutes for the SSH key to propagate to your run containers.
$ gigalixir ps:migrate
Migration succeeded.

With our new database ready, let’s go back to the browser. And we’ll refresh the page. And great, it worked and we see our new blog. Let’s create a post just to be sure everything is working. Perfect, our app is updated and our blog is working great.

Now let’s look at some other features of Gigalixir. Let’s start by looking at how to set environment variables. We can set one from the command line with the $ gigalixir config:set task. Let’s call our new environment variable SOME_VAR and we’ll give it the value of hello.

$ gigalixir config:set SOME_VAR=hello
SOME_VAR: hello
Setting SOME_VAR and restarting boring-tart-kob

It looks like it was set, but in order to test it, let’s use another feature of Gigalixir - the ability to start a remote console.

We’ll use the task $ gigalixir ps:remote_console. Once it starts up we can get our variable with System.get_env passing in our environment variable name. And great “hello” was returned.

$ gigalixir ps:remote_console
Interactive Elixir (1.5.0) - press Ctrl+C to exit (type h() ENTER for help)
> System.get_env("SOME_VAR")
"hello"

Now let’s look at one last feature of Gigalixir, the remote observer. We can start the observer with $ gigalixir ps:observer. While it’s starting up, Gigalixir prints instructions on how to connect to our production node. Let’s use these to connect to ours.

We’ll go to the observer. And click on “Nodes”, then “Connect Node”. We’ll paste in the node that we want to connect to. And great - we’re connected to our production node. We can now view data about it, like our “Load Charts”. And our applications. If we click on our teacher application, we can see all our different processes running.

$ gigalixir ps:observer
============
Instructions
============
In the 'Node' menu, click 'Connect Node'
enter: boring-tart-kob@10.1.90.206
and press OK.

As you can see there’s a lot to love about Gigalixir. While we covered a lot of the great features of Gigalixir, one thing we didn’t go over is node clustering. Gigalixir supports node clustering and once it’s working things like distributed Phoenix channels will just work without any additional setup.

© 2024 HEXMONSTER LLC