Releases

Releases

애플리케이션 준비가 끝났다면 이제 이걸 어떻게 패키징해서 프로덕션에서 실행할지 궁금해질 거예요. 지금까지의 모든 코드는 현재 시스템에 설치된 Erlang·Elixir 버전에 의존하고 있으니까요. 이 목표를 달성하기 위해 Elixir는 releases를 제공합니다.

출처: Releases

본문

릴리스는 애플리케이션 코드, 모든 의존성, 그리고 Erlang VM과 런타임 전체를 포함하는 자족적(self-contained) 디렉터리예요. 일단 릴리스가 조립되면 패키징해서 대상에 배포할 수 있는데, 단 대상이 릴리스를 조립한 머신과 같은 OS(운영체제) 배포판·버전에서 실행돼야 합니다.

시작하려면 MIX_ENV=prod를 설정하고 mix release를 실행하기만 하면 돼요.

$ MIX_ENV=prod mix release
Compiling 4 files (.ex)
Generated kv app
* assembling kv-0.1.0 on MIX_ENV=prod
* using config/runtime.exs to configure the release at runtime

Release created at _build/prod/rel/kv

    # To start your system
    _build/prod/rel/kv/bin/kv start

Once the release is running:

    # To connect to it remotely
    _build/prod/rel/kv/bin/kv remote

    # To stop it gracefully (you may also send SIGINT/SIGTERM)
    _build/prod/rel/kv/bin/kv stop

To list all commands:

    _build/prod/rel/kv/bin/kv

좋아요! 릴리스가 _build/prod/rel/kv에 조립되었습니다. 애플리케이션을 실행하는 데 필요한 모든 것이 그 디렉터리 안에 들어 있어요. 특히 bin/kv 파일이 시스템의 진입점인데, 여러 명령을 지원합니다.

  • bin/kv start, bin/kv start_iex, bin/kv restart, bin/kv stop — 릴리스 일반 관리용
  • bin/kv rpc COMMAND, bin/kv remote — 실행 중인 시스템에 명령을 실행하거나 연결할 때
  • bin/kv eval COMMAND — 단일 명령을 실행하고 종료하는 새 시스템 시작
  • bin/kv daemon, bin/kv daemon_iex — Unix 계열 시스템에서 데몬으로 시작
  • bin/kv install — Windows 머신에서 시스템을 서비스로 설치

릴리스 디렉터리 안에서 bin/kv start_iex를 실행하면 릴리스 이름(여기서는 kv)과 같은 짧은 이름(--sname)으로 시스템이 시작됩니다. 다음 단계는 앞서 했던 것처럼 서로 다른 포트·이름으로 두 인스턴스를 시작하는 일이에요. 다만 그 전에 releases의 장점을 잠깐 얘기해 볼게요.

왜 releases인가

Releases는 개발자가 모든 코드와 런타임을 미리 컴파일하고 패키징해서 단일 단위로 만들 수 있게 해 줍니다. 출시의 장점은 다음과 같아요.

  • 코드 프리로딩(Code preloading). VM에는 코드를 로드하는 두 가지 메커니즘, 즉 interactive와 embedded가 있어요. 기본적으로는 interactive 모드로 실행되어 모듈이 처음 사용될 때 동적으로 로드됩니다. 애플리케이션이 처음 Enum.map/2를 호출하면 VM이 Enum 모듈을 찾아 로드하는 식이죠. 여기에는 단점이 있어요. 프로덕션에서 새 서버를 시작하면 다른 모듈을 많이 로드해야 해서 처음 몇 요청의 응답 시간에 비정상적인 스파이크가 생길 수 있어요. Releases는 embedded 모드로 실행되어 모든 모듈을 미리 로드하므로, 부팅 후 시스템이 요청을 처리할 준비가 되어 있다는 걸 보장해 줍니다.
  • 설정과 커스터마이징. Releases는 개발자에게 시스템 설정과 시스템 시작에 쓰는 VM 플래그를 세밀하게 제어할 수 있게 해 줍니다.
  • 자족적(Self-contained). 이 출시는 프로덕션 아티팩트에 소스 코드를 포함할 필요가 없어요. 모든 코드는 미리 컴파일되고 패키징됩니다. 게다가 기본적으로 Erlang VM과 런타임을 포함하기 때문에 서버에 Erlang이나 Elixir조차 필요하지 않습니다. 또한 Erlang·Elixir 표준 라이브러리도 실제로 사용하는 부분만 남도록 잘라냅니다.
  • 여러 릴리스. 애플리케이션별로 다른 설정을 가진 다른 릴리스를 조립할 수 있고, 아예 다른 애플리케이션으로 릴리스를 만들 수도 있어요.

