こんにちは。Ruby on Rails(以下Rails)のリードエンジニアを担当している橋本と申します。
先日、6/28にメドピアでは、MedBeer - Rails 5.1での開発についてというイベントを開催しました。このイベントでは今年(2017年)4月にリリースされた、Rails 5.1の新機能や開発環境に関してさまざまな発表が行われ、来場したエンジニアの方からも好評のうちに終えることができました。
このイベントの直前に、今までRails 5.0で動いていたメドピアのWebアプリケーションをRails 5.1へとアップグレードを行い、当日、その内容の発表を行いました。今回のブログ記事ではその発表を元に、アップグレードのためにやった6つのことについて紹介を行います。
スムーズにアップグレードのするためにやった6つのこと
- 調査用ブランチと本番用ブランチを用意
- 専用の検証環境を用意する
- デプロイ時の差分を小さくしておく
- アップグレードガイドとリリースノートに目を通す
- アップグレード前後でコードを正しく修正する
- アップグレード時に大きな変更を行わない
1. 調査用ブランチと本番用ブランチを用意
Rails 5.1をはじめ、アップグレードされたGemを安全に本番環境に投入できるように、以下の2つのブランチを作成しました。
調査用ブランチ | 本番用ブランチ | |
---|---|---|
主な動作環境 | 開発環境 | 本番環境 |
目的 | 全体の作業量・修正の見通しをつける | 安全に本番環境で動作させる |
アップグレード対象 | Rails 5.1に依存するGemのみ | すべてのGem |
Gemのグループ分け | しない | する |
調査用ブランチ
開発環境で、手早くRails 5.1で動かせる状態にするためのブランチで、アップグレードに必要な作業量と、コード修正の見通しをつけるためのものです。このブランチでは、Rails 5.1に依存するGemのみ手早くアップグレードを行いました。
まず、Gemfile
に以下のように指定を行い、bin/bundle update rails
を実行します。
gem 'rails', '~> 5.1'
コマンドの実行後に、下記のようにRails本体に含まれるGem(具体的にはactivesupport
、activerecord
など)へ依存関係のあるGemの競合解消に失敗したというエラーが表示されるので、1つ1つアップグレードしていきます。
Bunlder could not find compatible versions for gem "activesupport": In Gemfile: act-fluent-logger-rails was resolved to 0.3.1, which depends on activesupport (< 5.1, >= 4)
この場合は、act-fluent-logger-railsのバージョンを0.3.1がactivesupportの5.1より下のバージョンでしか動かないという内容なので、Gemfile
でバージョン固定を外すか、Rails 5.1に対応したバージョンを指定して、bin/bundle update act-fluent-logger-rails
を実行します。
エラーメッセージが表示されなくなるまで各依存パッケージのアップグレードを行うと、Rails 5.1で動作できる状態になります。(コードの修正は別途行う必要があります。)
本番用ブランチ
本番環境でRails 5.1を動かすためのブランチで、調査用ブランチと異なり、(可能な限り)全てのGemのアップグレードを行なったものです。次節のようなGemのグループ分けを行い、各グループを順番にアップグレードを行いました。
一気に全体のbin/bundle update
を行わず、グループに分けたGemのアップグレードを行ったのは、後述のデプロイの差分を小さくするためと、グループごとに利用する環境や検証の作業が異なるためです。
本番リリースに必要なコードの修正も最終的にこのブランチにコミットを行いました。
Gemのグループ分け
本番用ブランチでのGemのグループ分けは以下のようにしました。
a) パッチバージョンのみアップグレードしたもの
あるGemのバージョンをX.Y.Z(X, Y, Zは数字)とした場合、最新バージョンでZの数字のみ上がっていて、大きな仕様変更はないGem
b) 開発環境、テスト環境のみ使用するGem
例)bullet, rspec-railsなど
開発支援ツールやテストツールなど本番環境での動作の必要がないGem
c) 本番環境でも使用するGemで影響が少ないもの
例)activerecord-import, draperなど
Railsの基本機能拡張など、本番環境での動作を行うGem
d) 本番環境でも使用するGemで影響が大きいもの
例)administrate, sidekiqなど
管理画面やジョブ実行システムなど、上記3よりも大規模なGem
e) Rails本体
Rails公式のGem(activerecord, actionpack, railtiesなどを含む)
グループ分けしたGemはアップグレード後に、各環境のサーバーで検証が行われました。
2. 専用の検証環境を用意する
今回は、通常のデプロイフロー(ステージング環境で検証して本番環境へリリースという流れ)を妨げないように、Rails 5.1アップグレード用の検証環境を用意しました。
以下の図のように、影響が小さいGemのアップグレードの場合は、通常のデプロイフローに乗せ、影響が大きいGemのアップグレード「d) 本番環境でも使用するGemで影響が大きいもの」と「e) Rails本体」の場合は、検証環境で手動テストを含む十分な検証を実施してから、本番環境へのリリースを行いました。
3. デプロイ時の差分を小さくしておく
本番環境へのリリース(デプロイ)の際には、できるだけ以前のリリースとの差分が小さくなるようにしておきました。理由としては、差分が大きくなると不具合が発生した際に、原因(どのGemのアップグレード・どのコードの変更によるものか)の判別が難しくなるからです。
特に、Rails本体を5.0から5.1へアップグレードしてデプロイする際にはリリースの差分が以下のようになるようにしました。
Gemfile.lock
の変更がRails本体に含まれるGemのアップグレードのみとなっている。- 他のコードも、5.1でしか動かないコードの変更のみとなっている。
このようにして、できるだけRails 5.0の段階で準備を行っておき、その後のRails 5.1でのリリースでの変化を小さくしておくようにしておきます。
4. アップグレードガイドとリリースノートに目を通す
アプリケーション側でどの部分のコードを変更するか把握するために、以下の公式のアップグレードガイドをチェックしました。
特に、Rails 5.0からRails 5.1へのアップグレードに関する部分に目を通しておきます。
Rails 5.1の変更内容や新機能の詳細に関しては、リリースノートに書かれているので、こちらもチェックを行いました。
ここで非推奨や廃止となったメソッドなどの記述についても詳しく書いており、アプリケーションで使用していないかチェックを行いました。
5. アップグレード前後でコードを正しく修正する
上記のアップグレードガイドとリリースノートの内容を元に、コードの修正を行いました。コードの修正は、Rails 5.1へのアップグレードの前後で行う必要がありました。
アップグレード前 (Rails 5.0) の対応
テストコード実行時や、アプリケーションの起動時のログにDEPRECATION WARNING
(非推奨の警告)が出力されている場合は、該当する記述を修正する必要があります。
has_many
のclass_name
オプションにクラスを指定している部分を修正
モデルのhas_many
のオプションのclass_name
にクラスを直接渡していた部分で上記の警告が出力されていたので、文字列に変更を行いました。
params
をハッシュとして扱っている部分を修正
Rails 5.0からActionController::Parameters
がHash
(のサブクラスのActiveSupport::HashWithIndifferentAccess
)から継承されなくなったのに伴い、コントローラのparams
に対して、symbolize_keys
を実行している箇所で警告が発生していたいので、修正を行いました。
アップグレード後 (Rails 5.1) の対応
各種設定ファイルを5.1に合わせて更新していきました。
bin/rails app:update
を実行
新しいバージョンに対応した設定ファイルの作成や、更新を行うタスクのbin/rails app:update
を実行します。このタスクを実行すると独自にカスタマイズした内容が上書きされる可能性があるので、実行の前後のファイルの差分を確認して妥当な内容にする必要があります。
config.load_defaults 5.1
を設定
Rails 5.1からconfig.load_defaults
というメソッドが提供されるようになり、バージョンごとの推奨の設定を読み込めるようになりました。今回は、config/application.rb
に以下の内容の記述を行いました。
config.load_defaults 5.1
config/secrets.yml
を読み込む際のハッシュのキーを文字列からシンボルに
config/secrets.yml
に記述されている内容がネストされている場合、その内容を参照する際に、これまでハッシュのキーとして文字列を指定していましたが、Rails 5.1からはシンボルにする必要があります。
Rails 5.0までは以下のように参照していたものが、
Rails.application.secrets[:smtp_settings]["address"]
Rails 5.1からは、以下のようにシンボルにしないと参照できなくなっています。
Rails.application.secrets[:smtp_settings][:address]
6. アップグレード時に大きな変更を行わない
今回は、Railsのアップグレードと同時に工数が発生するような大きな変更を加えないことにしました。 具体例として、プライマリキー(id)をBIGINT型にしないようにしました。
プライマリキー(id)をBIGINTにしない
Rails 5.1では新しく作成されるテーブルのプライマリキー(id)の型がBIGINTになるという大きな変更が加えられました(PostgreSQL/MySQLの場合)。
メドピアでは、特に大きな数のidを扱う予定がなく、予期せぬ不具合の発生を防ぐため、これまで通りidの型としてINT (integer)を使うという選択を行いました。
これに伴い、マイグレーションを新たに実行した際に、既存のテーブルとRails 5.1で作成した新しいテーブルでidがINT型になるように以下のような対応を行いました。
既存のテーブルへの対応
既存のテーブルに関しては、bin/rails db:migrate:reset
を再実行して、生成されたdb/schema.rb
のcreate_table
の部分にid: :integer
のオプションがつくようにしました。
Rails 4.2で作成された古いマイグレーションファイルを実行する際にバージョン表記がないという
エラーになってしまっていたので、ActiveRecord::Migration
の部分に例えば以下のようにバージョンを付与しました。
class CreateUsers < ActiveRecord::Migration[4.2]
新規のテーブルへの対応
Rails 5.1で作成されたマイグレーションファイルをそのまま実行すると、idがBIGINTになってしまうので、
以下のようにマイグレーションファイルのcreate_table
の部分にid: :integer
のオプションをつけるようにしています。
class CreateQuestions < ActiveRecord::Migration[5.1] create_table :questions, id: :integer do |t| ...
まとめ
Railsをスムーズにアップグレードするために上記の6つのことを実施しました。
現在はRails 5.1環境で安定して稼働しており、フロントエンド周りの新機能を生かした開発なども進めることができています。
今回のアップグレードを通して、古いRailsやGemを使い続けることによるセキュリティへのリスクを回避でき、また、チーム内のメンバーのRailsに対する意欲や関心を高めることができたのはよかったと思います。
Rails 5.1を題材に取り上げましたが、内容としては過去や将来のRailsのバージョンに対しても適用できる内容だと思うので、参考にしていただけると幸いです。
是非読者になってください(ง `ω´)ง
メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!
■募集ポジションはこちら
https://medpeer.co.jp/recruit/entry/
■開発環境はこちら