Elixir JavaScript Shell HTML CSS
Switch branches/tags
Clone or download
Latest commit c5c3800 Aug 9, 2017
Permalink
Failed to load latest commit information.
.circleci Configure deployment on CI Jul 26, 2017
.deliver Add explanation how to build app to generate a new sys.config file Jul 25, 2017
assets Generate new Phoenix 1.3.0-rc2 app Jul 24, 2017
bin Use proper app name phoenix_website Jul 25, 2017
config configure exactly which server to start per endpoint Jul 26, 2017
lib
priv Ensure the migrations directory is in git repo Jul 25, 2017
rel Revert "Don’t set explicitly list of applications for release" Jul 26, 2017
test Generate new Phoenix 1.3.0-rc2 app Jul 24, 2017
.gitignore
LICENSE Add MIT license for this project Jul 26, 2017
README.md Fix typo Aug 9, 2017
mix.exs Update to phoenix 1.3.0 Aug 1, 2017
mix.lock Update to phoenix 1.3.0 Aug 1, 2017

README.md

PhoenixWebsite

To start your Phoenix server:

  • Install dependencies with mix deps.get
  • Create and migrate your database with mix ecto.create && mix ecto.migrate
  • Install Node.js dependencies with cd assets && npm install
  • Start Phoenix endpoint with mix phx.server

Now you can visit localhost:4000 from your browser.

Deployment Overview

To build and deploy the application, we use edeliver with distillery build tool.

Here is the overview of the deployment process:

    -----------------------                                           ---------------------------
    |                     |                                           |                         |
    |                     | -------------- (1) build ---------------> |                         |
    |                     |                                           |       build server      |
    |                     | <------- (2) copy to release store ------ |                         |
    |                     |                                           |                         |
    |   control machine   |                                           ---------------------------
    |                     |
    |  (localhost /       |                                           ---------------------------
    |         Circle CI)  |                                           |                         |
    |                     | ------------- (3) deploy ---------------> |     target machine      |
    |                     |                                           |                         |
    |                     | ------ (4) restart & migrate -----------> |  (staging / production) |
    |                     |                                           |                         |
    -----------------------                                           ---------------------------

If (1) compiling and generating the release build was successful, then (2) the release is copied from the build server to the release store. The release store is the ./.deliver/releases directory on the control machine.

Deployment configuration

Here is the list of steps you need to follow to configure a fresh Phoenix Framework app in order compile it on Build Server and later deploy to App Server. You can find here relevant Ansible playbooks to provision Build Server and App Server.

Here you will find all code changes in this repo necessary to make deployment work.

  • Add distillery (release manager) in mix.exs and run $ mix deps.get.

    defp deps do
      [{:distillery, "~> 1.4", runtime: false}]
    end
  • Generate a new rel/config.exs file with $ mix release.init. You can learn more here if you like https://hexdocs.pm/distillery/getting-started.html

  • Add lib/phoenix_website/release_tasks.ex file to the repo and ensure the module name is relevant to your app PhoenixWebsite and the atoms mentioned in the code for it as well :phoenix_website.

  • Add rel/commands/seed.sh and set executable chmod for it $ chmod a+x rel/commands/seed.sh.

  • In rel/config.exs file we should add seed command with plugin LinkConfig.

    environment :prod do
      set include_erts: true
      set include_src: false
      set cookie: :"rm!z`joIlVak;]t$[Dyo$?D8Mu)/H,by9YjAQ/qIJ1(fS.]d(dI@Cu&{P>S%NI5J"
      # add below 2 lines
      set commands: [
        "seed": "rel/commands/seed.sh",
      ]
      # https://github.com/edeliver/edeliver/wiki/Use-per-host-configuration#linking-with-distillery
      plugin Releases.Plugin.LinkConfig
    end
  • Add edeliver (build and deploy Elixir app) in mix.exs and run $ mix deps.get.

    defp deps do
      [
        ...
        # add edeliver here
        {:edeliver, "~> 1.4.3"},
        {:distillery, "~> 1.4", runtime: false},
      ]
    end
  • Add in .gitignore the line .deliver/releases/.

  • In config/prod.exs ensure the # import_config "prod.secret.exs" is comment out. We don't want to load prod.secret.exs.

  • In config/prod.exs ensure the config :phoenix_website, PhoenixWebsiteWeb.Endpoint, server: true is uncommented.

  • Add at the very end of the config/prod.exs file:

    # NOTE: this should be commented because we don't use it
    # import_config "prod.secret.exs"
    
    # We don't want to use prod.secret.exs
    #
    # Instead we will compile the keys but with example values FILL_IN_HERE
    # that will be manually replaced later in /home/phoenix/phoenix_website/phoenix_website.config on the App Server.
    #
    # Thanks to that values won't be compiled into our release package.
    # In result we will be able to deploy compiled release package to multiple environments like staging/production.
    # (You don't have to compile app separatly for the staging and production as it was with using prod.secret.exs approach)
    config :phoenix_website, PhoenixWebsiteWeb.Endpoint,
      secret_key_base: "FILL_IN_HERE",
      # we use hardcoded port and it's set in Ansible playbooks
      # roles/phoenix-app/0.0.1/templates/nginx.j2
      http: [port: 8888]
    
    # Configure your database
    config :phoenix_website, PhoenixWebsite.Repo,
      adapter: Ecto.Adapters.Postgres,
      username: "FILL_IN_HERE",
      password: "FILL_IN_HERE",
      database: "FILL_IN_HERE",
      pool_size: 15
    
    config :phoenix_website,
      # Nodes connecting to each other are required to prove that they possess a shared secret, called a "cookie". This is
      # mostly aimed at ensuring that different Erlang clusters on the same network don't accidentally merge. All Erlang
      # nodes in a cluster trust each other completely. Any node in the cluster can run any code on any of the other nodes.
      # This must be atom hence colon sign before value.
      # You can generate new erlang_magic_cookie with: `mix phx.gen.secret`.
      erlang_magic_cookie: :"FILL_IN_HERE"
  • Create bin/deploy with executable chmod $ chmod a+x deploy and update there HOST to your staging/production servers.

  • Create bin/restart with executable chmod $ chmod a+x deploy

