https://github.com/cogini/mix-deploy-example
Example Elixir app which uses mix_systemd and mix_deploy to deploy
https://github.com/cogini/mix-deploy-example
elixir-application elixir-lang
Last synced: 3 months ago
JSON representation
Example Elixir app which uses mix_systemd and mix_deploy to deploy
- Host: GitHub
- URL: https://github.com/cogini/mix-deploy-example
- Owner: cogini
- License: apache-2.0
- Created: 2019-04-24T11:55:17.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-10-06T03:50:29.000Z (over 1 year ago)
- Last Synced: 2024-12-26T23:26:25.735Z (5 months ago)
- Topics: elixir-application, elixir-lang
- Language: HTML
- Homepage:
- Size: 885 KB
- Stars: 42
- Watchers: 7
- Forks: 9
- Open Issues: 24
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# mix_deploy_example
This is a working example Elixir app which shows how to deploy using
[mix_deploy](https://github.com/cogini/mix_deploy) to a local system and via
[AWS CodeDeploy](https://aws.amazon.com/codedeploy/).`mix_deploy` generates scripts which are used to deploy your app using systemd
on a server. It includes scripts to set up the initial system, deploy
code and handle configuration during startup. It uses
[mix_systemd](https://github.com/cogini/mix_systemd) to generate systemd unit
files.# Deploying locally
These instructions show how to deploy an app to the same server you are building
on. That can be a $5/month [Digital Ocean](https://m.do.co/c/150575a88316) server.## Install build dependencies
Install Erlang, Elixir and Node.js from OS packages:
```shell
# Ubuntu
LANG=en_US.UTF-8 sudo bin/build-install-deps-ubuntu# CentOS
LANG=en_US.UTF-8 sudo bin/build-install-deps-centos
```or install using [ASDF](https://www.cogini.com/blog/using-asdf-with-elixir-and-phoenix/):
```shell
# Ubuntu
LANG=en_US.UTF-8 sudo bin/build-install-asdf-deps-ubuntu && bin/build-install-asdf-init# CentOS
LANG=en_US.UTF-8 sudo bin/build-install-asdf-deps-centos && bin/build-install-asdf-init
```We normally use ASDF, but compiling from source on a small server takes a while
and may run out of RAM unless you adjust the config.## Configure
This example loads environment vars from `/srv/mix-deploy-example/etc/environment`:
```elixir
config :mix_systemd,
# Run scripts before starting the app
exec_start_pre: [
# Run db migrations script /srv/mix-deploy-example/bin/deploy-migrate
[:deploy_dir, "/bin/deploy-migrate"],
],
dirs: [
# Create runtime temp dir /run/mix-deploy-example
:runtime,
],
env_files: [
# Load environment vars from /srv/mix-deploy-example/etc/environment
["-", :deploy_dir, "/etc/environment"],
],
env_vars: [
# Tell release scripts to use runtime directory for temp files
# Needed by config/releases.exs
["RELEASE_TMP=", :runtime_dir],
]config :mix_deploy,
# Generate these scripts from templates
templates: [
# systemctl wrappers
"start",
"stop",
"restart",
"enable",# System setup
"create-users",
"create-dirs",
"set-perms",# Local deploy
"init-local",
"copy-files",
"release",
"rollback",# Release commands
"set-env",
"remote-console",
"migrate",
],
# Match mix_systemd
env_files: [
["-", :deploy_dir, "/etc/environment"],
],
env_vars: [
# Tell release scripts to use runtime directory for temp files
["RELEASE_TMP=", :runtime_dir],
],
dirs: [
:runtime,
],
# Copy config/environment from project to /etc/mix-deploy-example/etc/environment
copy_files: [
%{
src: "config/environment",
dst: [:deploy_dir, "/etc/environment"],
user: "$DEPLOY_USER",
group: "$APP_GROUP",
mode: "640"
},
]
```Set up your production db password and `secret_key_base`, used by Phoenix to protect
session cookies.Generate `secret_key_base`:
```shell
mix phx.gen.secret 64
```Create a database using
[Digital Ocean's Managed Databases Service](https://www.cogini.com/blog/multiple-databases-with-digital-ocean-managed-databases-service/)
and get the database connection URL.Create the file `config/environment` with app secrets:
```shell
SECRET_KEY_BASE="EOdJB1T39E5Cdeebyc8naNrOO4HBoyfdzkDy2I8Cxiq4mLvIQ/0tK12AK1ahrV4y"
DATABASE_URL="ecto://doadmin:SECRET@db-postgresql-sfo2-xxxxx-do-user-yyyyyy-0.db.ondigitalocean.com:25060/defaultdb?ssl=true"
```Add `config/environment` to `.gitignore`.
`bin/deploy-copy-files` copies `config/environment` to `/srv/mix-deploy-example/environment/etc`.
`systemd` then loads it on startup, setting OS environment vars.Configure `config/releases.exs` to use `System.get_env/2` to read config from
the environment vars:```elixir
config :mix_deploy_example, MixDeployExampleWeb.Endpoint,
http: [:inet6, port: System.get_env("PORT") || 4000],
secret_key_base: System.get_env("SECRET_KEY_BASE"),
cache_static_manifest: "priv/static/cache_manifest.json"config :mix_deploy_example, MixDeployExample.Repo,
url: System.get_env("DATABASE_URL")
```## Build the system
```shell
MIX_ENV=prod bin/build
```In addition to the normal build stuff, that does the following:
```
mix systemd.init
MIX_ENV=prod mix systemd.generatemix deploy.init
MIX_ENV=prod mix deploy.generate
chmod +x bin/*
```Initialize the libraries, copying templates from `mix_systemd` and `mix_deploy`
package dirs to `rel/templates`, then generate files based on the config in
`config/prod.exs`:## Initialize the local system
Set up the local system for the app, creating users, directories, etc:
sudo bin/deploy-init-local
THat does the following:
```shell
bin/deploy-create-users
bin/deploy-create-dirscp bin/* /srv/mix-deploy-example/bin
bin/deploy-copy-files
bin/deploy-enable
```# Log out and log in again
The `bin/deploy-create-users` adds the deploy user to the group used by the
app. In order for that to take effect, you have to log out and log in again.## Build
Build the app and make a release:
```shell
MIX_ENV=prod bin/build
```## Deploy
Deploy the release to the local machine:
```shell
# Extract release to target directory, creating current symlink
bin/deploy-release# Restart the systemd unit
sudo bin/deploy-restart
```Check the status:
```shell
systemctl status mix-deploy-example
journalctl -f -u mix-deploy-example
```## Test
Test it by making a request to the server:
```shell
curl -v http://localhost:4000/
```If things aren't working right, you can roll back to the previous release:
```shell
bin/deploy-rollback
sudo bin/deploy-restart
```# Preparing an existing project for deployment
Following are the steps used to set up this repo. You can do the same to add
it to your own project.## Generate Phoenix project
```shell
mix phx.new mix_deploy_example
mix deps.get
cd assets && npm install && node node_modules/webpack/bin/webpack.js --mode development
```- Add `mix.lock` to git
- Add `package-lock.json` to git## Configure Elixir 1.9+ mix releases
Configure releases in `mix.exs`:
```elixir
defp releases do
[
prod: [
include_executables_for: [:unix],
steps: [:assemble, :tar]
],
]
end
```Configure `rel/env.sh.eex` and `rel/vm.args.eex` if necessary, e.g.
to [increase network ports](https://www.cogini.com/blog/tuning-tcp-ports-for-your-phoenix-app/).See [the docs](https://hexdocs.pm/mix/Mix.Tasks.Release.html) for more details.
## Install mix_deploy and mix_systemd
Add libraries to deps from Hex:
```elixir
{:mix_deploy, "~> 0.7"}
```Add `rel/templates` and `bin/deploy-*` to `.gitignore`.
## Copy build and utility scripts
Copy scripts from the `bin/` directory to the `bin/` directory of your project.
These scripts install the required dependencies:
- `build-install-asdf`
- `build-install-asdf-deps-centos`
- `build-install-asdf-deps-ubuntu`
- `build-install-asdf-init`
- `build-install-asdf-macos`
- `build-install-deps-centos`
- `build-install-deps-ubuntu`This script builds the app:
- `build`
This script verifies that the app is running correctly:
- `bin/validate-service`
## Configure Phoenix for OTP releases
Update `config/prod.exs` to run from release:
- Start Phoenix endpoints automatically
```elixir
config :phoenix, :serve_endpoints, true
```- Don't import `prod.secret.exs`
```elixir
`# import_config "prod.secret.exs"`
```## Configure mix_deploy and mix_systemd
Configure `mix_deploy` and `mix_systemd` in `config/prod.exs`.
## Configure ASDF
Create a `.tool-versions` file in the root of your project, describing the versions
of OTP, Elixir, and Node that you will be building with:```
erlang 22.2
elixir 1.9.4
nodejs 10.15.3
```## Configure for CodeDeploy
- Add `appspec.yml`
## Configure for CodeBuild
- Add `buildspec.yml`
## Add database migrations
- Add `lib/mix_deploy_example/release.ex` as described in
[Ecto migrations and custom commands](https://hexdocs.pm/phoenix/releases.html#ecto-migrations-and-custom-commands)## Add TOML config provider
- Add to `mix.exs`
```elixir
defp deps do
[
{:toml_config, "~> 0.1.0"}, # Mix releases
]
end
``````
defp releases do
[
aws: [
include_executables_for: [:unix],
config_providers: [
{TomlConfigProvider, path: "/etc/mix-deploy-example/config.toml"}
],
steps: [:assemble, :tar]
],
]
end
```## Add Ansible scripts
See `ansible` dir.
## Add Docker file
```shell
build -f build/docker/Dockerfile .
```