以前の記事で、HerokuのMono環境でRakeタスクが実行できることが分かりました。
次は、RakeタスクのRunnerが存在するFluentMigratorを使って、Heroku Postgresのマイグレーションを試してみました。
環境
- Windows7 x64
- SourceTree
- .NET Framework 4.5
- Ruby 2.1.5 (RubyInstaller版)
- PostgreSQL 9.3.5 x64
- NuGetパッケージ
- FluentMigrator 1.3.1
- FluentMigrator.Tools 1.3.1
- Npgsql 1.2.2.3
- Herokuのbuildpack
- heroku/heroku-buildpack-multi
- friism/heroku-buildpack-mono
- heroku/heroku-buildpack-ruby
なお、ここに記載されていないものは以前の記事と同様です。
また、コマンド類はSourceTreeのターミナルから実行しています。
Herokuの環境変数確認
heroku-buildpack-multiを使うため、環境変数BUILDPACK_URL
が存在することを確認します。
$ heroku config === <AppName> Config Vars BUILDPACK_URL: https://github.com/heroku/heroku-buildpack-multi.git ...
Heroku Postgresの追加・確認
Heroku Postgresの追加
作成したアプリにHeroku Postgresを追加します。
$ heroku addons:add heroku-postgresql:hobby-dev Adding heroku-postgresql:hobby-dev on <AppName>... done, v14 (free) Attached as HEROKU_POSTGRESQL_PINK_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation.
Heroku Postgresのバージョン確認
開発環境にPostgreSQLをインストールするために、Heroku Postgresのバージョンを調べたところ、現在は9.3
系のようでした。
Heroku Postgres | Heroku Dev Center
ただ、Herokuでは32bit/64bitの動いているか分からなかったため、pgAdminでHeroku Postgresにつないで確認することにしました。
pgAdmin: PostgreSQL administration and management tools
また、Heroku Postgresの接続文字列が分からなかったことから、以下のようなRakeタスクを持ったRakefileを作成・デプロイし、確認しました。
task :show do require 'uri' db = URI.parse(ENV['DATABASE_URL']) puts "Server=#{db.host}; Port=#{db.port}; Database=#{db.path[1..-1]}; User Id=#{db.user}; Password=#{db.password}; SSL=true;SslMode=Require;" end
pgAdminを起動・Heroku Postgresへ接続し、SELECT version();
で確認したところ、
PostgreSQL 9.3.5 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, 64-bit
と表示されたため、PostgreSQL 9.3.5の64bit版をインストールすれば良さそうでした。
開発環境のPostgreSQLまわりをセットアップ
インストール
以下よりダウンロード・インストールします。
Download PostgreSQL | EnterpriseDB
インストーラーを起動し、以下の内容でセットアップしました。開発環境なので、パスワードは適当です。
項目 | 値 |
---|---|
Installation Directory | C:\PostgreSQL\9.3 |
Data Directory | C:\PostgreSQL\9.3\data |
Password | postgres |
Port | 5432 (デフォルト) |
Locale | C |
Stack Build ... | チェックを外す |
初めてPostgreSQLを使ったため、Localeがよく分からなかったのですが、以下を参考にC
を選びました。
- WindowsでPostgreSQLを使ってみよう — Let's Postgres
- ErogameScape -エロゲー批評空間- Blog : ErogameScapeにおいてPostgreSQLのロケールをデフォルトのUTF8で初期化すると重いクエリがある
環境変数Pathへ追加
PostgreSQLのCLIを使うため、環境変数のPathに以下を追加しました。
C:\PostgreSQL\9.3\bin
データベースの確認
psqlをpostgresユーザで起動します。
>psql -U postgres ユーザ postgres のパスワード: psql (9.3.5) "help" でヘルプを表示します. postgres=#
データベースを確認します。
postgres=# \l データベース一覧 名前 | 所有者 | エンコーディング | 照合順序 | Ctype(変換演算子) | アクセス権 -----------+----------+------------------+----------+-------------------+----------------------- postgres | postgres | UTF8 | C | C | template0 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres (3 行)
FluentMigrator用のデータベースを追加
開発環境で使う、fluentmigrator
データベースを追加します。
一度psqlを閉じ、createdbを実行します。
>createdb -U postgres fluentmigrator パスワード:
psqlで結果を確認します。
postgres=# \l データベース一覧 名前 | 所有者 | エンコーディング | 照合順序 | Ctype(変換演算子) | アクセス権 ----------------+----------+------------------+----------+-------------------+----------------------- fluentmigrator | postgres | UTF8 | C | C | postgres | postgres | UTF8 | C | C | template0 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres (4 行)
開発環境の接続はすべて信頼する
都度設定するのは手間なので、C:\PostgreSQL\9.3\data\pg_hba.conf
を編集し、すべて信頼します。
# IPv4 local connections: host all all 127.0.0.1/32 trust # 追加 #host all all 127.0.0.1/32 md5 # コメントアウト
サービスの再起動
設定ファイルを編集したため、以下のサービスを再起動します。
postgresql-x64-9.3
NuGetパッケージの設定
NuGetパッケージの追加
PostgreSQLに対してFluentMigratorを実行するため、以下のNuGetパッケージを追加します。
- FluentMigrator
- FluentMigrator.Tools
- Npgsql
FluentMigratorの実行に必要なファイルをプロジェクトへ追加・設定
ファイルをリンクとして追加
FluentMigratorをRakeタスクで実行するため、以下のファイルをプロジェクトの「追加 > 既存の項目」から「リンクとして追加」しておきます。
ファイル名 | 存在するパス |
---|---|
FluentMigrator.Runner.dll | path\to\project_root\packages\FluentMigrator.Tools.1.3.1.0\tools\AnyCPU\40\FluentMigrator.Runner.dll |
Migrate.exe | path\to\project_root\packages\FluentMigrator.Tools.1.3.1.0\tools\AnyCPU\40\Migrate.exe |
ファイルのプロパティを修正
ビルドした時に出力ディレクトリへコピーされるよう、上記2ファイルのプロパティを修正します。
項目 | 値 |
---|---|
ビルドアクション | コンテンツ |
出力ディレクトリにコピー | 常にコピーする |
gem Albacore
の確認
FluentMigratorのWikiには、gem Albacore
を使うことでRakeタスクとして実行できると書かれています。
Migration Runners · schambers/fluentmigrator Wiki · GitHub
しかし、Gemfileにalbacoreを追加してインストールし、Wikiのコードをコピペして実行したところ、エラーが発生しました。
$ bundle exec rake db:migrate -f Albacore.rake DL is deprecated, please use Fiddle rake aborted! NoMethodError: undefined method `fluentmigrator' for main:Object ...
Albacoreのソースコードを見ると、v1に存在していた以下のようなコードが、masterには存在しませんでした。
albacore/fluentmigrator.rb at v1.0.0 · Albacore/albacore
AlbacoreのWikiのIndexを見ても、Albacore 1.0.0の下にしかFluent MigratorのTask Typesが記載されていませんでした*1。
Home · Albacore/albacore Wiki · GitHub
また、他のTaskTypesであるCSC
もAlbacore v2.xではサポートされていないとの回答がありました。
ruby - Why is this csc task not working in Albacore? - Stack Overflow
これより、現在のAlbacore v2.xではFluentMigratorはサポートされていないと考えられたため、
- Albacore v1.0.0 を使う
- 自力でFluentMigratorのRakeタスクを書く
のどちらかの方法を取る必要がありました。
Albacore v1.x系は更新されなさそうなので、今回は後者の方法を選びました。参考としたソースコードは以下のあたりです。
- albacore/fluentmigrator.rb at v1.0.0 · Albacore/albacore
- albacore/runcommand.rb at v1.0.0 · Albacore/albacore
ちなみに、Albacore 2.xは2014/8/21にリリースされたので、それ以前の日付の記事は現時点ではあまり参考にならないかもしれません。
Rakeタスクを作成
Rakeタスクを書くにあたり、以下の点で悩みました。
開発環境とHeroku環境の判定
各環境では接続文字列などが異なるため、各環境をどのように判定するのか悩みました。
gem Platform
を使う
Herokuでは、
require 'platform' puts "OS:#{Platform::OS}" puts "IMPL:#{Platform::IMPL}" puts "ARCH:#{Platform::ARCH}"
とすると、Platform::ARCHがunknown
を返してきます。
ただ、unknownだけで判断するのはあまり良くない気がしました。
環境変数RACK_ENV
を使う
Herokuでよく見かける環境変数RACK_ENV
が使えないかを調べました。
すると、Herokuでは gem Rack
があるとENV['RACK_ENV']
にproduction
が入るというドキュメントを見ました((正確にはconfig
ディレクトリやconfig.ru
ファイルが必要そうですが、手元ではGemfileにrack
を追加するだけでもRACK_ENV
にproductionが設定されました))。
Heroku Ruby Support | Heroku Dev Center
ただ、Rackアプリではないのに不要なgemを追加するのも気が引けました。
結論
今回はHeroku Postgresを使うため、DATABASE_URL
が設定されているかどうか(設定されているとHeroku環境)ということにしました。
Heroku上でMigrater.exeを実行する方法
system "\"#{runner_path}\" <各種オプション>"
と書いて実行したところ、エラーが発生しました。
$ heroku run rake migration Running `rake migration` attached to terminal... up, run.2591 sh: 1: Migrate.exe: not found
Migrate.exeがデプロイされていないのかと思いましたが、ファイルは存在していました。
$ heroku run ls Running `ls` attached to terminal... up, run.3544 app.json Nancy.Owin.dll bin NancyOwinHeroku-sample FluentMigrator.dll NancyOwinHeroku-sample.exe FluentMigrator.Runner.dll NancyOwinHeroku-sample.exe.config Gemfile NancyOwinHeroku-sample.exe.mdb Gemfile.lock NancyOwinHeroku-sample.sln LICENSE Npgsql.dll Microsoft.Owin.dll Owin.dll Microsoft.Owin.Host.HttpListener.dll packages Microsoft.Owin.Hosting.dll Procfile Migrate.exe Rakefile mono README.md Mono.Security.dll tmp Nancy.dll vendor
調べてみたところ、Herokuでexeファイルを実行するには、Monoを使う必要がありました。
node.js - Is there a way to run a .net exe on heroku? - Stack Overflow
そのため、Rakefileで実行する箇所を以下のような感じにします。
system "mono \"#{runner_path}\" <各種オプション>"
HerokuにてFluentMigratorを実行
Herokuへデプロイ
git push heroku master
FluentMigratorによるマイグレーション
ネットワークを介してherokuコマンドを実行すると、「Running
One-Off Dynos - An example one-off dyno | Heroku Dev Center
$ heroku run bash Running `bash` attached to terminal... up, run.5670 ~ $
続いて、Rakefileに書いたマイグレーションを実行します。
~ $ rake migration ... [+] Using Database postgres and Connection String Server ... ... [+] Task completed.
Heroku Postgresの確認
テーブルが生成され、データも登録されていました。
これにより、HerokuでもFluentMigratorが実行できると分かりました。
$ heroku pg:psql ---> Connecting to HEROKU_POSTGRESQL_AMBER_URL (DATABASE_URL) psql (9.3.5) SSL 接続 (暗号化方式: DHE-RSA-AES256-SHA, ビット長: 256) "help" でヘルプを表示します. <AppName>::AMBER=> \d リレーションの一覧 スキーマ | 名前 | 型 | 所有者 ----------+-------------+----------+---------------- public | Ringo | テーブル | vumlwdhcmtacuy public | VersionInfo | テーブル | vumlwdhcmtacuy (2 行) <AppName>::AMBER=> SELECT * FROM "Ringo"; ID | Name ----+---------------- 1 | ふじ 2 | シナノゴールド 3 | あいかの香り (3 行)
なお、最後のSQLでテーブル名をダブルクオーテーションで囲まないとエラーになります。
<AppName>::AMBER=> SELECT * FROM ringo; ERROR: relation "ringo" does not exist 行 1: SELECT * FROM ringo;
参考:PostgreSQLのテーブル名はダブルクォート有無で別名になった. - それマグで!
ソースコード
GitHubに上げました。
thinkAmi-sandbox/NancyOwinHeroku-FluentMigrator · GitHub
参考
Heroku Postgres
Rake
- module Rake::DSL - Rake -- Ruby Make
- Rakeの基本的な使い方まとめ - うなの日記
- Rubyから外部プログラムを起動 - None is None is None
*1:ぱっと見、v1.0.0でしか使えないとは分かりづらかった