はじめに
僕の妻は兵庫県西脇市でCoupé Baguette (クープ バゲット)という小さなパン屋さんを営んでいます。
この店のwebサイトは僕がRailsで作り、Herokuでホスティングしています。
去年の1月に全面改装して以来、あまり触ってこなかったのですが、RubyやRailsのバージョンも古くなってきたので、そろそろ最新化したいな~という気持ちが高まっていました。
というわけで先日、ようやくサイトをアップデートすることができました。
見た目の変化は全くありませんが、内部的にはRubyのバージョンが1.9.3から2.1.1に、Railsのバージョンが3.2から4.1にアップデートされています。
さらに、TurbolinksやCloudFlare(CDN = Contents Delivery Network)も導入して、サイトの表示を高速化してみました。
今回のエントリはその内容を色々まとめてみます。
例によってまた長くなってしまったので、アウトラインを先に載せておきますね。
- Ruby & Railsのバージョンアップ
- フィーチャスペックを書いて一通り動作することを確認できるようにする
- Rubyのバージョンアップ
- Rails 4.0へのバージョンアップ
- 念のためRails 3.2系の最新版にアップデート(ついでにRSpecも)
- gemのアップデート
- bundle exec rake rails:updateの実行
- テストを全部パスさせる
- 警告を潰す
- 目視で確認する
- ステージング環境にデプロイ
- 本番環境にデプロイ
- Rails 4.1へのバージョンアップ
- Turbolinksで画面遷移を高速化
- CloudFlareでHerokuのlegacy routing問題を解消&高速化
- 「Aレコード + ネイキッドドメイン」の裏技が使えなくなる
- 「CNAME flattening」を実現してくれるCDNサービス、CloudFlare!
- ウイザード通りにやれば設定も簡単
- CDNで画像の表示もサクサク!
それでは本編へどうぞ!
Ruby & Railsのバージョンアップ
さて、RubyとRailsのバージョンを上げるなら何から始めましょう?
Gemfileを書き換えて、bundle update?
いやいや、いきなりそこから始めちゃダメです!
まずはテストを書きましょう。
フィーチャスペックを書いて一通り動作することを確認できるようにする
「最初からテストは当然書いとるわい!」という人ならOKですが、このwebサイトではまだテストを全然書いていませんでした。
テストを書いていないと、「不具合が出なくなるまで手作業で動作確認を何度も繰り返す」か、「壊れていないことを祈ってリリースする」のどちらかになってしまいます。
どちらに転んでも非効率&リスキーなので、ちゃんとテストを書いて自動実行できるようにしておきましょう。
Railsのテストを書く場合はモデルやコントローラなど、いろんなレベルでテストを書くことができますが、こういうケースであればやはりフィーチャスペックを書くべきでしょう。
なぜなら、ビューからデータベースアクセスまでまとめてテストできるからです。
いわゆる「統合テスト」っていうやつですね。
というわけで今回はまず、テストを書くことから始めました。
といってもこのサイトは単純な「お店のホームページ」なので、テストもとても単純です。
以下のように、リンクをクリックしてエラーなくページが開けるか、という点を一通り確認しています。
feature "User" do scenario "internal pages" do visit root_path within 'h2' do expect(page).to have_content 'Information' end click_link 'Concept' within 'h2' do expect(page).to have_content 'Concept' end click_link 'Menu' within 'h2' do expect(page).to have_content 'Menu' end click_link 'Shop Info' within 'h2' do expect(page).to have_content 'Shop Info' end click_link 'Contact' within 'h2' do expect(page).to have_content 'Contact' end end end
その他、問い合わせメールが送信できることや外部のサイトへのリンクも正しく動いていることもテストしました。
詳しくはGitHubのテストコードを見てください。
https://github.com/JunichiIto/cb/blob/master/spec/features/user_spec.rb
また、「Railsのテストを書くのが苦手!!」「フィーチャスペックってよくわからない!!」という「テスト初心者」の方に打って付けの電子書籍があります。
こちら、「Everyday Rails - RSpecによるRailsテスト入門」です!
Everyday Rails - RSpecによるRailsテスト入門 - Leanpub
これを読めば、あなたも不具合が出なくなるまで手作業で動作確認を何度も繰り返したり、壊れていないことを祈ってリリースしたりする人生から解放されます!
まだ買ってない人はぜひお買い求めください(^o^)/
Rubyのバージョンアップ
テストが書けたら、いよいよRubyとRailsのバージョンアップに進みましょう。
まずはRubyのバージョンアップからです。
Rubyは後方互換性がかなり保たれているので、ほとんどトラブルなくバージョンアップできると思います。
手順としてはこんな感じです。
- 念のため rails4 移行用のgitブランチを作っておく
- rbenv local 2.1.1 みたいなコマンドを打って、 .ruby-version を更新 (rbenvを使っている場合)
- Gemfileに ruby "2.1.1" を指定
- bundle install を実行
- bundle exec rspec でテストを実行
たいていの場合、テストは普通にパスすると思います。
でももし、テストを書いていなかったら、全部手作業で確認しなきゃいけないんですよね。。。
「ちゃんと動くだろう」と思っていても、テストなしでバージョンアップするのはやはり怖いです。
Rails 4.0へのバージョンアップ
さて、次はRailsのバージョンアップです。
いきなり4.1に上げるのではなく、まず4.0に上げて、それから4.1に移行させます。
また、こちらはRubyのバージョンアップに比べると結構苦労すると思います。
念のためRails 3.2系の最新版にアップデート(ついでにRSpecも)
バージョンを飛躍させすぎると予期せぬトラブルの原因になりそうなので、いったんRails 3.2系の最新版(執筆時点では3.2.17)にアップデートしてテストを実行しておきましょう。
ついでにRSpec(rspec-rails)も2.14系の最新版にアップデートしておくと安心です。
RSpec 2.11から記法が変わっているので、古いバージョンのRSpecでテストを書いていた場合はこのタイミングで新しい記法に直しておくことをオススメします。
なお、記法のアップデートはtranspecというgemを使うと一気に自動コンバートできて大変便利です。
gemのアップデート
まずはGemfileのRailsバージョンを4.0.4(執筆時点での4.0系の最新版)に上げます。
gem "rails", "4.0.4"
group :assets は不要になったのでブロックを消して、関連するgemのバージョンも上げます。
gem 'sass-rails', '~> 4.0.3' gem 'coffee-rails', '~> 4.0.1' gem 'uglifier', '>= 1.3.0'
そして、 bundle update rails を実行します。
が、たぶんこのあたりでいろいろとgemの依存関係に起因する警告が出て、すんなりとバージョンアップできないと思います。
他のgemを先にバージョンアップしたり、bundle update rails xxx のように他のgemと一緒にバージョンアップしたりして、全体のバージョンアップをなんとか完了させてください。
bundle exec rake rails:updateの実行
gemのアップデートが終わったら、今度は bundle exec rake rails:update を実行します。
これを実行すると、config周りのファイルが追加されたり上書きされたりします。
routes.rb以外のファイルはいったん上書きしてしまって、diffを見ながら必要な設定を手作業で元に戻す、というのがスタンダードな手順になると思います。
rails:update がインテリジェントに設定をマージしてくれたらうれしいんですけどね~。
というわけで、設定の戻し忘れに注意してください。僕もたまにやってしまいます。
テストを全部パスさせる
さて、ここまでくれば起動に向けた準備が整いました。
bundle exec rspec でテストを実行させましょう!
・・・テストは全部パスしましたか?
おそらく、最初からテストが全部パスする確率は10%ぐらいなんじゃないでしょうか。
全部コケるか、一部のテストがfailすることがほとんどのような気がします。
というわけで、発生したエラーを一つずつ潰していきましょう。
発生するエラーはケースバイケースなので、ここで具体的な方法を示すことはできません。
ただし、Rails 4に関するネット上の情報はかなり出そろってきているので、エラーメッセージをコピペしてググれば、多くの場合「あー、これこれ!」という情報にたどり着けると思います。
がんばってテストをオールグリーンにしてください。
警告を潰す
テストがオールグリーンになれば完璧!・・・ではなくて、たぶん警告も表示されていると思います。
この警告も潰していきましょう。
よくある警告は「ラムダを使わずにscopeの条件を書いている」「find_all_by_xxxのようなファインダメソッドを使っている」「"Are you sure?"のような確認メッセージ(:confirm)が data: で囲まれていない」といったあたりかなーと思います。
こちらは結構単純作業で潰していけます。
警告をゼロにしてスッキリさせましょう。
目視で確認する
テストがしっかり書けていても、100%壊れていないことを保証することはできません。
画面の表示が崩れていたり、テストを書かなかった部分が壊れていたりする可能性もあります。
念のため、実際に画面を操作して問題が起きていないことを確認してください。
ステージング環境にデプロイ
ローカルでちゃんと動くようになったので、次はいよいよHerokuへのデプロイです!
といっても、いきなり本番環境にデプロイするのはリスキーです。
Herokuにインスタンスをもう一個立てて、ステージング環境を作っておきましょう。
また、Herokuへデプロイする前にHeroku用の準備を整えておく必要もあります。
この作業については以下のドキュメントが参考になると思います。
ポイントは rails_12factor gemを入れておくことと、Webserverとして Unicorn を使うことかなーと思います。
詳しくは上記のドキュメントを参照してください。
では、Herokuにデプロイしましょう。
エラーなくデプロイできましたか?ブラウザで開いたときも問題なく動いていますか?
経験上、このときの確率も五分五分のような気がしています。
意外と変なエラーが出てうまく起動しなかったりするんですよね~。
エラーが出たらHerokuのログを見たりして、エラーの原因を調べてください。
ちなみに僕はPapertrailというアドオンを使って、ブラウザからログを見られるようにしています。
本番環境にデプロイ
ここまで来てようやく、本番環境へのデプロイです。
ステージング環境でもちゃんと動作確認できていれば、本番環境でコケるリスクはかなり低いと思います。
どうでしょう?うまくデプロイできましたか?
僕の場合は一発で問題なくデプロイできました。
サイトが動かなくなると困る!というような重要なシステムであれば、すぐ以前のバージョンに戻せるような手順書も事前に作成しておきましょう。
これまでずっとgitのブランチを分けていた場合は、mainブランチにマージすることもお忘れなく!
Rails 4.1へのバージョンアップ
Rails 4.0が問題なく動いたら、続いて4.1にバージョンアップします。
手順は上で書いた「Rails 4.0へのバージョンアップ」以降と同じです。
マイナーバージョンアップなので、3.2→4.0のときほどトラブルは起きにくいと思いますが、それでも注意しながら実施してください。
なお、Rails 4.1特有のエラーは「ActionView::Template::Error (795: unexpected token at {I"session_id: ETI"...」かもしれません。
このエラーが出たらconfig/initializers/cookies_serializer.rbを以下のように書き換えます。
# config/initializers/cookies_serializer.rb Rails.application.config.action_dispatch.cookies_serializer = :hybrid
詳しくはこちらのブログ記事をどうぞ。(僕も参考にさせてもらいました)
Turbolinksで画面遷移を高速化
さて、RubyもRailsも最新バージョンになったのでこれで終わり!・・・でもいいんですが、せっかくバージョン上げたのでRails 4の新機能を使いたくなりました。
「そういえばTurbolinksを使うと画面遷移が速くなるんだったっけ?」ということを思い出し、ちょっと使ってみることにしました。
ただし、このTurbolinksはその仕組み上、JavaScript関係で予期せぬ不具合が起きやすいことでも有名(?)です。
幸い、Coupé Baguetteのwebサイトでは自前のJavaScriptを全く書いていなかったので、Turbolinksを導入してもトラブルは起きにくいはずだと考えました。
Turbolinksの導入はとても簡単です。
- Gemfileに gem "turbolinks" を追加
- application.jsに //= require turbolinks を追加
- = javascript_include_tag "application" をbodyタグ内に書いていた場合は headタグ内に移動
と、こんな感じです。
さあ、これでサイトの画面遷移が爆速に!!・・・なったかどうかは不明です。
「まあまあ速くなったかな?」というぐらいの変化はありました。
どちらかというと、開発者(=僕)の自己満足レベルですね(苦笑)。
また、自前のJavaScriptをバリバリ書いてるようなサイトだと落とし穴に何度もハマりそうなので、個人的にはあまりオススメしません。
(まだ僕がそこまでTurbolinksを使いこなせていないから、というのもありますが)
CloudFlareでHerokuのlegacy routing問題を解消&高速化
Herokuを使っている開発者の方は、もしかすると「Action Required: Legacy Routing Ending」という件名のメールを受け取っているかもしれません。
簡単に言うと、昔の「xxx.heroku.com」は2014年9月22日で使えなくなりますよー、というメールです。
「Aレコード + ネイキッドドメイン」の裏技が使えなくなる
「あれ?今Herokuで動かしているアプリは全部 xxx.herokuapp.com に移行したから大丈夫なはずなんだけど?」と思ったのですが、 https://legacy-routing.herokuapp.com/ という確認用サイトを開いてみると、Coupé Baguetteのwebサイトは「Legacy!」の赤バッジが付いていました。
メッセージには「This domain points directly to IP addresses of the legacy router. Update the CNAME record pointing to xxx.herokuapp.com. If it's a naked domain, use DNS provider that supports ALIAS / CNAME flattening.」と書いてあります。
つまり、DNSのAレコードでHerokuのレガシーなIPアドレスを指定しているのが問題、ということみたいです。
Herokuで独自ドメインを使う場合は「CNAME + サブドメイン付きのURL」で運用するのが原則だとはわかっていたのですが、「AレコードでIPアドレスを直接指定すれば、ネイキッドドメインも使える」という裏技でこれまで coupe-baguette.com を運用してきました。
しかし、そろそろこの裏技も使えなくなってしまうみたいです。
「CNAME flattening」を実現してくれるCDNサービス、CloudFlare!
「どうしようかな~。サブドメイン付きで www.coupe-baguette.com にしなきゃいけないのかな~。でもURLが変わるのはイヤだし、短い方がスッキリするもんな~。かといって、別の有料ネームサーバーに乗り換えるのもそれはそれで・・・(ごにょごにょ)」とか思いながら、ネットを検索していたところ、CloudFlareというサービスを見つけました。
本来はCDN(Contents Delivery Network)を提供するためのサービスなのですが、ネームサーバーとして利用することもでき、Herokuで「CNAME + ネイキッドドメイン」を実現可能にするCNAME flatteningという機能が使えるみたいです。
しかも、SSLを使ったりしないなら帯域制限無しで無料で使えるとのこと!
ウイザード通りにやれば設定も簡単
「本当にそんなうまい話があるのか?」と少し不審に思いながらも、とりあえず試しにSign upしてみました。
Sign upを進めるとウイザード形式でサイトの設定が始まるんですが、ウイザードの言うとおりに設定を進めると、あら不思議、それほど面倒な手続きもなくネームサーバーの移行が完了してしまいました!
しかも、今まで付いていた「Legacy!」の赤バッジも外れています。
In Use?の欄は Yes になっていますが、横の説明を見ると「CNAME flatteningを使ってそうだから問題なし」と書いてあります。
どうやらHerokuのlegacy routing問題は解決したみたいです!
CDNで画像の表示もサクサク!
しかも、ネームサーバーを切り替えるだけでサイトの静的ファイルがCDN経由で取得されるようになりました。
その証拠に、画面をリロードしてもログに jpg や png へのアクセスが出てこなくなりました。
また、東京にも配布ポイントがあるので、画像の読み込みも確かに速くなっています。
今までは大きな画像が完全に表示されるまで1秒ぐらい待たされましたが、CloudFlareにしてからはパッと表示されます。
legacy routing問題も解決できたし、CDNでページの表示も速くなったし、その上帯域制限無しで無料だなんて素晴らしすぎる!
まあもしかすると、いつか無料プランに制限ができて有料プランに移行せざるを得なくなったりするのかもしれませんが、今のところは問題ないので、しばらく使い続けようと思います。
まとめ
というわけで、Ruby 2.1.1/Rails 4.1に移行し、TurbolinksとCloudFlareのCDNのおかげで高速化されたCoupé Baguetteのwebサイトがこちらになります。
Before/Afterを比較できないので、「どこまで速くなったの?」というのがわかりにくいと思いますが、体感的にはサクサクとページの切り替えができてるんじゃないかと思います。
ちなみにGTmetrixでスピード測定すると A/A になっています。
なかなかいい感じです。
まあ、妻の店は地方の小さなパン屋なので、別にここまでがんばらなきゃいけないサイトというわけでもないのですが、僕の技術者としての自己満足を追求するためにいろいろと遊ばせてもらいました(苦笑)。
ソースコードはGitHubに置いていますので、詳しく見てみたい方はこちらをそうぞ。
https://github.com/JunichiIto/cb
(諸事情により、上記リポジトリはprivateリポジトリに変更しました)
それにしても、独自ドメインの更新料が年間1000円程度かかっている以外はすべて無料のサービスでこのwebサイトが成り立っている、というのが自分でも信じられないですね。
これだけあれこれやってほぼ無料、というのはすごい時代だな~と思います。
あと、最後に一言。
テスト重要!!
まだテストが書けない人は早く書けるようになりましょうね〜!
お知らせ その1: オンライン販売の予告とFacebookページの紹介
Coupé Baguetteは地方の小さなパン屋ですが、今月ぐらいからオンライン販売を始めるかもしれません。
妻のパンを食べてみたい!という方はFacebookページに「いいね!」をしてもらうと、販売開始のお知らせをキャッチしやすくなると思います。
また、オンライン販売に興味がある方もない方も、みなさん「いいね!」で妻の店を応援してもらえると嬉しいです。
よろしくお願いします (^ ^)
https://www.facebook.com/coupebaguette
2014.05.07 追記
2014年5月11日よりオンラインショップをオープンします!URLはこちらです。
http://shop.coupe-baguette.com/
お知らせ その2:「Everyday Rails - RSpecによるRailsテスト入門」もよろしくお願いします
僕が翻訳したRSpec関連の電子書籍です。PDF、Kindle、iPad等で読めます。
「Railsのアプリは書けるようになってきたけど、テストを書くのはまだまだ苦手」というあなたにピッタリの一冊です!
Everyday Rails - RSpecによるRailsテスト入門 - Leanpub
あわせて読みたい
「第一回 プログラマ向けデザイン勉強会」の内容を参考にして妻のパン屋のWebサイトをリニューアルしてみた - give IT a try
店のWebデザインを変更したときの一部始終をいろいろ書きました。
サンプルストーリーで理解するDNSの設定方法と周辺知識(改) - give IT a try
「DNS?Aレコード??CNAME???・・・実はよくわかっていません(><)」という方が読むと参考になるかもしれません。
独学の主婦が自宅で開業したパン屋さん「クープ バゲット」の2013年を夫が振り返ってみる - give IT a try
妻の店について詳しく知りたい方はこちらのエントリをどうぞ。