こしごぇ(B)

旧:http://d.hatena.ne.jp/koshigoeb/

Heroku で運用するプレビュー環境に自動デプロイする試み(0)

中途半端だけど、せっかくやってみたので晒してみる。一番の収穫は、シェルスクリプトの trap を覚えた事だったりするのは内緒。

やっていること

  • CI で CircleCI を使用している
  • Git を使用している
  • GitHub を使用している
  • PR をレビューして master ブランチにマージする運用
  • 開発時のプレビュー環境として Heroku を使用している

やりたいこと

  • master ブランチの最新版を、労力少なくプレビュー環境に反映させたい

手作業

  • 適宜 Heroku にデプロイする運用
  • めんどくさい

Heroku の Automatic deploys

  • Heroku の GitHub 連携機能のひとつ
  • ブランチを指定しておくと、自動的にデプロイしてくれる機能
  • Rails 固有の rake db:migrate みたいな後処理は実行させられない
    • ビルドパック作ればいいのかもしれないけど、試してない
  • 残念

余談: Heroku の Pull request apps

  • 存在する PR から Heroku アプリの複製を作れる機能
  • PR が作られたら自動で複製を作ってくれる機能もある
  • app.json を使う
    • 環境変数の継承が使えて便利
    • PostgreSQL 9.4 を使う場合、今は --version=9.4 を付ける必要があり、それを app.json の addons で記述する方法が分からない
  • 便利 and 残念

CircleCI Heroku Deployment

  • Continuous Deployment with Heroku - CircleCI
  • CircleCI のアカウント設定で Heroku の API キーを登録する
  • CircleCI のプロジェクト設定の Heroku Deployment で、使用する SSH キーのユーザをセットする
    • ボタンを押すと Heroku と GitHub に公開鍵が登録される
  • circle.yml の deployment でデプロイ設定書いたら ok
  • 簡単 and 便利

雑感

  • koshigoe/rails-example-circleci-heroku-deployment
  • 既存環境の上書きは余計な心配を抱える事になって上手くないかもしれない
  • Blue-Gree Deployment 的な考えで、毎回アプリを作成する方向で考えた方が健全かもしれない
    • URL を変えずに運用する工夫の手間次第
  • master ブランチの自動デプロイはやりすぎかもしれない
    • 同一 Heroku アプリに対する並行デプロイが発生するはず
  • 動作確認用の初期データを db:seed で冪等対策せず入れる事を考えているため、毎回 db:setup してしまっている
    • プレビュー環境で確認中にデプロイされてデータが消えるとか、余計にストレスたるはず
  • データベースのロールバックってどうするんだっけ
    • 今回のリリースで変更したスキーマとデータを元に戻す、という作業になるんだっけ
    • 今回は無理矢理 db:setup できるから気にしなくてもいいんだけど
  • CircleCI の出力無しタイムアウトの上限はどれだけなのか
    • コマンド全体のタイムアウトは別にあるのか、あるとして上限はどれだけなのか
    • timeout: 3 して while で sleep 1 をすると延々続けられるみたいだけど…
machine:
  ruby:
    version: 2.2.1
dependencies:
  cache_directories:
    - "vendor/bundle"
deployment:
  staging:
    branch: master
    commands:
      - ./deploy.sh:
          timeout: 600 # default
#!/bin/sh -ex
# -e を使って途中でエラーが発生したら即終了へ

# HEROKU_APP_NAME が未定義なら -u で拾って即終了
# HEROKU_APP_NAME が空なら if -z で拾って即終了
if [ -z "$HEROKU_APP_NAME" ]
then
    echo "\$HEROKU_APP_NAME required."
    exit 1
fi

# 1) まずはメンテナンスモードへ、失敗したら即終了
heroku maintenance:on -a $HEROKU_APP_NAME

# よくあるシグナルを受け取ったら異常終了
trap "echo trap SIGHUP;  exit 1" HUP
trap "echo trap SIGINT;  exit 1" INT
trap "echo trap SIGQUIT; exit 1" QUIT
trap "echo trap SIGTERM; exit 1" TERM
# CircleCI の timeout は拾えない様なので諦める

# ここ以降での終了を拾って後処理する
#
# 正常終了時)
#   1) メンテナンスモード終了
#
# 異常終了時)
#   1) ロールバック
#   2) DB セットアップ(リセット)
#   3) メンテナンスモード終了
trap "
case \$? in
  0 )
    echo Auto deploy successful.
    ;;
  * )
    heroku rollback -a $HEROKU_APP_NAME
    heroku run rake db:setup -a $HEROKU_APP_NAME
    echo Auto deploy failed.
    ;;
esac

heroku maintenance:off -a \$HEROKU_APP_NAME
" EXIT

# 2) デプロイ
git push -f git@heroku.com:$HEROKU_APP_NAME.git $CIRCLE_SHA1:refs/heads/master

# 3) DB セットアップ(毎回リセット)
heroku run rake db:setup -a $HEROKU_APP_NAME