ROCHAS

MiddlemanをHerokuで公開して独自ドメインにリダイレクトする

HerokuはRuby、Java、Python、Scala、Node.jsなどのアプリケーションをGitを使ってデプロイ環境を構築できるプラットフォームですが、Ruby製であるMiddlemanでつくったサイトもHerokuで公開すると便利です。

通常Middlemanでは、一旦ビルドした静的ファイルを手動でFTPへアップロードする必要がありますが、Herokuを使うとビルドからデプロイまでgitコマンドを打つだけで済んでしまいます。

RailsやSinatraとかと共通する部分と、Middlemanならではのところがあるので一通り流れを書いておきます。

Agenda

  1. MiddlemanをHerokuにデプロイする
  2. HerokuのURLを独自ドメインに割り当てる
  3. Rack::RewriteでHerokuのURLから独自ドメインにリダイレクトする

Heroku

1. MiddlemanをHerokuにデプロイする

MiddlemanにProcfileを作成する

MiddlemanプロジェクトのルートディレクトリにProcfileを作成し、以下のコマンドを書きます。

web: bundle exec middleman build && bundle exec middleman server -p $PORT

Herokuで割り当てられたPORTでビルドしてサーバーを起動する、といった内容です。
とりあえずはこれでよいのですが、もしリダイレクト用にconfig.ruを作成している場合は内容が変わりますので後述します。

Herokuアカウント作成とHeroku Toolbeltのインストール

  • Herokuの公式サイトから、アカウントを作成します。
  • Heroku ToolbeltからHeroku CLIをインストールします。ワンボタンなので簡単。
    システムの/usr/local/heroku/binにインストールされるのでどのプロジェクトからもHerokuコマンドが使えるようになり、一度入れておけば以降は自動でアップデートされます。

MiddlemanプロジェクトをHerokuリポジトリに登録する

コマンドラインからHerokuにログインします。登録時のEmail:とPassword:を聞いてくるので入力してください。

$ heroku login
--------------------------
Enter your Heroku credentials.
Email: 
Password:

ルートディレクトリに移動し、MiddlemanプロジェクトをHerokuリポジトリに登録します。
好きなURLにしたい時は引数を指定します。指定しなければランダムなURLが指定されます。

$ cd ~/[SITE_DIR]
$ heroku create [SITE_NAME]
--------------------------
Creating [SITE_NAME]... done, stack is cedar
http://[SITE_NAME].herokuapp.com/ | git@heroku.com:[SITE_NAME].git
Git remote heroku added

登録されたURLとHerokuのリモートリポジトリが表示されるので確認しておきます。

Gitを使ってHerokuにpushする

いよいよHeroku上のリモートリポジトリにGitを使ってpushします。

$ git add .
$ git commit -m'deploy Heroku'
$ git push heroku master

30秒位待ってできたかなと思ったら$ heroku openhttp://[SITE_NAME].herokuapp.com/にアクセスできれば成功!
もしエラーになってしまったら$ heroku logsで確認します。

2. HerokuのURLを独自ドメインに割り当てる

HerokuのURLでもいいんだけどせっかくなので独自ドメインで公開できるようにしていきます。

変更前 http://example.herokuapp.com/
変更後 http://www.example.com/

ドメインにはwwwなしのルートドメインと、wwwなどが付いたサブドメインがありますが、2013.8.29のHerokuによると、HerokuではAレコードでwwwなしのドメインを設定することへ制限がかかるようになったとのことです。以前までは普通にできたので、固定のIPアドレスも配布されていたのですが、現在はランダムな値になり以前のIPアドレスを設定してもうまくいきません。 どうしてもwwwなしがいい場合は、別途ALIASを設定できるDNSサーバーを利用しなくてはなりません。その方法についてはまた別の機会にして、今回はサブドメインでの方法を書きます。

Heroku側の設定

ターミナルからHerokuに独自ドメインを設定します。

$ heroku domains:add www.example.com

DNSサーバー側の設定

ご利用のDNSサーバーから、CNAMEでHerokuのホストネームを設定します。サブドメインはwwwでなくても構いません。

Record Name Target
CNAME www example.herokuapp.com

レンタルサーバーによって最後comの後にドット. が必要な場合もあるのでそれに従ってください。

ちゃんと設定できたかを確認します。

$ host www.example.com

確認ができたら成功です。実際にURLに反映するまでには時間がかかります。

3. Rack::RewriteでHerokuのURLから独自ドメインにリダイレクトする

このままでは2つURLが存在してしまうので、HerokuのURLにアクセスがあった場合は独自ドメインに301リダイレクトさせ、また存在しないページにアクセスがあった場合は404リダイレクトでPage Not Foundページを表示させるように、Rack::RewriteというGemを使います。
Rack::RewriteとはHerokuなど、サーバ側は何もいじれないような場合でもリダイレクトなどの処理を実行できるRackミドルウェアです。

Rack::RewriteをGemインストールする

まずはGemfileに以下を記述し、Rack::Rewriteをインストールします。

gem "rack-rewrite", "~> 1.4.1"
$ bundle install

RubyGem / Github

config.ruを作成する

ルートディレクトリにconfig.ruを作成し、以下の301と404のリダイレクトルールを書きます。真ん中辺りのwww.example.com2箇所は正しいURLに置き換えてください。

module Rack

  class TryStatic

    def initialize(app, options)
      @app = app
      @try = ['', *options.delete(:try)]
      @static = ::Rack::Static.new(lambda { [404, {}, []] }, options)
    end

    def call(env)
      orig_path = env['PATH_INFO']
      found = nil
      @try.each do |path|
        resp = @static.call(env.merge!({'PATH_INFO' => orig_path + path}))
        break if 404 != resp[0] && found = resp
      end
      found or @app.call(env.merge!('PATH_INFO' => orig_path))
    end
  end
end

require 'rack/rewrite'
use Rack::Rewrite do
  r301 %r{.*}, 'http://www.example.com$&', :if => Proc.new {|rack_env|
    rack_env['SERVER_NAME'] != 'www.example.com'
  }
end

use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html', 'index.html', '/index.html']

run lambda{ |env|
  not_found_page = File.expand_path("../build/404.html", __FILE__)
  if File.exist?(not_found_page)
    [ 404, { 'Content-Type'  => 'text/html'}, [File.read(not_found_page)] ]
  else
    [ 404, { 'Content-Type'  => 'text/html' }, ['404 - page not found'] ]
  end
}

こちらのコードは上記のサイトを参考にしつつ、@tkawaさんに教えていただきました。
Heroku全体的には@satococoaさんやRails Girlsで教えていただきました。ありがとうございます!

Procfileを修正する

Procfile を以下のように書き換えます。

web: bundle exec middleman build && bundle exec rackup -p $PORT

middleman serverの部分をrackupにすることで、config.ruが実行されリダイレクトできるようになります。

ちなみにconfig.ru.rurackupの略で、Rackベースのサーバーに処理を実行するためのファイルなのだそう。
MiddlemanにはRackミドルウェアへアクセスするこの仕組みがあるからこそ、HerokuのようなRackベースのサーバーにホスティングできるのですね。
MiddlemanやHeroku、便利なのはもちろんだけど、RailsやRuby、サーバーまわりも垣間見れてとってもよいです。 ソースはGithubで公開していますのでもしよかったら。。