Staging and production environments

Machines

  • Build server:
    • domain: elixir-build-server.lunarlogic.io
  • Staging:
    • domain: phoenix-website-staging.lunarlogic.io (this server was not provisioned. It's just example)
  • Production:

Credentials

Ensure you have /home/phoenix/phoenix_website/phoenix_website.config file on the staging and production hosts.

🎓 We use different configurations on different deploy hosts, as described here (we link the sys.config by setting the env LINK_SYS_CONFIG in .deliver/config).

If you change configuration structure in config/config.exs or config/prod.exs, then you need to generate new phoenix_website.config file. To do so, you need to compile the application and in the compiled package you will find $DELIVER_TO/$APP/releases/$VERSION/sys.config template with FILL_IN_HERE instead of credentials. Use the template file, add proper credentials in it and then upload to staging and production hosts.

Please read comment above the line LINK_SYS_CONFIG="$DELIVER_TO/$APP/$APP.config" in the file .deliver/config to see what steps to follow to achieave that.

You can also find here compiled config phoenix_website.config and you could use it exactly for this application. Remember to change FILL_IN_HERE to your credentials.

[{sasl,[{errlog_type,error}]},
 {logger,
     [{console,
          [{format,<<"$time $metadata[$level] $message\n">>},
           {metadata,[request_id]}]},
      {level,info}]},
 {phoenix_website,
     [{ecto_repos,['Elixir.PhoenixWebsite.Repo']},
      {'Elixir.PhoenixWebsiteWeb.Endpoint',
          [{render_errors,
               [{view,'Elixir.PhoenixWebsiteWeb.ErrorView'},
                {accepts,[<<"html">>,<<"json">>]}]},
           {pubsub,
               [{name,'Elixir.PhoenixWebsite.PubSub'},
                {adapter,'Elixir.Phoenix.PubSub.PG2'}]},
           {load_from_system_env,false},
           {url,[{host,<<"example.com">>},{port,80}]},
           {cache_static_manifest,<<"priv/static/cache_manifest.json">>},
           {server,true},
           {secret_key_base,<<"FILL_IN_HERE">>},
           {http,[{port,8888}]}]},
      {'Elixir.PhoenixWebsite.Repo',
          [{adapter,'Elixir.Ecto.Adapters.Postgres'},
           {username,<<"FILL_IN_HERE">>},
           {password,<<"FILL_IN_HERE">>},
           {database,<<"FILL_IN_HERE">>},
           {pool_size,15}]}]}].

Performing the deployment

The app is deployed to production by CircleCI from master branch when tests are green. You can see deployment configuration in .circleci/config.yml.

You can see example builds here:

CircleCI

Deploying manually
BRANCH=master TARGET_SERVER=staging bin/deploy      # deploy to staging
BRANCH=master TARGET_SERVER=production bin/deploy   # deploy to production

Tips

  • $ mix phx.gen.secret can generate secret that can be used for erlang_magic_cookie or for secret_key_base in config/prod.exs.

  • In order to run seeds do:

    ssh phoenix@phoenix-website.lunarlogic.io /home/phoenix/phoenix_website/bin/phoenix_website command Elixir.PhoenixWebsite.ReleaseTasks seed
  • How to check logs on App Server

    $ ssh admin@phoenix-website.lunarlogic.io
    
    # logs for systemd phoenix_website service
    $ sudo journalctl -u phoenix_website
    
    # check nginx errors log
    $ sudo tail -f /var/log/nginx/error.log
  • How to start Phoenix Console:

    $ ssh admin@phoenix-website.lunarlogic.io
    $ sudo su phoenix
    $ cd /home/phoenix/phoenix_website
    $ MIX_ENV=prod bin/phoenix_website console
  • How to check logs for the Phoenix service:

    # -f will tail the logs
    $ sudo journalctl -f -u phoenix_website

Learn more