Elixir Advent Calendar 2016 14日目です。
現在開発しているphoenixのプロジェクトで使用しているedeliverとconsul+strecherを使ったdeploy構成を簡単に紹介したいと思います。
edeliver
edeliverはelixir/erlangで使えるbuild&deployツールです。
build部分はexrmやdistilleryをwrapして、gitを使ったAuto Versioningやupgrade buildサポート機能を提供してくれます。
deploy部分は、buildしたpackageをリリースタイプを指定することで、ローカル保存、S3にアップロード、stagingまたはproduction環境へのdeployなどの動作を実行することができます。
興味が出たらwikiにいろいろ書いてあるのでそちらを見るのがいいと思います。
deploy部分はコードを見ると結構思い切りがいい書き方がされているので(褒め言葉)、build部分だけ利用してconsulと組み合わせることでdeployすることにしました。
Versioningは基本的にstop/startやblue/greenで運用しているような環境ではversion更新が問題になることはないのですが、upgradeを実施しようとするとincrementalなVersioningが必要になってきます。
applicationのversionは基本はmix.exsの中に記載して更新しますが、細かいreleaseを何回も行うような環境だと、release branchにマージするたびにversionを更新しないといけません。
edeliverではgitのcommit-countやrevisionを利用して、バージョニングのルールに違反しないようなバージョン番号を発行してbuildに含めてくれます。(このあたりのルールを使用)
現在自分たちの環境で使用しているAUTO_VERSIONの設定は
AUTO_VERSION="git-commit-count-branch+git-revision"
(git-commit-countとgit-commit-count-branchは別物なので注意)
また、upgrade buildを作成するためにはupgrade前のbuildが必要になるのですが、以前の作成時のrevision番号を指定するだけで、2回build作成が走るので時間はかかりますがクリーンな環境からでもupgrade buildを行うことができます。
distilleryを使う場合にmix release.init
で前もってrel/config.exs
を作っておく必要があります。
upgrade buildを作成するためにはその内部に
environment :plan do
set include_erts: true
set cookie: :"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
end
で、include_erts
をtrue
にしておく必要があります。
consul + stretcher
consulのイベント検出を利用してpull型の配送を行うための組み合わせになります。
詳しく知りたい方は本家の記事を参照していただければと思います。
build + deployの流れ
build処理とdeploy処理にわけて実行しています。
build処理はおおざっぱに言うと、buildして精製物をS3に置く処理になっていて、
- edeliver用のconfigをビルド環境用のものに取り替える
- edeliverが.deliver/configが直接読むので、環境ごとに切り替える必要があった
- S3上に
lock
を取得する - S3上から
current_relase
を取得する
-
current_release
は現在deployされて動いているbuildの情報を記載してあるyml - 存在しない場合は
build release
を実行する - 存在する場合は
build upgrade
を実行する
- 作成したビルドをS3にアップロード
- stretcher用のmanifestファイルを作成してS3に配置
- start_manifest, restart_manifest, (upgrade_manifest)の3つを前もって作成しておく
- 作成したビルドのバージョン等を
latest_build
に記載してS3上にupload - S3上の
lock
を解放
上記の処理を実行しています。
deploy処理は最新のbuildをappサーバに配布する処理で、start|restart|upgradeのリリースタイプを指定しています
- S3上の
current_release
、latest_build
とリリースタイプの組み合わせから整合性をチェック
- release buildに対してupgradeじゃないかとか、release_buildとversionが変わらないなど...
- S3上に
lock
を取得する - consulにeventを発行
- リリースタイプに対応するmanifestを一緒に渡してstretcherの挙動を制御しています
-
current_release
を更新 - S3上の
lock
を解放
上記のような仕組みでdeployを実行しています。
開発ステージごとにいくつかの共有環境があるので、buildや管理用のファイルはbucketやパスで分離されており、自分の反映したい環境にslackで実行依頼を投げてhubotを介して、jenkins内のdocker containerでbuildとdeployを実行しています
###なんでこんな構成にしたのか
APIをメインとしたリクエストのみの場合、containerを使ったdeployの仕組みをとったほうが容易だと思います。
ただphoenixの一つの魅力であるphoenix channelのsocket接続を切断せずに継続的デプロイを実施したいと考えていて、とりあえず試行錯誤している段階です。
hot deployはやはりまだ試行錯誤している段階で、安定しているとは言えていない状況です。
ただもう少しノウハウ等がたまってきたらまた共有していきたいなと思っています。