Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Railsバージョンアップを 段階的に行うために Rails3/4並行稼動させる仕組み を作ってる話

6,481 views

Published on

Rails Upgrade Casual Talks 2016/03/28
Railsバージョンアップを 段階的に行うために Rails3/4並行稼動させる仕組み を作ってる話

Published in: Engineering
  • Be the first to comment

Railsバージョンアップを 段階的に行うために Rails3/4並行稼動させる仕組み を作ってる話

  1. 1. Railsバージョンアップを 段階的に⾏うために Rails3/4並⾏稼動させる仕組み を作ってる話 Rails Upgrade Casual Talks 2016/03/28 @minamijoyo
  2. 2. ⾃⼰紹介 •  森⽥ 真之 (Masayuki Morita) •  Twitter: @minamijoyo •  GitHub: minamijoyo •  CrowdWorks Inc. •  インフラ周りの改善してる •  最近はRails3.2=>4.2アップ デートの検証中
  3. 3. はじめに •  今⽇お話すること •  複数Railsバージョンを並⾏稼動させる取り組み •  なぜ並⾏稼動させるのか •  開発フロー(ブランチの運⽤) •  並⾏稼動の仕組み •  Gemfileの共存 •  リクエストの振分 •  Rails3.2/4.2を並⾏稼動のハマりポイント •  リクエストの互換性、データの互換性 •  もうすぐproduction投⼊するので忘れてそうなのあれば教 えて下さい •  今⽇お話しないこと •  Rails5
  4. 4. CrowdWorks •  ⽇本最⼤級のクラウドソーシングサービス •  2012年3⽉〜サービス開始 •  会員数85万⼈、発注クライアント数12万社
  5. 5. CrowdWorksシステム概要 Found (Elasticsearch) RDS (MySQL) ElastiCache (memchached) EC2ELB •  システム構成はよくあるWebサービス •  Railsはまだ3.2系が稼働している
  6. 6. なぜ並⾏稼動させるのか •  ひとことで⾔うと⼩さくリリースしていきたい •  ⼀発バージョンアップはリスキー •  問題の原因は特定できてもすぐ対応が難しいものもある •  モンキーパッチしてる箇所など •  バージョン依存のバグ修正を黙々してると⾃信をなくす •  ⾃動テストで⾒つけづらいバグもある •  アセットの読み込みに起因する問題、データに起因する問題 •  最終的な確認としてある程度は⽬視で確認したい •  全機能を⼀度に稼働確認するの⼤変 •  アプリケーションとしては業務系システムに近い •  仕事マッチング以外にも細々した機能がたくさんある •  サービス開発を全部⽌めるわけにもいかない •  Railsバージョン上げる度に毎回開発⽌めるの?
  7. 7. 開発フロー •  基本⽅針 •  Railsバージョンアップ対応の修正は互換性を保ち つつどんどんmasterブランチに⼊れていく •  差分が⼤きくなりすぎるとマージできなくなる
  8. 8. ブランチ運⽤ master develop rails4 feature-XXX rails4-XXX Production環境 staging環境 Rails3⽤テスト環境 ⼀⽇数回マージ してデプロイ 機能開発/バグ修正 バージョンアップ起因のバグ修正 rebaseして追いつき マージ Rails4⽤テスト環境 •  master: production環境にデプロイされてるもの •  develop: メインの開発ブランチ •  rails4: Rails4対応⽤の作業ブランチ •  Rails3/4⽤テスト環境をそれぞれ⽤意し、同じリビジョンをデプ ロイして挙動を⽐較できるようにする •  随時developブランチでrebaseして最近の変更も取り込み •  修正の稼働確認できたら適宜developにマージ
  9. 9. CIによる⾃動テスト •  master/develop/rails4ブランチ •  Rails3/4両⽅でCIによる⾃動テストでクロスビルド •  トピックブランチ •  デフォルトRails3でビルド •  CIのリソース効率化、実⾏時間短縮のため •  ブランチ名の⼀部にrails4が含まれるとRails3/4両⽅でビルド •  Rails4対応の修正はマージ前にテストしたい
  10. 10. 複数のGemfileを共存させる •  Gemfileを複数⽤意してBUNDLE_GEMFILE変数で制御 •  Gemfile: Rails3⽤ •  gemfiles/rails4.gemfile: Rails3/4の差分 •  Rails3でしか使わないgemをリストから削除 •  Gitリポジトリをしているものはsourceからも削除 •  Rails4でしか使わないgemをリストに追加
  11. 11. gemfiles/rails4.gemfileの例 # Rails3⽤のGemfileを読み込み eval_gemfile File.expand_path(File.join(File.dirname(__FILE__), '../Gemfile')) # Rails3でしか使わないものをリストから削除 ignored_gems = %w( rails … ) dependencies.delete_if do |g| # Rails 4では:assetsグループは廃⽌される ignored_gems.include?(g.name) || g.groups.include?(:assets) End # git リポジトリを指定しているものはsourceからも削除 ignored_git_source_uris = %w( git://github.com/xxxxx/xxxxx.git ) @sources.git_sources.delete_if do |source| ignored_git_source_uris.include?(source.uri) end # Rails4でしか使わないgemをリストに追加 gem 'rails', '~> 4.2.6’ …
  12. 12. BUNDLE_GEMFILEの制御 •  ローカル開発環境は環境変数で切り替えが簡単 •  capistrano •  現状remote_cacheで各サーバでbundle installしている •  リモート側の~/.bundle/configに設定すればサーバ単位で 切り替え可能 •  whenever •  現状バッチはwhenever経由でcrontabを⽣成している •  job_templateでcronジョブごとにBUNDLE_GEMFILEの exportを出し分けする
  13. 13. リクエストを振り分ける •  サーバ単位 •  ロードバランサからトラフィックの⼀部を流すだけで簡単 •  問題あればとりあえずバランサから切り離す •  機能的には全部⼀発リリース •  ユーザ単位 •  社内ユーザだけ先⾏リリースができる •  現状振り分ける仕組みがないので何がしか作りこみが必要 •  問題あっても影響は⼀部ユーザに限定される •  機能的には全部⼀発リリース •  URL単位(機能単位)=>これを採⽤ •  稼働確認できた機能から段階的に新バージョンに振り分ける •  現状振り分ける仕組みがないので何らか作りこみが必要 •  機能ごとにちょっとずつテストしてリリースできる •  問題あっても機能ごとに切り戻しできる •  今後も同じような段階リリースしていくのに便利そう
  14. 14. 並⾏稼動のシステム構成 •  Rails3/4⽤のサーバ群をそれぞれ⽤意 •  前段にnginxによるリバプロ層を追加しURLで振り分け Found (Elasticsearch) RDS (MySQL) ElastiCache (memchached) EC2 ELB EC2 ELB ELB EC2 (nginx) Rails3 Rails4
  15. 15. リバプロ層 •  基本はURLパスを⾒てどっちに振り分けるかを判断する •  完全に10:0じゃなくて9:1とか⽐率はnginxのupstreamコ ンテキストで調整できる •  両バージョンで発⽣するかどうかの切り分け •  Rails3しか発⽣しない潜在的な問題があると切り戻しリスク
  16. 16. nginx.confのイメージ http { resolver xxx.xxx.xxx.xxx valid=5s; upstream rails4 { server unix:/var/run/nginx_rails4.sock weight=9 fail_timeout=60; server unix:/var/run/nginx_rails3.sock weight=1; } upstream rails3 { server unix:/var/run/nginx_rails3.sock; } server { listen xxxx; server_name example.com; ... location / { proxy_pass http://rails3; } location ^~ /hoge/ { proxy_pass http://rails4; } server { listen unix:/var/run/nginx_rails3.sock; set $elb_rails3 "xxxx"; ... location / { proxy_pass http://$elb_rails3:80; } server { listen unix:/var/run/nginx_rails4.sock; ... } }
  17. 17. nginxのtips •  nginxでupstream先にELBを使う場合はDNS解決に注意 •  nginx起動時のIPアドレスがキャッシュされてしまう •  serverコンテキストならset変数を使うハックで回避可能 •  upstreamコンテキストではresolveオプションは有償 •  upstreamコンテキストで必要な負荷分散しつつ、Unixドメイ ンソケットを1段挟んで、serverコンテキストでDNS解決すれ ば無償の範囲でも可能 •  (参考)nginxのupstreamコンテキストで有償のresolveオプション を使わずに動的にDNS解決する •  http://qiita.com/minamijoyo/items/183e51a28a3a9d79182f •  デバッグ⽤にカスタムHTTPヘッダを⾒てRails3/4を指定し て振り分けるようにしておくと便利 •  (参考)nginxでカスタムHTTPヘッダを⾒てproxy_passを振り分け る •  http://qiita.com/minamijoyo/items/3705ad7c9df70e87a953
  18. 18. Rails3/4⽤のサーバ群 •  ELB/EC2/AutoScale/CloudWatch/CodeDeployなど必要な AWSリソース⼀式をTerraformでモジュール化 •  同じようなサーバ群を簡単に並べて作れるようにする •  デプロイ⾃動化している箇所の修正とかも必要なので忘れず •  ホスト名の⼀部にRailsバージョン⼊れるとログが区別しやすい •  サーバを作って壊せる環境なら雑なネーミングが可能 •  パフォーマンス情報の収集 •  ActiveSupport::Notificationsで process_action.action_controllerのイベントをフックしてアク ションごとの実⾏時間などを取得 •  Railsバージョンのタグを付けてDatadogに送りつけておく •  トラフィック流して⼤幅に劣化してそうだったら、がんば る。。。
  19. 19. 並⾏稼動の考慮ポイント •  Rails3.2/4.2を並⾏稼動させる上でのハマりポイントの 具体例 •  リクエスト/レスポンスの互換性 •  ルーティング •  CSRFトークン •  アセット •  flash •  データの互換性(データが保存される場所ごとに考える) •  Cookie •  MySQL •  memcached •  Elasticsearch •  データの互換性は、並⾏稼動しなくても⼀発リリース失 敗で旧戻しする場合は考慮が必要 •  キャッシュ系は最悪は再⽣成できるけどDBだけはヤバイ
  20. 20. リクエスト/レスポンスの互換性 •  ルーティング •  PUT/PATCH •  ActionDispatchでPUT/PATCH処理にモンキーパッチ •  並⾏稼動のためRails3/4でPUT/PATCHを同じ扱いにする •  CSRFトークン •  Rails4.2で⽣成ロジックが変更されている •  breach-mitigation-railsをRails3側に⼊れると同じになる
  21. 21. リクエスト/レスポンスの互換性 •  アセット •  マニフェストの形式が変わった •  依存ライブラリと結合したjsファイルが同じにならない •  asset_hostを分けた •  flash •  rails_4_session_flash_backport •  移⾏⽤gemがあるんで⼤丈夫と思ってた時代が私にもありました •  Rails3.2=>4.2で使うと微妙に実装が変わってる?(確認中) •  移⾏系のgemはバージョン離れると注意
  22. 22. データの互換性 •  Cookie •  cookies_serializerがmarshalからjson形式に •  並⾏稼動中はmarshalに揃える
  23. 23. データの互換性 •  MySQL •  明⽰的なシリアライズ •  プレーンなHashとかなら問題ない •  Modelとかparamsとかをシリアライズすると型が違って死ぬ •  暗黙のシリアライズ •  serialize宣⾔していないtextカラムにHashを突っ込むとYAML だったのがJSONになってる •  そんなこと良い⼦は普通しないよね?油断してるとある •  delayed_job •  Modelをシリアライズするので型が違って死ぬ •  キュー名で分ける=>LIKE検索の負荷が重くて死ぬ •  delayed_jobにカラムを追加する •  時刻の精度 •  MySQL5.6 + Rails4.2で時刻がマイクロ秒精度に •  秒精度に統⼀
  24. 24. データの互換性 •  Memcached •  単純な数値や⽂字列とかなら問題ない •  Modelとか突っ込むと(以下略 •  そんなこと良い⼦は普通(ry •  ダメな⼦はキーを分ける •  Elasticsearch •  JSONコーダの実装が変わってる •  インデックスすると時刻がミリ秒精度に •  秒精度に統⼀ •  ActiveSupport::JSON::Encoding.time_precision = 0 •  JSON.dump(Time.now)した結果の時刻フォーマットが違う •  Ojとか別にJSONコーダを使う
  25. 25. まとめ •  なぜ並⾏稼動させるのか •  ⼩さくリリースしたい •  開発フロー •  バージョンアップ対応の修正はmaterに⼊れていく •  並⾏稼動の仕組み •  複数のGemfileを共存させてBUNDLE_GEMFILEで制御 •  URLパスベースでリクエストを振り分ける •  Rails3.2/4.2を並⾏稼動のハマりポイント •  並⾏稼動させる上でリクエストとデータの互換性は⼤事 •  特にデータは並⾏稼動しなくても旧戻しのために⼤事 •  並⾏稼動させるのに考えることいろいろあって⼤変 •  でも仕組みができれば継続的にバージョンアップしていける •  もうすぐproduction投⼊するので忘れてそうなことあれば教え て下さい
  26. 26. We’re Hiring •  クラウドワークスでは個⼈の働き⽅を変えたいRailsエン ジニアを募集中です •  https://www.wantedly.com/projects/42054

×