릴리스에 관한 방대한 문서를 작성해 두었으니 더 자세한 내용은 공식 문서를 확인해 주세요. 지금은 위에서 소개한 기능들을 조금 더 살펴볼게요.

Releases 설정하기

Releases는 프로덕션 시스템의 거의 모든 요구를 설정할 수 있는 내장 훅(built-in hook)도 제공합니다.

  • config/config.exs — 애플리케이션이 컴파일되기 전에 실행되는 빌드 타임 애플리케이션 설정. 이 파일은 종종 config/dev.exs, config/prod.exs 같은 환경별 설정 파일을 import합니다.
  • config/runtime.exs — 런타임 애플리케이션 설정. 릴리스가 부팅될 때마다 실행되며 config provider를 통해 더 확장할 수 있어요.
  • rel/env.sh.eex, rel/env.bat.eex — 모든 릴리스에 복사되고 모든 명령에서 실행되는 템플릿 파일. VM에 특화된 변수를 포함한 환경 변수를 설정합니다.
  • rel/vm.args.eex — 모든 릴리스에 복사되어 Erlang VM과 기타 런타임 플래그의 정적 설정을 제공하는 템플릿 파일.

이 경우 이미 PORTNODES 환경 변수를 다루는 config/runtime.exs를 지정해 두었죠. 게다가 releases는 --sname 파라미터를 받지 않지만 RELEASE_NODE 환경 변수로 이름을 설정하게 해 줍니다. 따라서 _build/prod/rel/kv로 들어가 다음을 입력하면(실제 컴퓨터 이름으로 @computer-name을 조정하세요) 시스템 복사본 두 개를 시작할 수 있어요.

$ NODES="foo@computer-name,bar@computer-name" PORT=4040 RELEASE_NODE="foo" bin/kv start_iex
$ NODES="foo@computer-name,bar@computer-name" PORT=4041 RELEASE_NODE="bar" bin/kv start_iex

잘 됐는지 확인하려면 IEx 세션에서 Node.list를 입력해서 다른 노드가 반환되는지 보면 돼요. 안 그렇다면 각 iex> 프롬프트 안의 노드 이름을 비교하고 Node.connect/1을 직접 호출하면서 진단을 시작할 수 있습니다. 애플리케이션이 실행되는 동안 평소처럼 telnet으로 접속할 수도 있어요.

위 내용으로 시작하기엔 충분하지만, 배포 대상 환경에 따라 고급 설정을 하고 싶을 수도 있어요. Releases는 호스트·네트워크·클라우드 설정 기반으로 자동화하기 좋은 스크립트를 제공합니다.

운영체제 스크립트(Operating System scripts)

모든 릴리스에는 Elixir 시스템이 시작되기 전에 실행되는 환경 파일이 있어요. Unix 계열에선 env.sh, Windows에선 env.bat라고 하죠. 이 파일 안에서는 다른 애플리케이션을 호출하거나 환경 변수를 설정하는 등 OS 레벨 코드를 실행할 수 있습니다. 그중 일부 환경 변수는 릴리스 자체가 어떻게 실행될지까지 설정할 수 있어요.

예를 들어 releases는 짧은 이름(--sname)으로 실행됩니다. 하지만 프로덕션에서 실제 분산 키-값 저장소를 실행하려면 여러 노드가 필요하고 --name 옵션으로 릴리스를 시작해야 해요. 이를 env.shenv.bat 파일 안에서 RELEASE_DISTRIBUTION 환경 변수를 설정해서 달성할 수 있습니다. Mix는 이미 이 파일들의 템플릿을 갖고 있고 커스터마이즈할 수 있으니, Mix가 그것들을 애플리케이션에 복사하게 해 볼게요.

