monolithic kernel

Heroku最速移行メモ

September 19, 2011

    SinatraとDataMapperで作ったWebアプリをHerokuに移行した時のメモ。

    この記事では以下のような作業について書いています。

    1. Herokuに移行した理由
    2. 必要なもの
    3. アプリケーションの作成
    4. デプロイ
    5. データベースの移行
    6. 設定の管理
    7. キャッシュの利用
    8. 独自ドメインの利用

    Herokuに移行した理由

    小規模であれば無料で使えて、かつロックインされないからです。特にロックインされないという点は非常に重要で、先日Google App Engineの料金体系が変更された際に終了に追い込まれてしまったサービスが幾つかありましたが、Herokuでは同じようなことがあっても他の環境に逃げればいいだけなので安心です。

    必要なもの

    Bundler : gemの管理はBundlerで行います。

    git : デプロイはgitで行うので、アプリケーションのファイルはgitで管理しておく必要があります。

    アプリケーションの作成

    Herokuの操作はほぼすべてgemをインストールしてherokuコマンドで行います。

    $ gem install heroku

    heroku createコマンドでアプリケーションを作成します。初回はメールアドレスとパスワードの入力、および公開鍵の送信を行いますが、指示に従って進めればOKです。

    この時、ローカルのgitリポジトリのremoteにherokuが追加されます。

    $ heroku create myapp

    デプロイ

    デプロイはリモートのリポジトリにpushするだけです。push後に自動的に依存関係の解決が行われ、アプリケーションが動作するようになります。

    $ git push heroku master

    データベースの移行

    Herokuでは無料の範囲だとRDBMSにPostgreSQLを使うことになります。私はこれまでSQLiteを利用していましたが、RDBMSの違いはDataMapperが吸収してくれるので接続先のURLを変更するだけで移行できました。

    Herokuでの接続先はDATABASE_URLという環境変数で渡されます。以下のようにすると、Heroku上ではPostgreSQL、ローカルではこれまでどおりSQLiteで作業できます。

    DataMapper.setup :default, ENV['DATABASE_URL'] || 'sqlite3://db/database.sqlite3'

    データベースのマイグレーション・アップグレードはrakeで行います。すでに利用している場合は問題ありませんが、そうでなければGemfileにrakeを追加し、Rakefileを作成する必要があります。

    # -*- coding: utf-8 -*-
    require 'dm-migrations'
    
    $:.push "#{File.dirname(__FILE__)}"
    
    require 'models/...'
    
    namespace :db do
      task :migrate do
        DataMapper.auto_migrate!
      end
    
      task :upgrade do
        DataMapper.auto_upgrade!
      end
    end

    作成したタスクはheroku rakeコマンドで実行します。

    heroku rake db:migrate

    データの移行

    これまでのデータを移行するにはtapsというgemを利用します。インストールしてheroku db:pushコマンドでデータを送信します。元のデータベースは、SQLiteであれば”sqlite://データベースへの相対パス”、PostgreSQLであれば”postgres://user:password@host:port/database_name”のように指定してください。

    $ gem install taps
    $ heroku db:push sqlite://db/database.sqlite3 --app myapp

    設定の管理

    ソースコード内に書き込みたくない情報は、設定ファイルに分離してリポジトリには含めないようにすると思いますが、Herokuでのデプロイはgit pushで行うため、リポジトリにないファイルをリモートに置くことができません。そこで、Herokuでは設定のやり取りに環境変数を利用します。

    以下のコマンドで設定を行うことができます。設定を追加するとアプリケーションは自動的に再起動して設定が反映されます。

    $ heroku config:add KEY=VALUE SESSION_COOKIE_SECRET=XXXXXXXX ...

    接続先のときと同様に、Ruby側ではENVで参照できます。

    configure do
      use Rack::Session::Cookie, :secret => ENV['SESSION_COOKIE_SECRET']
    end

    キャッシュの利用

    Herokuではデフォルトの構成ですでにVarnishが挟まっていて、Cache-Controlヘッダを設定するだけで適切にキャッシュしてくれます。

    get '/' do
      ...
      cache_control :public, :max_age => 3600
      ...
    end

    独自ドメインの利用

    custom_domainsというアドオンの追加とDNSの設定が必要です。

    $ heroku addons:add custom_domains
    $ heroku domains:add www.example.com

    DNSの設定では、以下の3つのIPアドレスのaレコードを追加します。

    • 75.101.163.44
    • 75.101.145.87
    • 174.129.212.2

    以下はバリュードメインでの設定例。

    a www 75.101.163.44
    a www 75.101.145.87
    a www 174.129.212.2

    問題点

    Herokuはgitのsubmoduleに対応していません。根本的な解決方法はないので、おとなしくファイルを自分のリポジトリに追加しましょう。

    おわりに

    いくつか作業は必要でしたが、割とあっさりRackアプリが動いてしまったのは驚きでした。他の環境で動作する状態が崩れていないのがすばらしいですね。

    GAEのイメージでこの手のサービスは独自のルールがあって面倒なのではないかと思っていたのですが、Herokuではそんなことはまったくなく、もっと早く手を出していればよかった、というのが率直な感想です。

    参考