herokuにカジュアルなスキーマ変更を求めるのは間違っているだろうか
herokuブログでは初めまして。@ucmskyです。
まず最初に告知ですが。前の記事でも紹介したとおり、9月10日に弊社で勉強会を開催します。
第1回FlectMeetup MEANスタック+HerokuでWebサービスを作ろう!
内容はMEANスタックのハンズオンですので、node.jsに興味のある人は是非。
rigepoleの簡単な紹介
さて、今日紹介することですが、その前に。
皆さん、herokuでもridgepole使いたいですよね? 使いたいですよね?(震え声
ところで、ここでridgepoleって何? という人はこちらをどうぞ。
平たく言うと、クックパッドの中の人が開発したgemです。
通常、railsで開発を進める場合、DBのスキーマを変更する場合ですと、各変更毎にmigratenファイルを生成し、変更内容を記載します。変更内容はカラムの変更(追加削除更新など)と、逆に差し戻す場合の逆の手順(カラムを追加した場合は削除し、変更した場合は変更前の状態に戻すなど)を記載します。
こうすることで、変更と差し戻しをrakeで制御するのですが、あまりスキーマの変更を容易に出来る仕組みではありません。
ところが、ridgepoleはテーブルの情報を記載したスキーマファイルを読み取り、DBの情報と差異がある部分だけを直接DBに適用することが出来るので変更コストを減らすことが出来ます。
さて、ridgepoleですが、heroku上でも使うことはできるでしょうか? 結論から言うと可能です。
$heroku run ridgepole -e config/database.yml ...
とか
$heroku run bash
$ridgepole -e config/database.yml ...
ではheroku上では使えません。
(9月4日追記)
heroku runから呼び出す場合は、以下のようにクォーテーションで囲むと使えます。
後述のようにrakeタスクを登録する手間を省きたい場合はこちらの方法も可能です。
(dry runの例)
$ heroku run "bundle exec ridgepole -c config/database.yml \
-E production -f config/Schemafile --apply --dry-run"
herokuでの使い方
便利な方法として、rakeタスクに記述して、rake経由でridgepoleを呼び出すという方法です。
$bundle exec rails g task ridgepole
で lib/tasks/ridgepole.rake が生成されるので、こちらのページの内容に従って編集します。
【Ruby】【Rails】手間な実行コマンドはRakeタスクに書いて行くようにした。
エクスポートのコマンドはheroku上では意味が無いですが、ローカルで使う分には便利です。
デモ
では実際に変更されるのか試してみます。今回はこちらのrails apiのサンプルを使って、実際にmigrationファイルを生成しないでスキーマ変更ができることを確認してみます。 途中まではサンプル通りに作り、そこからridgepoleによってスキーマを変更するまでを行います。 スキーマ変更はローカル→herokuの順に行います。
因みになぜrails apiかというと、rails5で標準搭載されると以前ginza.rbで話題になった時に軽く触ってみていたので。
確認で使ったrailsとrubyのバージョンは以下の通りです。サンプルのrailsのバージョンは3系ですが、4でも問題なく動きます。
ruby 2.2.2 / rails 4.2.3
今回はpostgresを使うので作成時にdオプションを付けます。
$rails-api new todo -d postgresql
作成されたらGemfileを編集します。
gem 'ridgepole'
group :production do
gem 'rails_12factor' # herokuで必要
end
後はサンプルの通りに作りこんでいきます。
取り敢えず一旦サンプルの通りに作ったら、herokuにデプロイします。
$heroku login
$heroku create flt-simple-todo
$git init
$git add .
$git commit -m "first commit"
$git remote add heroku https://git.heroku.com/flt-simple-todo.git
$git push heroku master
$heroku run rake db:migrate
余談ですが、最後の heroku run rake db:migrate が好きなherokuのコマンドです(あくまで初回のみ)。
ridepoleによるマイグレート
さて、ここからがridgepoleを使った場合のマイグレーションです。 上で書いたとおりにridgepoleを呼び出すタスクを生成します。
スキーマファイルを生成してみます。
$bundle exec rake ridgepole:export
するとconfig/Schemafileが生成され、以下のような内容になっていると思います。
create_table "tasks", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
ご覧のとおり、テーブルの定義が記載されています。 ではこのファイルを編集し、herokuにデプロイするところまでやってみます。
create_table "tasks", force: :cascade do |t|
t.string "name"
t.string "title" #<==タイトルを付けてみる
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
まずローカルで適用します。
$bundle exec rake ridgepole:apply
本当に増えているのか動作確認も含めて行うので、諸々編集します(追加したカラムを含めて表示編集できるような内容であれば特になんでも構いません)。
こんな感じで反映されていればローカルでは成功です。
では続いて肝心のherokuでの動作確認をします。
その前に以下の注意点が有ります。
注意点
heroku上でridgepoleからDB(PostgreSQL可)に繋ぎに行く場合、config/database.ymlの接続情報が正しく設定されていないと接続できません。
何を当たり前のことを、思われるかもしれませんが、herokuの場合、config/database.ymlの設定を読み飛ばすという仕様になっています。
参考 https://devcenter.heroku.com/articles/rails-database-connection-behavior
The config/database.yml file no longer generated for applications using ActiveRecord 4.1+
接続のための情報は環境変数「DATABASE_URL」から設定します。
DB名 DBNAME
ユーザ名 DBUSER
パスワード DBPASS
DBホスト名 DBHOST
ポート DBPORT
環境変数にセットしたら、config/database.ymlから読み出せるように設定します。
production:
<<: *default
database: <%= ENV['DBNAME'] %>
port: <%= ENV['DBPORT'] %>
username: <%= ENV['DBUSER'] %>
host: <%= ENV['DBHOST'] %>
password: <%= ENV['DBPASS'] %>
(9月4日追記)
環境変数「DATABASE_URL」が設定されている場合は、以下の設定にすることで、特に追加の設定は必要無いみたいです。
production: url: <%= ENV['DATABASE_URL'] %>
設定できたらもう一度デプロイします。
git add .
git commit -m "ridgepole deploy"
git push heroku master
ではデプロイ後、ridgepoleによるマイグレートを行います。
heroku run rake ridgepole:apply
こんな表示になっていて上手く動いたら完了です。
お疲れ様でした。
コメント