無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Nginx でリバースプロクシを立てるときに気にすべき proxy_next_upstream 設定

f:id:muziyoshiz:20171026000347p:plain:w400

個人的に、Nginx で「これは危険だ」と思っている設定があって、Nginx でなにかあるたびにその設定をつい疑ってしまいます。その設定について他の人に話すたびに、いちいち資料を集めるのが面倒になってきたので、今回はその設定項目についての情報をまとめておきます。

まだ理解に自信がない部分があるので、新しい情報が入ってきたら、この記事を適宜修正します。

リバースプロクシ設定の基本

Nginx をリバースプロクシとして使う時には、ngx_http_upstream_module でサーバのグループを定義します。そして、サーバ名やロケーション(パス)に対して、送信先のグループを指定します。

以下はマニュアルにある例です。その Nginx サーバへのすべてのアクセスを、backend グループに指定されたいずれかのサーバに送信します。

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

この送信に関わる設定は、proxy_pass を含む ngx_http_proxy_module の方にあります。このモジュールの設定のなかで、(僕が個人的に)よくつまづくのが proxy_next_upstream から始まる設定です。

proxy_next_upstream から始まる設定

これらは、upstream(リクエストの送信先)からエラーが返されたり、リクエストがタイムアウトした場合の動作に関する設定です。

  • proxy_next_upstream
    • 失敗したリクエストを他のサーバに再送する条件(複数指定可)
  • proxy_next_upstream_timeout
    • Nginx 側でリクエストがタイムアウトしたと判断するまでの時間
    • proxy_next_upstream で timeout が指定された場合のみ、この設定が使われる
    • 時間の単位は Configuration file measurement units を参照
  • proxy_next_upstream_tries
    • proxy_next_upstream の条件に合致したリクエストを、最大で何台のサーバに送信するか
    • マニュアルには明示されていないが、この送信回数は最初の1台を含む
      • 1が設定されたら、最初のサーバ1台にしかリクエストを送信しない
      • 3が設定されたら、最初のサーバ1台への送信と、それ以外の2台への再送を行う

これらの設定が明示的に指定されなかった場合のデフォルト値と、その意味は以下の通りです。

  • Default: proxy_next_upstream error timeout;
    • 何らかのエラーが発生した場合、または Nginx 側でリクエストがタイムアウトした場合に、リクエストを再送する
    • ここで言う「エラー」とは、(転送先)サーバへの接続時、リクエストの転送時、またはレスポンスヘッダの読み込み時に発生するエラーのこと
    • 4xx 応答、5xx 応答は、ここで言う「エラー」には含まれない
  • Default: proxy_next_upstream_timeout 0;
    • Nginx 側でのタイムアウトは起こらない(0 は無制限を表す)
  • Default: proxy_next_upstream_tries 0;
    • upstream ディレクトリで定義されたすべてのサーバに対して順番に、エラーが発生したリクエストを再送する(0 は無制限を表す)

不適切な設定が問題になるケース

proxy_next_upstream_tries を指定せずに使っていると、バックエンドのサーバへの接続で何らかのエラーが発生したら、最悪の場合、そのリクエストはすべてのサーバに対して送信 されます。

例えば、以下のような状況になると、無駄なリクエストが Nginx で大量に増幅されて、システム全体の負荷が急増します。

proxy_next_upstream 設定を何も指定していない状態で、
→ アプリケーションサーバが何かのバグで不正なレスポンスを返すようになる
→ そのバグを踏むリクエストが来る
→ そのリクエストがアプリケーションサーバの台数だけ複製される(サーバ10台なら10倍になる)
→ システム全体の負荷が急増

また、proxy_next_upstream_timeout だけ設定していると、こういうこともあり得ます。

proxy_next_upstream_timeout が2秒に設定されていて、proxy_next_upstream_tries は未指定の状態で、
→ 処理時間が2秒を超える重いリクエストが来る
→ その重いリクエストがアプリケーションサーバの台数だけ複製される(サーバ10台なら10倍になる)
→ システム全体の負荷が上がって、普段は2秒未満のリクエストも2秒以上かかるようになる
→ それらのリクエストも10倍に複製される
→ システム全体の負荷が急増

あるべき設定

個人的に考える、あるべき設定は以下の通りです。

proxy_next_upstream_tries は必ず0以外に設定する

この値がデフォルト値の0(無制限)でさえなければ、上記のような問題は起こらないので、まずこれを設定します。

1にすれば再送は起こりませんが、アプリケーションサーバを再起動するような場合にいちいちエラーが出てしまいます。再起動の場合のみを考えるなら、この値が大きすぎても意味はありません。そのため、proxy_next_upstream_tries は2〜3でいいと思います。

proxy_next_upstream_timeout はアプリケーションサーバ側の応答時間より長くする

proxy_next_upstream_timeout がアプリケーションサーバ側の応答時間よりも短いと、せっかくアプリケーションサーバがレスポンスを返しても Nginx で破棄されてしまいます。これではサーバの計算資源の無駄遣いです。

そのため、アプリケーションサーバの応答時間を事前に見積もって、それより長い時間を proxy_next_upstream_timeout に指定しましょう。これは、タイムアウト設計をきちんとしましょう、そして時間がかかる処理(データベース接続)があるならアプリケーション内にきちんとタイムアウト処理を入れましょう、という話ですね。

応答時間の見積もりが難しいなら、proxy_next_upstream_timeout はデフォルト(タイムアウトなし)のままでもいいと思います。

サービスによっては再送処理をオフにする

API 提供時など、クライアント側で再送処理をしてくれるなら、proxy_next_upstream off; を設定し、再送処理をオフにするという手もあります。

関連情報

艦これアーケードのプレイデータ管理ツール Admiral Stats 1年分のユーザデータ解析

f:id:muziyoshiz:20170128164643p:plain

今月の9月3日に、艦これアーケードのプレイデータ管理ツール Admiral Stats の公開から1周年を迎えました。少しずつユーザが増え、開発に協力してくれる方も現れて、おかげで今日まで楽しく開発を続けてこられました。本当にありがたいことです。

1周年というのはよい節目なので、今回は Admiral Stats のユーザデータを解析して、利用傾向を調べてみました。この手のプレイデータ管理ツールを公開すると、どれくらい人が集まるのか(あるいは集まらないのか)の参考としてどうぞ。

解析対象は MySQL 上のデータです。解析方法の詳細は、このブログ記事の最後に貼っておきます。

アクティブユーザ数

Admiral Stats は、Twitter アカウントがあれば誰でもログインできるようになっています。しかし、この時点では、プレイデータは何も表示されません。

ログイン後に、SEGA 公式サイトからエクスポートしたプレイデータを Admiral Stats にインポートすると、プレイデータを時系列に表示できるようになります。

そのため、ログインしただけではアクティブユーザとは言えず、インポート機能を使っていればアクティブユーザと言えます。ただ、1回インポートして辞めてしまう人も割といるので、いくつかの基準で集計してみました。

グラフ上の項目名 集計基準 9月3日時点
ログイン その日の23:59までに1回以上ログインしたユーザ数 417
インポート その日の23:59までに1回以上インポートしたユーザ数 248
インポート(過去30日) その日から過去30日以内に1回以上インポートしたユーザ数 113
インポート(過去60日) その日から過去60日以内に1回以上インポートしたユーザ数 134
f:id:muziyoshiz:20170928002847p:plain

このデータを見ると、だいたい、以下のような傾向がありそうです。

  • ログインしても、インポートする前に離脱してしまうユーザが多い。しかし Admiral Stats 自体の改善が進んだためか、離脱率は徐々に下がっている
  • 2016年9月の まとめサイト取り上げ時 は、離脱率がかなり高かった
    • その後の1週間で増加したログインユーザのうち、78%(=61/78)が離脱してしまった
    • 当時は Ruby 版エクスポータしか無く、使うのが難しかったためと思われる
    • 流入の効果は1週間程度しか続かなかった
  • 2017年7月に 艦らぼ で紹介してもらった際は、離脱率が低かった
    • 公開後1週間で増加したログインユーザのうち、32%(=9/28)しか離脱していない(!)
    • これは、艦らぼに 丁寧な解説記事 を書いてもらえた影響もかなり大きいと思ってます
  • 艦これアーケードの期間限定海域(イベント)の期間中はアクティブユーザが増えて、終わると一時的に落ち込む

余談ですが、Admiral Stats は、おーぷん2ちゃんねると Twitter(@admiral_stats) で宣伝していました。おーぷんで宣伝したのはまとめサイトでの取り上げを期待してのことだったのですが、

  • 機能追加しても、まとめサイトでは最初の1回しか取り上げられなかった
  • おーぷんで宣伝しても、ある時期から PV が増えるだけでインポートユーザ数は増えなくなった

ことから、いまは Twitter でしか宣伝していません。

MAU (Monthly Active User) と継続率

インポート機能を使っているユーザを「アクティブユーザ」と定義して、これをもう少し詳しく調べてみました。

グラフ上の項目名 集計基準 8月末日時点
MAU(全体) その月に1回以上インポートしたユーザ数 118
MAU(その月の新ユーザ) MAU のうち、その月に初回ログインしたユーザ数 29
継続率 前月に1回以上インポートしたユーザのうち、その月もインポートしたユーザの割合 83.3 %
f:id:muziyoshiz:20170928002920p:plain

この指標で見ても、Admiral Stats の離脱率は徐々に下がっている(=継続率が徐々に上がっている)と言えそうです。

また、意外なことに、毎月増えるユーザ数(その月の新ユーザ)はイベントにそれほど左右されず、一定の値を保っていたようです。しかし、それが今年の7月で2倍近く増加しています。とうとう普及が一山越えたんでしょうか?

インポート回数

ユーザ1人あたり、1ヶ月に何回くらいインポートしているか調べてみました。インポート回数が多いほど、Admiral Stats を日常的に使っていると言えます。

Admiral Stats は複数のプレイデータ(提督情報、艦娘情報、など)をサポートしていますが、「提督情報」のインポート回数のみを数えました。ほとんどのユーザがこのデータをインポートしており、これを Admiral Stats の利用回数と同一視して差し支えないと判断しました。

以下がその結果です。僕自身のインポート回数(動作確認含む)と、それぞれの月で最もインポート回数が多かったユーザは、外れ値として計測対象外としました。

平均 標準偏差 合計
2016-09 5.84 4.51 111
2016-10 5.33 4.80 192
2016-11 5.52 5.35 276
2016-12 5.71 6.57 217
2017-01 4.65 4.36 214
2017-02 5.02 5.10 246
2017-03 7.45 7.83 417
2017-04 8.02 7.73 497
2017-05 9.60 8.83 806
2017-06 8.36 9.64 619
2017-07 9.50 11.40 893
2017-08 9.77 11.05 1133
全期間 7.76 8.79 5621
f:id:muziyoshiz:20170928002958p:plain:w640
f:id:muziyoshiz:20170928003012p:plain:w640

インポート回数も、イベント期間中は増えて、終わると一時的に落ち込んでいます。それとは別に、インポート回数の平均と標準偏差が増えていますが、これは Admiral Stats のヘビーユーザが徐々に増えている影響のようです。

エクスポータの種類別のユーザ数

SEGA 公式サイトからプレイデータをエクスポートするために、以下のエクスポータを提供しています。エクスポータは Admiral Stats の「使い方」ページ からダウンロードできます。

  • Ruby 版
    • 僕が最初に公開したエクスポータ
  • PowerShell 版
    • sophiarcp さんが Ruby 版を移植してくれたもの(2016-09-18 提供開始)
  • ブックマークレット版
    • sophiarcp さんが開発してくれたもの(2016-10-29 提供開始)
  • Python 版
    • mimikun さんが開発してくれたもの(2017-04-18 提供開始)

今年の3月12日に、エクスポートしたプレイデータを Admiral Stats に自動アップロードする機能を追加しました。この自動アップロード時の User Agent から、実際に使われているエクスポータの種類が(やっと)わかるようになりました。

以下がその結果です。全ユーザが自動アップロード機能を使っているわけではないので、MAU よりは少なくなっています。また、1人が複数のエクスポータを使っている場合もあります(例:家では Ruby、外ではブックマークレット)。

Ruby 版 PowerShell 版 ブックマークレット版 Python 版 その他
2017-03 5 10 14 0 0
2017-04 5 8 26 0 0
2017-05 7 9 45 0 2
2017-06 5 8 43 0 1
2017-07 6 7 69 1 1
2017-08 3 6 86 1 1
f:id:muziyoshiz:20170928003058p:plain:w640

ブックマークレット版のユーザ数が圧倒的ですね。正直、この集計をするまで、Ruby 版を使ってる人はもっと多いと思ってました……。PowerShell 版とブックマークレット版を作ってくれた sophiarcp さんにはホントに頭が上がらないです。

集計方法

今回の集計は、以下の手順で行いました。普通ですね。

  1. MySQL のバックアップデータをローカル開発環境に持ってくる
  2. rails console で CSV 出力する
  3. CSV を Excel に取り込んで、グラフを書いたり、ピボットテーブルを作る

rails console で実行したコマンドは、2周年のときの集計のために(自分が忘れないように)Gist に貼っておきました(Gist: Admiral Stats 1周年のユーザデータ解析)。Admiral Stats のソースコードは GitHub (admiral_stats) にあるので、興味のある方はそちらもどうぞ。Star を押すと開発者が喜ぶのでオススメです。

あわせて読みたい

muziyoshiz.hatenablog.com

Admiral Stats がどういうものかわかるプレゼン資料です。

muziyoshiz.hatenablog.com

今回は Admiral Stats の利用傾向を見ましたが、こちらはプレイデータの中身を掘り下げた記事です。

Wiki のページ、[[タイトル]] から作るか、いきなり作るか?

f:id:muziyoshiz:20170827200124p:plain:w480

最近、職場で Wiki について話していて、Wiki のページの作り方は2通りあるよね、って話になりました。

  1. ページ本文のなかに [[タイトル]] と書いて保存し、あとからそのリンクを踏んで「タイトル」ページの作成画面を表示する
  2. いきなりページの新規作成画面を開いて、タイトルを記入する

僕はだいたい1番で作る方なんですが、毎回2番で作る、という人もいました。

そんなことがあって「僕の Wiki の使い方は普通じゃなかったのかな?」と思って調べてみたら、最近の Wiki の一部(Qiita:Team や esa.io)は 1番のようなページの作り方ができない んですね。

そうなのか!これは面白い!と思って、Wiki の歴史を遡って色々調べてみました。今回はそんなネタです。

Wiki ソフトウェアそれぞれのリンク記法

まず、Wiki の機能を持つソフトウェア(以下、Wiki ソフトウェア)について、その Wiki 内のページにリンクするための文法を調べてみました。僕が有名だと思うソフトウェアと、最近登場した国産のソフトを主に取り上げています。

登場時期 ソフトウェア名 記法 WikiName [[pagetitle]] その他
1994 WikiWikiWeb Yes
1999 UseModWiki Yes Yes
2000 YukiWiki Yes Yes
2001 PukiWiki Yes Yes
2002 MediaWiki Yes
2002 Tiki ((pagetitle))
2003 Hiki Yes Yes
2004 Trac Yes Yes [wiki:pagetitle]
2004 Confluence [pagetitle]
2006 Redmine Yes
2006 Backlog Markdown Yes (※)
2008 GitHub Markdown Yes (※)
2011 GitLab Markdown [text](pagetitle)
2013 Qiita:Team Markdown [text](url)(作成前にリンク不可)
2014 Crowi Markdown <pagetitle>
2015 esa Markdown [text](url)(作成前にリンク不可)
2015 DocBase Markdown [text](url)(作成前にリンク不可)
2016 Scrapbox [pagetitle]
2017 Kibela Markdown [text](url)(作成前にリンク不可)

(※) 相対パスで書けばリンク可能だが、[[pagetitle]] 記法と比べたメリットが無いので省略

以下、この表の読み方と補足事項です。

  • 「登場時期」列
    • ベータ版と正式版があるときは、正式版のリリース日を登場時期とした
    • 正確なリリース日がわからないものは、確認できた最も古いリリースの日付とした
  • 「記法」列
    • 最近は Markdown 記法(またはその独自拡張)が多いため、その場合は “Markdown” と記載した
    • Wiki ソフトウェアが複数の記法をサポートしている場合は、Markdown 記法での仕様のみ記載した
  • WikiName 列が “Yes”
    • ページ本文に CamelCase で単語を書くと、それが自動的に Wiki ページへのリンクになるもの
  • [[pagetitle]] 列が “Yes”
    • ページ本文に [[タイトル]] 形式で単語を書くと、それが自動的に Wiki ページへのリンクになるもの
  • 「その他」列
    • WikiName と [[タイトル]] 以外の文法を採用しているもの
    • ページ作成前にリンクを張れる Wiki ソフトウェアのほうが多いため、作成前にリンク不可のものはそのように明記した

[[タイトル]] 記法の歴史は古い

ウォード・カニンガムが最初に作成した Wiki(WikiWikiWeb)は、CamelCase で単語を書くと、自動的にその名前を持つページへのリンクになります。また、その名前を持つページがまだ作られていない場合は、CamelCase? のように末尾に ? が付いて、この ? がページの新規作成画面へのリンクになります。これは、他の読者に、新しいページの作成を促すための工夫だったようです。

WikiWikiWeb にはこの CamelCase の記法(WikiName とも呼ばれる)しか無かったのですが、これでは不便だということで、後発の UseModWiki で、初めて [[タイトル]] 記法が発明されました。

UseModWiki は、MediaWiki が開発される前に、Wikipedia で使われていた Wiki ソフトウェアです。History of wikis によると、この記法が発明されたのは、Wikipedia のユーザビリティを改善するためだった、とのことです。

This CamelCase convention was used by most wiki software for the first few years of wikis' existence. In 2001, the software UseModWiki, which at the time was in use on Wikipedia, switched to allow internal links to be done using standard spelling and double square bracket instead, in order to improve Wikipedia’s usability. This square bracket syntax has since become more of a default convention for internal links within wiki software in general.

WikiName のルールは日本語では不便すぎるので、YukiWiki や PukiWiki といった国産のソフトウェアが早々に [[タイトル]] 記法を採用したのは納得できます。

[[タイトル]] からページを作れない Wiki が増えている

Markdown が普及して以降、Markdown やその拡張の GitHub Flavored Markdown(GFM)を採用した Wiki が増えています。昔から存在する Wiki があとから Markdown を採用したパターンもあります。上記の表では Backlog がそのパターンです。

Markdown は Wiki を想定せずに作られたので、Wiki ページにリンクするための文法がありません。そのため、Wiki ソフトウェアごとに、Wiki で一般的な [[タイトル]] 記法を導入したり、Markdown のリンク記法を Wiki のために拡張したりしています。

その一方で、Qiita:Team や esa などの新しいソフトウェアの一部は、「ページの作成前にリンクを張る機能は不要」と割り切っているのか、そのような文法を用意していません。

どういうことか? 例えば esa で新しいページを作ると、URL は https://muziyoshiz.esa.io/posts/5 のようになります。従来の Wiki は URL にタイトルの一部が入っていましたが、この種の Wiki では連番が入るので、事前に予想はできません。そのため、リンクを張りたければ、ページを作ったあとで、[タイトル](/posts/数字) のように、Markdown の文法でリンクを書く必要があります。これはめんどい。

ただ、esa の場合は、この面倒さを減らすためにリンクのサジェスト機能を提供しています。# からテキストを書き始めると、同じチーム内の投稿へのリンクがサジェストされます。

f:id:muziyoshiz:20170827214108p:plain

そして投稿を選ぶと、以下のように Markdown 形式のリンクに置き換えられます。

f:id:muziyoshiz:20170827214121p:plain

esa の場合、テンプレートを元に階層化されたページを作れるので、ページ名はだいたい「日報/2017/08/28/masahiro_yoshizawa」のようになります。これを手入力するのは現実的じゃないですよね……。それも、[[タイトル]] に相当する記法がない理由の一つかもしれません。

まとめ

[[タイトル]] という記法は昔からあって、さらにその大本には WikiWikiWeb が持っていた「ページの新規作成を促す機能」があった、という話をしました。だから、[[タイトル]] のリンクを踏んで新しいページを作る、というのは昔からの Wiki の使い方、と言えるでしょう。

しかし、これはインターネット上で誰もが参加できるような Wiki では必要でも、企業内などに閉じた Wiki では不要な機能なのかもしれません。企業内ならテンプレートに従ってページを作る方が一般的で、それなら

  • テンプレートに従って、いきなりページを作る
  • 必要なリンクはあとから書き足す

という使い方を助けるほうがいいのでしょうか? こういう使い方が今後一般的になっていくのか、気にしてみたいと思います。

2017年9月3日追記:アンケートへのご協力ありがとうございました

この記事の公開後の1週間、9月3日までアンケートを公開していました。ご協力ありがとうございました。

結果は52:48で、まさかここまで接戦になるとは予想してませんでした……。25名の方が投票してくれたので、13:12、1票差ですね。もう少し投票数を集められるなら、次はその人が普段使ってる Wiki ソフトウェアとセットでアンケートを取ってみたいです。

あわせて読みたい

この調査中に見つけた History of wikis はかなり読み応えがあって面白いので、Wiki に興味があったら是非一読をおすすめします。個人的に面白かったのはこのあたりです。

  • フローからスタックへ、という話題は、1996年に WikiWikiWeb へ ThreadMode が実装されたころにすでに存在していた
  • WikiWikiWeb にカテゴリとトピック(今で言うタグのようなもの)の機能が提案されたことがあったが、カテゴリのみが採用された
  • 「Wiki の機能をメール、IM、SNS と結合しようとした試み」として Google Wave の話が載っている

https://en.wikipedia.org/wiki/History_of_wikisen.wikipedia.org

このブログで過去に書いた Wiki 関連記事もあわせてどうぞ。

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

趣味で作っているサービスを Rails 5.0 から Rails 5.1 へアップグレードする

f:id:muziyoshiz:20170724152414p:plain

最近、趣味で作っているサービス(Admiral Stats)を Rails 5.0.0.1 から 5.1.2 にアップグレードしました。サービスが小さいので、それほど苦戦するポイントは無かったのですが、調べたこと、やったことをメモしておきます。

調べたこと

とりあえず、関連しそうな情報に一通り目を通しました。

アップグレード方針

上記のページを見る限り、今回は移行対象のアプリが小さいこともあり、特に問題もなくアップグレードできそうでした。

まず、「application.secretsですべてのキーをシンボルとして読み込むようになった」はコード中の参照箇所を確認して、問題なし。JWT のエンコード、デコード時に Rails.application.secrets.secret_key_base と参照していましたが、これは Rails 5.1 でも動作しました。

秘密情報の暗号化は、元々 MySQL のパスワードなどは Apache から環境変数(SetEnv)で渡していたので、この方針を踏襲することにして変更せず。config/environments/production.rb で config.read_encrypted_secrets = false と書いておいて、今まで通りに secrets.yml を使います。

Yarn のサポート、Webpack のサポート、デフォルトでのjQuery依存の廃止、については、引き続き jQuery を使うことにして今回は気にしないことに。

あと、主キーのデフォルト型が BIGINT に変更に変更される件については、テーブル作成時期によって主キーの型が変わるのは混乱しそうなので、INT に統一することにしました。アップグレード時に必要な作業は特にないですが、テーブル作成時に id: integer を付ければ OK(以下はその例)。ただ、これはうっかり忘れることが多そうなので、generate model 時に必ず自動で付くようにしたい……。

class CreateBlueprintStatuses < ActiveRecord::Migration[5.1]
  def change
    create_table :blueprint_statuses, id: :integer do |t|

やったこと

Rails 5.0 系の最新版へのアップグレード、および関連 gem のアップグレード

まずは Gemfileのバージョンを更新。

- gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
+ gem 'rails', '~> 5.0.0', '>= 5.0.4'

そして bundle update を実行。参考にしたページには bundle update rails を実行して rails 関係だけをアップグレードし、関連 gem は後回しにすべき、というアドバイスがあったのですが、すっかり忘れてました……。今回はそのまま続行。

最後に、動作確認のためにテストがある部分は rails test で確認し、ない部分は手作業で軽くポチポチ確認。

Rails 関係では特に問題ありませんでしたが、rack-cors が 0.4.1 から 1.0.0 に上がった影響で動作が少し変わっていて rails test が失敗。その部分のテストコードを修正しました。

あとは、Capistrano が 3.6.1 から 3.8.2 に変わった影響で、動かない部分と、Deprecation Notice が出る部分があったのでそこを修正。

  • Capfile の修正
- lock '3.6.1'
+ lock '3.8.2'
  • Capfile で Git SCM plugin のロードを明示
# Include default deployment tasks
require "capistrano/deploy"

# Future versions of Capistrano will not load the Git SCM plugin by default.
+ require "capistrano/scm/git"
+ install_plugin Capistrano::SCM::Git

Rails 5.1.2 へのアップグレード

先ほどと同じく、まずは Gemfile のバージョンを更新。

- gem 'rails', '~> 5.0.0', '>= 5.0.4'
+ gem 'rails', '~> 5.1.0', '>= 5.1.2'

次は bundle update を実行。関連 gem はアップグレード済みなので、今回は rails 関連の gem のみがアップグレードされました。

そして rails app:update を実行。bin 以下と config 以下のファイルが、新規作成されたり上書きされたりします。git で差分を見て、既存の設定を上書きしている部分以外は、基本的に採用しました。

  • bin/setup
    • bin/yarn の読み込みがコメントアウトされた状態で追加された。参考のために残した
  # Install JavaScript dependencies if using Yarn
  # system('bin/yarn')
  • bin/yarn (新規作成)
    • 今回は使わないが、呼び出し自体は上記の通りコメントアウトされているので残した
#!/usr/bin/env ruby
VENDOR_PATH = File.expand_path('..', __dir__)
Dir.chdir(VENDOR_PATH) do
  begin
    exec "yarnpkg #{ARGV.join(" ")}"
  rescue Errno::ENOENT
    $stderr.puts "Yarn executable was not detected in the system."
    $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
    exit 1
  end
end
  • config/application.rb
    • 以下の行が追加された。確認のうえで残した
+    config.load_defaults 5.1
  • config/cable.yml
    • Redis PubSub で使う prefix が追加された。設定しても、この自作サービス(Admiral Stats)では使わないが、自動生成されたまま残した
+  channel_prefix: admiral_stats_production
  • config/puma.rb
    • ビルトインサーバの設定。コメントが変わっただけ
  • config/routes.rb
    • 単純に空にされるだけなので、上書きされたものをもとに戻した
  • config/secrets.yml
    • 環境変数から読み込む運用にしているので、そのままにした
  • config/environments/development.rb
    • Cache-Control の表記が分かりやすい表記に変わった(max-age の長さは同じ)ため、そのまま残した
-      'Cache-Control' => 'public, max-age=172800'
+      'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
  • config/environments/production.rb
    • config.read_encrypted_secrets = true という行が追加されるが、これを false に変更した
  • config/initializers/assets.rb
    • 以下の行が追加された。Yarn は使っていないが、そのままにした
Rails.application.config.assets.paths << Rails.root.join('node_modules')
  • config/initializers/cors.rb
    • 基本的な設定が作成されたが、このファイルは元々作成済みだったため、元に戻した
  • config/initializers/new_framework_defaults_5_1.rb
    • 以下の内容で作成された。そのままにした。
# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 5.1 upgrade.
#
# Once upgraded flip defaults one by one to migrate to the new default.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.

# Make `form_with` generate non-remote forms.
Rails.application.config.action_view.form_with_generates_remote_forms = false

# Unknown asset fallback will return the path passed in when the given
# asset is not present in the asset pipeline.
# Rails.application.config.assets.unknown_asset_fallback = false

最後に、動作確認のためにテストがある部分は rails test で確認し、ない部分は手作業で軽くポチポチ確認して、リリースしました。

まとめ

5.0 から 5.1 へのアップグレードは、それほど詰まるところはありませんでした。アップグレードが溜まってると辛くなるので、早いうちにやっておいてよかったです。

ただ、当たり前の話ですが、アップグレード前にはちゃんとテストを整備しておかないとダメですね。今回のアップグレード対象は趣味のサービスで、テストより機能追加を優先してしまっていたので、今回のアップグレードはおっかなびっくりでした。なにか僕の気付いていない問題が見つかったら、報告頂けたら緊急で対応します……。