Мы уже обсуждали сборку и развёртывание приложений Elixir(перев: с помощью exrm): как осуществлять миграции поверх релиза или как работать с переменными среды. Пришло время открыть для себя ещё один инструмент, который поможет развёртывать Elixir приложения.


Практика развёртывания Elixir приложений и дальнейшее отслеживание их работы на нодах с помощью Exrm позволяет нам чувствовать себя гораздо увереннее в вопросах управления релизами в production. Однако возникает следующий вопрос: как управлять самим процессом развёртывания? Конечно, мы можем воспользоваться Capistrano, особенно если в мир Elixir мы пришли из Rails. Но посмотрим на цитату из Edeliver README:


edeliver основан на доставке и предоставляет bash-скрипт для сборки и развёртывания Elixir и Erlang приложений, а так же позволяет совершать "горячее" обновление кода.

Пытаться организовать весь процесс развёртывания вручную — это жёсткая головная боль с кучей повторяющегося кода. А вот использование Edeliver для развёртывания оказалось очень простым с первой же попытки! В конце концов, весь процесс развёртывания уместился в один меленький bash-скрипт:


#!/bin/bash -ex

BRANCH=${1:-master};

mix edeliver build release --branch=BRANCH --verbose
mix edeliver deploy release to production --verbose
mix edeliver start production --verbose
mix edeliver migrate production up --verbose

Скорее всего Вам придётся подкрутить этот скрипт под собственные нужды. Мы используем его только для развёртывания в production, но Вы так же можете использовать его и для staging развёртываний. Описание того, как всё это работает — под катом.


Как это работает


Как мы видели в README, Edeliver работает в основном с помощью bash-скриптов. Команды Mix исполняются с помощью Elixir, но фактически они запускают bash-скрипты. Некоторые инструкции терминала выполняются локально: они создают новые инструкции, которые будут выполнятся на удалённых серверах с через RPC.
Давайте углубимся в некоторые аспекты работы библиотеки.


Окружение


Edeliver представляет собой отличную возможность запускать и доставлять релизы в различные окружения. Концепция окружений проста: build, stage и production. Тут всё понятно, разве что некоторые нюансы есть у окружения build.


Для того, чтобы релиз благополучно запускался на рабочем сервере, он должен быть собран на машине с такой же архитектурой. Всё дело в том, что Edeliver использует Exrm для сборки релизов. Exrm будет использовать свои NIFы (это функции, написанные на C для Erlang), которые могут различаться в зависимости от архитектуры машины, поэтому, к примеру, релиз собранный на OSX может не работать на Linux. Вы можете почитать подробнее об этом вот в это пичальке Phoenix, где люди обсуждают проблемы кросс-компиляции и всякие другие выкидоны Exrm.


Короче говоря, чтобы использовать build окружение на нашей машине разработчика, у неё должна быть такая же архитектура как и на staging и production машинах. Иначе работать не будет.


Для конфигурации наших окружений, нужно создать папку .deliver в нашем проекте и добавить конфигурационный файл. Посмотрим, что авторы Edeliver советуют нам в него записать:


#!/usr/bin/env bash

APP="your-erlang-app" # name of your release

BUILD_HOST="build-system.acme.org" # host where to build the release
BUILD_USER="build" # local user at build host
BUILD_AT="/tmp/erlang/my-app/builds" # build directory on build host

STAGING_HOSTS="test1.acme.org test2.acme.org" # staging / test hosts separated by space
STAGING_USER="test" # local user at staging hosts
TEST_AT="/test/my-erlang-app" # deploy directory on staging hosts. default is DELIVER_TO

PRODUCTION_HOSTS="deploy1.acme.org deploy2.acme.org" # deploy / production hosts separated by space
PRODUCTION_USER="production" # local user at deploy hosts
DELIVER_TO="/opt/my-erlang-app" # deploy directory on production hosts

В общем всё просто и прозрачно, надо только удостовериться в наличии ssh доступа ко всем перечисленным сервакам. Дополнительной фичей является то, что как уже горилось ранее, вполне реально запулить релизы на несколько серверов.


Как мне добавить дополнительные задания для моих процессов развёртывания?


Edeliver делает по стандарту только общие штуки для всех Elixir или Erlang приложений. Но если, к примеру, мы используем Phoenix, нам нужно запустить ещё парочку команд до того, как мы сгенерируем релиз. Самые важные — это


$ branch build --production

и


$ mix phoenix.digest

чтобы статика работала в наших релизах.


Для этого определим хук в нашем .deliver/config файле:


pre_erlang_clean_compile() {
  status "Preparing assets with: brunch build and phoenix.digest"
  __sync_remote "
    # runs the commands on the build host
    [ -f ~/.profile ] && source ~/.profile # load profile (optional)

    # fail if any command fails (recommended)
    set -e

    # enter the build directory on the build host (required)
    cd '$BUILD_AT'

    mkdir -p priv/static # required by the phoenix.digest task

    # installing npm dependencies
    npm install

    # building brunch
    brunch build --production

    # run your custom task
    APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD phoenix.digest $SILENCE
  "
} 

Код вверху был нагло выдран из документации к Edeliver, которая объясняет как работать с хуками, и почему-то сразу заработал.


Что там насчёт моих переменных окружения?


Мы уже рассказывали как правильно себя вести с переменными окружения так, чтобы не экспортировать их в build окружение вот тут, и это всё ещё работает! Впрочем, ещё кое-что надо иметь в виду.


Чтобы можно было менять окружения, необходимо добавить RELX_REPLACE_OS_VARS=true перед нашей командой запуска. Но это невозможно в Edeliver, потому что первая команда выполняется локально:


$ mix edeliver start production

Поэтому возможное решение — экспортировать RELX_REPLACE_OS_VARS в ваше production окружение.


Перед стартом


Похоже, что Edeliver — классная штука для управления релизами и вообще процессом развёртывания. Мне она показалась очень простой в использовании. В этой статье я никак не вникал во внутренности её работы, так что прочитайте README и всякие доки — там очень хорошо всё расписано.


Я деплою так, а ты как? Пиши в комментариях!

Поделиться с друзьями
-->

Комментарии (22)


  1. thousandsofthem
    09.08.2016 17:13

    Вечная головная боль в elixir/phoenix — управление конфигами. Дело в том, что при компиляции конфиг приложения превращается в статический файл с намертво прибитыми гвоздями значениями. Т е любое изменение требует пересборки, причем с предварительно подсунутыми правильными данными. Зачем такое извращение было добавлено в язык — понятия не имею.
    Если кто-то знает как это можно адекватно обрабатывать — очень хочу услышать.


    1. Virviil
      09.08.2016 17:16

      Что насчёт System.get_env в конфиге?


      1. thousandsofthem
        09.08.2016 17:23

        Использую. Работает, но фиксирует значение во время компиляции. Требуется рекомпиляция для изменения


    1. azhi
      09.08.2016 18:22

      Мы используем вот этот скрипт для перегенерации «статического» файла из эликсировского сырца в момент деплоя релиза. Вот статья автора, где он описывает как это работает.

      Там же упомянута более тяжелая альтернатива — conform, но ее мы не пробовали.


    1. nwalker
      09.08.2016 18:22
      +1

      Конфиги в Elixir — небольшой сахар для application env в Erlang, в котором хранятся статические значения. Так что, ничего удивительного в этом нет.

      Ну и вообще, вы что, хотели, чтобы в application env лежали thunk-и, которые вычислялись про каждом вызове Application.get_env?
      Нет, из коробки ничего подобного нет и не будет. Хотя бы потому, что это невозможно нормально подружить с hot code reload как он реализован в BEAM.

      Ну и вообще, моя обычная рекомендация — не пользуйтесь релизами, это непростая в использовании и очень специфичная фича, она категорически не для всех.


      1. thousandsofthem
        09.08.2016 19:13

        При каждом вызове — не надо. При каждом старте приложения — надо
        Например FOO=bar mix phoenix.server


        1. nwalker
          09.08.2016 20:08

          А вот это уже удивительно.
          Вообще, конечно, разработчики Elixir меня удивляют все больше и больше.


          1. thousandsofthem
            09.08.2016 20:14

            Чуть выше написали причину (наследие erlang) — такое поведение только в конфиге, в любом другом месте кода все ок. Вообще конечно косяки такого рода в языке есть но на удивление мало, очень много всего из коробки сделано очень чисто и продуманно


            1. nwalker
              09.08.2016 20:50

              Нет, вот это интересное поведение — не наследие Erlang. Application env, конечно, статичен, но не настолько. В том комменте я говорил о другом.

              Вообще говоря, ничего подобного происходить не должно, но я пока не могу найти, где там ошибка.


            1. nwalker
              09.08.2016 21:10

              А вот про продуманность я бы очень сильно поспорил, в языке, stdlib и plug есть достаточно просто абсурдных вещей, про которые авторы считают, что все ок. Я когда-нибудь напишу пост о них, когда не лень будет.


        1. nwalker
          09.08.2016 21:04

          Все там нормально работает. Там есть пара странностей в духе «если в конфиге nil, Application.get_env :app, :var_name, 123123 вернет nil, а не 123123», но никакого кеширования конфигов между запусками нет и не может быть by design.

          Другой дело, если у вас значения из ENV через конфиг используются в макросах, тогда да, нужно будет перекомпилировать проект. Где в Phoenix макросы я точно не знаю, но в этом случае точно ничего удивительного нет. В этом случае рекомендую делать touch config/config.exs перед каждым запуском.

          Вам лично — «двойка» за умение формулировать проблемы.


      1. Virviil
        09.08.2016 19:41

        А если не пользоваться релизами, тогда как? Каждый раз git pull?


    1. erlyvideo
      09.08.2016 20:26

      эта херня растет из той дури, что можно держать аппликейшн конфиг в файлах типа sys.config

      При этом все скачут как дурни вокруг hot code reload, а на вопрос: как будете перечитывать конфиг, скакуны бодро отвечают: рестартить приложение с новым конфигом, который уже лежит на сервере под рутовыми правами и недоступный для редактирования.

      Это всё растет из того, что на эликсире-эрланге редко пишут отчуждаемые приложения, которые пишутся одними, а деплоятся другими людьми.


  1. yamatoko
    09.08.2016 19:42

    А миграции как запускать?


    1. Virviil
      09.08.2016 20:00

      mix edeliver migrate


      Если экто — то на автомате


      1. yamatoko
        10.08.2016 08:34

        что значит «Если экто — то на автомате»? ecto я знаю, что такое.


        1. Virviil
          10.08.2016 09:01

          Если вы написали собственный механизм миграций — то и edeliver у надо будет рассказать как мигрировать.
          Если вы используете ecto — то он и сам знает.
          Вот что значит "на автомате"


  1. kireevco
    10.08.2016 00:54

    Иронично, первая картинка от Octopus Deploy, если не ошибаюсь.


  1. yamatoko
    10.08.2016 08:57

    Где нужно хранить загруженные пользователем картинки и файлы, чтобы они не были задеплоины вместе с приложением? То есть, пользовательские картинки или файлы должные быть отдельно, не обязательно даже совсем в другой папке. Просто я хочу, чтобы они не были статически включены в само приложение, а были отдельно, как они и должны быть.


    1. Virviil
      10.08.2016 09:00

      По-моему, нигде в проекте на хранятся пользовательские данные. Если это картинки и файлы — в отдельной папочке, которая даже не обрабатывается beamом, а сервится прямо нгинксом.


      1. erlyvideo
        10.08.2016 09:34
        -1

        зачем nginx то?

        Эрланговский веб-сервер не хуже нжинкса.


  1. dzolotarev
    10.08.2016 10:56
    +1

    Эх, вот не пошел у меня Elixir после Erlang'а. Какой-то он сложный, запутанный и инопланетный слишком. Как небо и земля прям.