$ mix release.init
* creating rel/vm.args.eex
* creating rel/remote.vm.args.eex
* creating rel/env.sh.eex
* creating rel/env.bat.eex

rel/env.sh.eex를 열어 보면 다음과 같습니다.

#!/bin/sh

# # Sets and enables heart (recommended only in daemon mode)
# case $RELEASE_COMMAND in
#   daemon*)
#     HEART_COMMAND="$RELEASE_ROOT/bin/$RELEASE_NAME $RELEASE_COMMAND"
#     export HEART_COMMAND
#     export ELIXIR_ERL_OPTIONS="-heart"
#     ;;
#   *)
#     ;;
# esac

# # Set the release to load code on demand (interactive) instead of preloading (embedded).
# export RELEASE_MODE=interactive

# # Set the release to work across nodes.
# # RELEASE_DISTRIBUTION must be "sname" (local), "name" (distributed) or "none".
# export RELEASE_DISTRIBUTION=name
# export RELEASE_NODE=<%= @release.name %>

노드 간에 작업하는 데 필요한 단계는 예시로 이미 주석 처리되어 있어요. RELEASE_DISTRIBUTION 변수를 name으로 설정하면 완전한 분산을 활성화할 수 있습니다.

Windows라면 rel/env.bat.eex를 열어 봐야 하는데, 여기서 이걸 찾을 수 있어요.

@echo off
rem Set the release to load code on demand (interactive) instead of preloading (embedded).
rem set RELEASE_MODE=interactive

rem Set the release to work across nodes.
rem RELEASE_DISTRIBUTION must be "sname" (local), "name" (distributed) or "none".
rem set RELEASE_DISTRIBUTION=name
rem set RELEASE_NODE=<%= @release.name %>

다시 한번 RELEASE_DISTRIBUTION 변수를 name으로 설정하면 끝입니다!

VM 인자(VM arguments)

rel/vm.args.eex는 Erlang VM과 런타임이 동작하는 방식을 제어하는 저수준 플래그를 지정할 수 있게 해 줍니다. 명령줄에 인자를 지정하는 것처럼 항목을 적으면 되고, 코드 주석도 지원됩니다. 기본 생성 파일은 다음과 같아요.

## Customize flags given to the VM: https://www.erlang.org/doc/man/erl.html
## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here

## Increase number of concurrent ports/sockets
##+Q 65536

## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10

VM 인자와 플래그의 전체 목록은 Erlang 문서에서 볼 수 있습니다.

정리하며(Summing up)

이 가이드 전체에서 우리는 제네릭 서버, 슈퍼바이저, 태스크, 에이전트, 애플리케이션 등 많은 구성을 탐구할 기회로 아주 단순한 분산 키-값 저장소를 만들어 봤어요. 그뿐 아니라 애플리케이션 전체에 테스트를 작성하고 ExUnit에도 익숙해졌으며, Mix 빌드 도구로 다양한 작업을 처리하는 법도 배웠습니다.

프로덕션에 쓸 분산 키-값 저장소를 찾고 있다면 Riak을 꼭 살펴보세요. Riak도 Erlang VM에서 실행되며, 데이터 손실을 피하기 위해 버킷을 여러 노드에 걸쳐 복제·저장합니다.

물론 Elixir는 분산 키-값 저장소보다 훨씬 더 많은 곳에 쓸 수 있어요. 임베디드 시스템, 데이터 처리·수집, 웹 애플리케이션, 오디오·비디오 스트리밍 시스템, 머신러닝 등이 Elixir가 뛰어난 여러 도메인이에요. 이 가이드가 여러분이 그런 도메인이나 앞으로 Elixir를 가져가고 싶은 어떤 도메인을 탐험할 준비를 해 줬기를 바랍니다.

행복한 코딩 되세요!

더 알아보기