oliveiragahenrique

oliveiragahenrique

Mix release - CI / CD Pipeline suggestions

I’m studying elixir for a while and never had a chance to use it in production on my day to day job.

Recently I’ve decided to put one of my phoenix side projects in “production”, by hosting a server a and config all the CI/CD on Github Actions to see how it works with elixir.

I’ve ended up with a pipeline such as this:

name: Server Deploy SDX
on:
  push:
    branches: ['sdx']
  pull_request:
    branches: ['sdx']

jobs:
  CI-CD:
    runs-on: ubuntu-latest
    env:
      MIX_ENV: test
      DATABASE_URL_SDX: ${{ secrets.DATABASE_URL_SDX }}
      SECRET_KEY_BASE_SDX: ${{ secrets.SECRET_KEY_BASE_SDX }}
      PORT_SDX: 7001

    services:
      db:
        image: postgres:12.8
        ports: ['5432:5432']
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v2
      - name: Set up Elixir
        uses: erlef/setup-elixir@885971a72ed1f9240973bd92ab57af8c1aa68f24
        with:
          elixir-version: '1.12.2'
          otp-version: '24.0'

      - name: Restore Dependencies Cache
        uses: actions/cache@v2
        with:
          path: deps
          key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
          restore-keys: ${{ runner.os }}-mix-

      - name: Install dependencies
        run: mix deps.get

      - name: Run Tests
        run: mix test

      - name: Run Migrations SDX
        run: MIX_ENV=sdx mix ecto.migrate

      - name: Build SDX Release
        run: MIX_ENV=sdx mix release

      - name: Install SSH key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.APPLICATION_SERVER_SSH_KEY_SDX }}
          known_hosts: 'to be defined on next step'

      - name: Add Known Hosts
        run: ssh-keyscan -H ${{ secrets.APPLICATION_SERVER_HOST_SDX }} >> ~/.ssh/known_hosts

      - name: Rename Old Build Folder
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.APPLICATION_SERVER_HOST_SDX }}
          username: ${{ secrets.APPLICATION_SERVER_SSH_USER_SDX }}
          key: ${{ secrets.APPLICATION_SERVER_SSH_KEY_SDX }}
          script_stop: true
          script: mv _build _build_old
      
      - name: Stop Old Application
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.APPLICATION_SERVER_HOST_SDX }}
          username: ${{ secrets.APPLICATION_SERVER_SSH_USER_SDX }}
          key: ${{ secrets.APPLICATION_SERVER_SSH_KEY_SDX }}
          script_stop: true
          script: _build_old/sdx/rel/alfred_server/bin/alfred_server stop

      - name: Deploy Release with RSYNC 
        run: rsync -avz ./_build ${{ secrets.APPLICATION_SERVER_SSH_USER_SDX }}@${{ secrets.APPLICATION_SERVER_HOST_SDX }}:~

      - name: Start Application
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.APPLICATION_SERVER_HOST_SDX }}
          username: ${{ secrets.APPLICATION_SERVER_SSH_USER_SDX }}
          key: ${{ secrets.APPLICATION_SERVER_SSH_KEY_SDX }}
          script_stop: true
          script: echo ${{ secrets.APPLICATION_SERVER_USER_PWD_SDX }} | sudo -S _build/sdx/rel/alfred_server/bin/alfred_server daemon

      - name: Remove Old Build Folder
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.APPLICATION_SERVER_HOST_SDX }}
          username: ${{ secrets.APPLICATION_SERVER_SSH_USER_SDX }}
          key: ${{ secrets.APPLICATION_SERVER_SSH_KEY_SDX }}
          script_stop: true
          script: echo ${{ secrets.APPLICATION_SERVER_USER_PWD_SDX }} | sudo -S rm -rf ./_build_old

I’m not experienced with Github Actions stuff also, so I’m happy to hear some suggestions about it too (for instance how to parallelize mix test, build and migrations without having to setup elixir for every job! Did not figure it out yet). :smiley:

My questions are:

1 - Between the steps Rename Old Build Folder and Stop Old Application I have my application running but the original _build folder no longer exists. Can something goes wrong between these 2 steps? Should I stop before renaning? Nor renaning at all?

Something I’ve noticed is that if I run the server and rename the build folder, I can no long access it via the mix release remote command. I think it happens because the iex process cannot find the log files anymore but I’m not sure.

2 - Thinking about minimizing downtime, my first approach was to only run Stop Old Application just before Start Application, and this approach worked when there is no traffic in the application. But when the application has traffic for some reason the new release isn’t started and I don’t know why.

3 - How is your standard CI / CD pipeline for elixir projects? Are they similar to my approach? What we do similar? What we do different?

Thanks very much guys! :smiley:

Most Liked

cpgo

cpgo

I used to run a pipeline on circleci that ran the build and tests in parallel but only ran the deployment step if both the test and build steps passed. Its a good way to shave off a few minutes/seconds of deployment time.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement