メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

RubyWorld Conference2018にRubyスポンサーとして参加しました

こんにちは。メドピアのRailsエンジニアの小林です。遅くなってしまいましたが、11/1, 11/2に島根県松江市で行われたRubyWorld Conferenceに行ってきたので、レポートをお届けいたします。

2018.rubyworld-conf.org

多くの興味深いセッションが行われましたが、内容はRubyWorld Conferenceの公式YouTubeチャンネル*1から動画で見れるため、本記事では基調講演の大枠のまとめと、ブース出展の様子をお伝え致します。

今回、メドピアは初のブース出展(Rubyスポンサー)でした!そもそもRuby関連のイベントへのブース出展することが初なので、準備に手間取ることを予想して前日入りしました。

f:id:marikokobayashi:20181115235854j:plainf:id:marikokobayashi:20181116000544j:plain
f:id:marikokobayashi:20181115235910j:plainf:id:marikokobayashi:20181116000516j:plainf:id:marikokobayashi:20181116002319j:plain

1日目は開会挨拶と来賓挨拶からスタート。まずは島根県知事代理・松江市市長代理がいらっしゃってのご挨拶。島根県・松江市がRubyを使ったIT企業の支援に力を入れていることが伝わってきます。

Matzの基調講演 The power of the community

  • Rubyのこれまで歩んできた道
  • ピッケル本 (正式名: Programming Ruby: The Pragmatic Programmer's Guide、英語で初めて出版されたRuby本)の出版社のAddison-Wesleyから本を書きたいと連絡が来たときのお話
  • 最初のRubyConfのときのお話
  • Rubyのユーザー数の増加
  • Rubyコミュニティについて
  • Rubyのハイプサイクル
  • 「あきらめない」こと
  • 一歩踏み出すこと
  • Matzの引退とRuby4について(!)

などについて語っていただきました。

特にRubyコミュニティのお話が印象的でした。 もしRubyコミュニティが存在しなかったら、Rubyは今のような形では存在していなかっただろうとMatzはおっしゃいます。

Matzが発案したものではない機能が今ではRubyに欠かせないものとなっていますし、周囲の人がRubyのカンファレンスや勉強会の開催、Rubyの本の執筆等の活動をしたことでRubyは発展してきました。

RubyコミュニティはNiceなコミュニティです。

ですがRubyコミュニティの良いフィードバックをより長く強く維持していくために、Matzは「もう一歩だけ先に行ってほしい」と言っていました。

コンフォートゾーン、居心地が良い環境にいつづけるだけでは進歩がなくなってしまう。

新しいカンファレンスに出てみる、友だちを作ってみる、新しいことを試してみる、他の人を褒める、目を見て握手してみる、アイディアを出し合ってみる。そういったことをしてみて下さい、とのことでした。

「宿題を出します。カンファレンス全体で感じたことをなにかでシェアして下さい。ブログでもTwitterでもFacebookでもMediumでもQiitaでもいいのでシェアして下さい。皆さんにも一歩踏み出していただけたらいいなと思っています。それが未来を良くすると思います」とのことでした。

f:id:marikokobayashi:20181116000215j:plain

メドピアのスポンサーセッション(1日目のお昼)

メドピアのスポンサーセッションは1日目のお昼!CTOの福村が登壇しました。

f:id:marikokobayashi:20181116000247j:plainf:id:marikokobayashi:20181116000328j:plain

発表資料はこちらです。

メドピアの事業紹介、メドピアの働きやすい制度、それからエンジニア発信のしくみである「プルリク振り返り会」と、そこで過去に上がったTipsについて話してくれました。

今メドピアでは数多くのプロジェクトが走っており、PRが上がってきたら同プロジェクトのメンバーがPRをレビューするという体制になっています。

しかしそれだとプロジェクトを横断して全エンジニアが知っておいたほうが良い知識が共有されないので、そのようなPRを振り返って話すのが「プルリク振り返り会」です。この取り組みは他社のエンジニアさんとお話すると「良いね」と言ってもらえることが多いです。

気になるという方は、是非取り入れてみてください。また、取り入れた際は感想を共有してもらえると嬉しいです!

レセプション(懇親会)

1日目の最後にはレセプションがありました。夕食や美味しいお酒とともに、和気あいあいと、島根の学生や各地からの参加者と交流していた弊社メンバーたちでした。 しかし突然の出来事が…!

レセプションの中でTシャツのプレゼント企画を行っていたのですが、弊社参加メンバーの名前が突然壇上から呼ばれ、Tシャツが当たってしまいました!しかもMatzがそれにサインしてくださいました!

喜びと驚きで興奮状態の我々、写真を撮ったりしてはしゃいでいたうちに、近くにいらっしゃったRuby/Railsコミッターの松田さんにお声がけしたところ、松田さんもサインをくださり、そして松田さんがさらに他のRubyコミッターを呼び寄せ、その場にいらっしゃったRubyコミッターによるサインリレーが始まってしまい…

f:id:marikokobayashi:20181116001032j:plainf:id:marikokobayashi:20181116001056j:plain
(弊社メンバー大はしゃぎだったため失礼をしていたら大変申し訳ありません)

…最終的にこのようなTシャツになっていました。Rubyistからしたら完全にお宝ですね。

f:id:marikokobayashi:20181116001418j:plain

今回参加したメドピアメンバーにとって、思い出に残る1日となりました。

Chad Fowler氏基調講演 Don't Stop Moving

2日目のスタートは、RubyConfやRailsConfの共催者、The Passionate Programmer: Creating a Remarkable Career in Software Development (邦題: 情熱プログラマー ソフトウェア開発者の幸せな生き方 )の著者として知られるChad Fowler氏による基調講演でした!

私は彼の著書「情熱プログラマー」は読んだことがあり、感銘を受けた人間の一人ですが、講演を聴くのは初めてなのでわくわくしながら聞きました。

今回は、モチベーションの保ち方や、プログラマーとして生き抜くための戦略について教えてくださいました。

深さと広さ

テクノロジーの仕事をするときは、色々なテクノロジーを広く少しづつ学ぶか、一つのことを深く極めるか、どちらかの戦略を取ると思います。まず、どちらのキャリアを選ぶか決めましょう、という話でした。Chad氏は色々なテクノロジーを広く学ぶ方を選びました。

その道を進んだChad氏は、いろいろな人が新しいテクノロジーを追いかけていることを目の当たりにしますが、「こんな新しいものがいっぱいあるのだ」と驚いてしまったときに、自分のキャリアの中での初心を思い返そうと考えたそうです。

テクノロジーに興味を持ったきっかけとなったビデオゲームである World War Craft のことを思い出してみたら、キャラクター育成の際に最初に「魔術師」「戦士」などを選び、スキルツリーからスキルカテゴリを選んで、どのように育成するかを選んでいました。

それと同じことで、テクノロジーの仕事をするときも、ひとつひとつの細かいことを気にせず、スキルカテゴリを見つけ出すことが大事だとのことです。

1996年頃にメンターの先輩から教わったことがあったそうです。当時は、ディレクトリサービス/UNIX/プログラミング言語という3つのカテゴリを軸に理解すれば、良いキャリアのスタートとなると言われたとのことでした(それぞれのカテゴリについて理解すればその中で具体的に何を選んで学ぶかは重要ではないとのこと)。

2018年の今、同じことを考えた場合、Chad氏が見出した3つのカテゴリは

  • オペレーティングシステム
  • データベース
  • プログラミング言語

とのことです。 さらに、それぞれのカテゴリの中でさらに、3つのカテゴリを見つけると良いとのことです。例えば言語であれば

  • オブジェクト指向言語(Ruby、Pythonなど)
  • Haskell/OCamlのような関数型言語
  • LISP

を挙げていました。これらは、「必要だから学べ」ということではなく、自分の知っている部分と別のカテゴリにおいては考え方の違いを知るのに頭を使うので、実際にはRubyだけで食べていくことができても、 違うカテゴリのものを学ぶことはテクノロジーを広く知るためには役に立つということをおっしゃっていました。

Technology is a fashion

  • ファッションは進化し続けています。全てに付いていくは大変です。テクノロジーもそれと同じ。全部追いかけ回すと全部やらなければいけない、全部最新の流行を理解しないといけないと思うとやる気が無くなってしまう。それよりも自分が安定して取り組めるものにきっちりついていったほうが良いというお話がありました。

自発的な動機づけと外的な動機づけ

  • 外からアメやムチで人を動かそうとしても、結果は出せず、自発的な動機があって初めて結果が出せます。ダニエル・ピンク氏の著書 Drive (邦題: モチベーション3.0 )でも、モチベーションを持つには「自発的であること」「熟練すること」「目標があること」が大事と書かれています。

  • Chad氏も、最初にDave Thomas氏に「情熱プログラマー」を書いてくれと言われた時は、正直大変だなと思ったらしいです。本を書いて有名になりたい気持ちもあったが、それは外的動機づけなのであまり気持ちが乗らなかったとのこと。 しかし、この本を書くことでだれか1人の人間でも幸福にする事ができるかもしれないという自発的なモチベーションを持ったことで、書き上げることができたというお話でした。

「情熱プログラマー」を読んだことでソフトウェア開発者としての生き方を見つめ直したという人も多いのではないでしょうか。その著者自身の執筆のモチベーションのお話を伺ったことで、私ももっと自発的な動機づけをもって物事に取り組もうという気持ちが高まりました。

ブース

ブースでは、弊社のメインサービスである医師向けコミュニティサイト「メドピア」の紹介の他に、子会社Mediplatの遠隔診療サービス「first call」と、同じく子会社FitsPlusの管理栄養士による食事アドバイスアプリ「DietPlus」のご紹介をさせていただいておりました。

f:id:marikokobayashi:20181116002650j:plain

また、PeerWaterというお水とメドベアのステッカー(メドピアの熊キャラクターなので「メドベア」です)を無料で配布していたのですが、ステッカーの評判が良かったですね。ちなみにMatzにも1日目にブースにいらっしゃった時に渡したのですが、気に入っていただけたのか、2日目にもブースに来てくださって数枚持って帰ってくださっていたぐらいです。(メドベアグッズ出したらもしかして需要あるんでしょうか…)

観光

あまり観光の時間は多く取れなかったのですが、前日入りした日には少し松江の空気を味わうことができました。

f:id:marikokobayashi:20181116000111j:plainf:id:marikokobayashi:20181116000015j:plainf:id:marikokobayashi:20181116000032j:plainf:id:marikokobayashi:20181116000052j:plain

初日夕食には美味しいご飯をいただきました。お邪魔したのは「おでん庄助」さん。地元の人にも知られたおでん屋さんです。我々が向かったときはもうすでにメニューの売り切れが多くて「メニューは選べないけど盛り合わせなら出せますよ」とのことでした。

f:id:marikokobayashi:20181116132531j:plainf:id:marikokobayashi:20181116132617j:plain

いただいた盛り合わせはどれも美味しかったです。東京だったらもっと高いであろう夕ご飯が地方価格でしかも美味しい…ワインの種類も豊富に用意されており弊社のワイン大好き社員が感激のあまりワイン講義を初め出すほどでした。

次回以降のRWCなどで松江市観光を検討されている方へ向けたアドバイスを一つ書いておきます。松江市周辺で外食できるお店は全体的に東京より閉まるのが早かったりメニューがなくなるのが早いので、東京の時間感覚で行くとご飯難民になる可能性があります。

この日私達は8時ぐらいからご飯場所を探しましたが、他のお店で「もうご飯がない」と断わられていました。庄助さんで美味しいおでんにありつけたのは運が良かったです。

2日目のアフターパーティには出ずに帰りましたが、初の島根・松江だったメンバーが多かったので、もっとゆっくりすればよかったかもしれないですね。しかしそれはまたいつか機会があるときにリベンジしたいですね。

まとめ

Ruby関連のイベントにスポンサーブースを出すのは初めてで慣れない箇所もありましたが、メドピアを知っていただく機会が増え、多くの方との交流ができたことが嬉しかったです。

個人的な話となりますが、私は地方出身の人間なので、地方のIT産業の活性化には興味があります。

松江の学生は、学生のうちからこのような世界レベルのRubyプログラマーのセッションを自分の地元で聞ける機会に恵まれていて素晴らしいなと思いましたし、島根県・松江市にはこういった取り組みをぜひこれからも続けてほしいと思いました。

弊社としても、今後もこのようなスポンサーの機会がまたあれば良いなと思っております。

f:id:marikokobayashi:20181116002540j:plain

以上、RubyWorld Conference2018のレポートをお届けいたしました!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

メドピア に整地部ができたってよ!

はじめに

こんにちは、メドピアの駆け出しエンジニアの川﨑です。

最近我が広島カープが日本シリーズ進出を決めて機嫌が良いのでブログ書きたいと思います。

今回僕がお届けするのは、先月の9月14日 「Rails開発での技術的負債との付き合い方」 をテーマに開催された 「MedBeer」 にて、 Classi株式会社のCTOである佐々木 達也(@sasata299)さんの発表にて紹介された「整地部」がMedPeerでもできたよ!というお話です。

整地部とは?

整地部の定義に関しては、以下にある佐々木 達也さん の興奮する資料が分かりやすいです。

一言で言うと

技術的負債となっているコードを少しずつでも良くしたい人たちの集まり

です。

どんな風に進めたか?

記念すべき第一回は、弊社Railsの技術顧問である前島(@willnet)さんがいらっしゃる水曜日に開催されました。

進め方としては、僕が前々から直したいと思っていた箇所を自分なりに修正したPRを作っていたので、そのPRを元にモブプロで洗練させる形をとりました。

今回の整地対象は?

MedPeerでは製薬企業様から医師の方々へ向けた講演会をWeb上で行えるサービスを展開しており、講演会サービスのTOPページではオススメの講演会をスライドで表示しています。

そのスライドに表示する講演会の情報を抽出するロジックが今回の整地対象となりました。

対象となった理由は以下の通りです。

  • 講演会の情報を抽出するロジックが少し複雑
  • ロジックが書かれているモデルが300行を超え肥大している
  • ActiveRecord のメソッドを使えば良いところが散見される
  • メソッド/変数/定数の名前がわかりにくい

どんなことをしたのか?

前提として

・ロジックが書かれているモデルが300行を超え肥大している

という問題に辛さを感じていたので、以下前島さんの記事を参考にしつつ、対象の処理を単機能として一つのクラスへ切り出す方針で進みました。

tech.medpeer.co.jp

クラスに切り出した後は主に以下のことを行いました。

  • 1メソッドでやるべきでない処理を分けてわかりやすくする
  • メソッド/変数/定数の名前をいい感じにする
  • ActiveRecord のメソッドを使った方が良いところは使うようにする

実例

ここで実際に整地された例を一つご紹介します。

今回クラスへ切り出したコードの中で次のようなメソッドがありました。

  def today_conferences(conferences)
    newly_start_at(conferences.find_all { |c| c.start_at.today? }, TODAY_RECOMMENDATION_COUNT)
  end

  def newly_start_at(conferences, limit) 
    conferences.sort_by(&:start_at)[0..limit] 
  end

このメソッドでやりたいことは次の通りです。

  1. 取得した講演会から当日開催のものだけ抽出
  2. 開催日の昇順ソート
  3. 先頭からlimit取得

やりたいことは実装できていますが辛いですね。

今回は ActiveRecord のメソッド where,order,limit を使って次のように修正を行いました。

  def today_conferences
    conferences.where(start_at: Time.zone.today.all_day)
               .order(:start_at)
               .limit(TODAY_RECOMMENDATION_COUNT)
  end

Rubyの世界でのごちゃごちゃした処理がなくスッキリ書けているので、やりたいことが明確になり可読性が上がったかと思います。

実際やってみた感想

実際やってみて特にデメリットは感じないほど良い活動だったと思います。

メリットはたくさんありましたが、個人的に強く感じたことは次の2点です。

  • レベルが高いエンジニアの方と一緒にモブプロするのすごい勉強になる
  • プロジェクトへ貢献するチャンス(特に僕のような新人エンジニア)

一つ目はそのままの意味で、実装するときの考え方や気をつけることを学べる場にもなると思います。

二つ目についてですが、新人エンジニアは実力的にプロジェクトへ大きく貢献することが難しいと思います。

そういった状況でも、整地部の活動へ積極的に参加することで、技術的負債を作らない方法を学びながらプロジェクトに貢献できる点がメリットだと感じています。

今後はどんな感じで進めて行くのか?

実際に活動してみて割と大きな修正が必要なものが見えてきたこともあり、今後は以下のような方法で活動を進めて行く予定です。

・活動頻度は1回1.5h/隔週
・大きな修正は技術力の高いエンジニアでモブプロ
・新人エンジニアは上記モブプロを見て勉強したり、自分で修正できる箇所があれば整地する(必要であればペアプロ依頼)

また、工夫ではないですが、整地活動をする上でやっておいた方が良いこととして、以下2点を意識すると良いと思いました。

・新人エンジニア場合、余裕があれば事前に自分なりに修正したPRを作っておく
・小さいもので良いので、1つはPRを作るところまで対応する

今後の活動へ向けた気持ち

当然ですが大きな成果が出たりする活動ではないので、地道に続けていくことが重要だと思います。

佐々木 達也さんの発表でもありましたが、小さな修正を続けて成功体験を増やしていくことがとても重要だと感じておりますので、僕自身も積極的に活動へ参加したいと思っております。

おわりに

今回はMedPeerで始まった新たな取り組みについてのご紹介でした。

技術的負債は作らないのが一番ですが、長くシステムを運用していく中で浮き彫りになってくるものもあるかと思います。

技術的負債に目をつぶらず、積極的に立ち向かうチームが社内にできたことそのものがとても素晴らしいことだと感じています。

今後も負債に負けない柔軟で堅牢なシステムを維持するべく整地部は突き進みます!!!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

「MedBeer -Rails開発での技術的負債との付き合い方-」を開催しました!

身長体重が変わっていないにも関わらず、5年前より体脂肪率が4.5%増加したエンジニアの村上(@pipopotamasu)です。

本日は9/12(水)に開催したMedBeerというイベントを報告したいと思います。

medpeer.connpass.com

f:id:ec0156hx39:20180921184837j:plain f:id:ec0156hx39:20180921165847j:plain @GINZA SIX 12F 株式会社リンクアンドモチベーション内 イベントスペース

MedBeerとは?

MedBeerとは、年1回くらいのペースでメドピアが主体となって行なっている技術イベントになります。 基本的にRailsに関することをテーマに発表を行い、今回で4回目の開催となりました。

第1回から着々と規模が大きくなっており、前回は約60名、今回は約120名程が参加されました。

また「Beer」という名をつけているので、10種類ほどのビールとそれにおつまみ(今回はからあげエンジニアが参加するということでしたので、日本で初めて外食メニューとして唐揚げを出した三笠会館の唐揚げを用意しました)を用意していました。

MedBeer -Rails開発での技術的負債との付き合い方-

サブタイトルにも書いてある通り、今回のテーマは「Rails開発における技術的負債」です。 このテーマでメドピアからは2名、またClassi株式会社CTOの佐々木さん、クックパッド株式会社の小室さんをお招きし、計4名で上記テーマに沿って発表を行いました。

Rails Good Parts, Bad Parts

まず初めのトップバッターはメドピアの技術顧問、前島真一(@willnet)さんです。

f:id:ec0156hx39:20180921183445j:plain

発表では「負債の増加を防ぐにはどうしたら良いか?」という観点から、Railsで負債の増加を防ぐ方策を提示していただきました。 重要な要素を一部抜粋すると以下のようになります。

  • Railsが用意する便利機能と、要注意な機能の扱い方を知ることで負債を貯めずに開発することができる
  • そのためにはレビューや社内教育などを通してRailsに習熟することが大事

また発表の中では、前島さんの主催するクリーンなRailsのコードを議論するコミュニティ「clean-rails.org」を紹介していただきました。 皆さんもRailsの設計や実装で悩んだ時など、こちらで相談したらいかがでしょうか?

発表資料は以下になります。

小さな成功体験を積み重ねてチームで負債に立ち向かう

2番目は、Classi株式会社でCTOである佐々木 達也(@sasata299)さんに、負債返却についてClassiで行なった事例について発表していただきました。

f:id:ec0156hx39:20180921184318j:plain

負債返却に必要なものは「負債返却のリソース」と「負債返却いけるいけるという気持ち」の2つが必要で、後者を中心に話していただきました。 既存コードの改修のためにモブプロ/ペアプロの導入や、整地部というコードをリファクタリングする活動を設けたりと、とても参考になる事例を含む発表でした(ちなみに整地部はメドピアで取り入れようとする動きがあります)。

発表資料は以下になります。

メドピアにおけるライブラリアップデート

3番目は私、メドピアの村上大和(@pipopotamasu)が発表させていただきました。

f:id:ec0156hx39:20180921183639j:plain

「メドピアにおけるライブラリアップデート」という題の通り、メドピアでどのようにライブラリのアップデートを行なっているかを発表しました。 メドピアではGemをアップデートするためのフローがあり、1ヶ月毎にそのフローに沿ってアップデートを行い、バージョンが最新と乖離しないように(=負債をためない)しています。具体的にどんなフローでアップデートを行なっているのかの説明と、そのフローをJavaScriptのライブラリに適用させようとしていることをメインで話しました。

発表資料は以下になります。

クックパッドの巨大 Rails アプリケーションの改善

最後に発表したのは、クックパッド株式会社の小室 直(@hogelog)さんです。クックパッドの巨大なRailsアプリケーションの改善について発表していただきました。

f:id:ec0156hx39:20180921184340j:plain

お台場プロジェクトというクックパッドの巨大レポジトリの改善プロジェクトのお話がメインでした。「巨大なRailsアプリケーションの改善は計測と可視化から」という言葉通り、CIにかかった時間・アプリのロード時間・コード量・依存Gem数など詳細にデータをとり、改善結果がそれらの数値にどう影響あったかを逐次確認できる という状態にして、プロジェクトメンバーのモチベーション向上やメンバー以外への成果の共有にも繋がるため、とても参考になりました。

発表資料は以下になります。

終わりに

冒頭にも書きましたが、今回のMedBeerは今までで最も規模の大きいのものになりました。 多くの皆さまに参加いただき、他社の事例も知ることができ、実りの多いイベントでした。 また、急遽会場を貸していただき、当日の準備も手伝っていただけたリンクアンドモチベーション社の皆さまも本当にありがとうございました。 様々な人の協力もあり、第4回MedBeerも良いイベントにすることができました。

今回参加された人もされなかった人も、次回のMedBeerでお待ちしてます!!!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

開発環境のレンダリング完了時間を1/10にした話(Rails)

ビール大好きですが、3回に1回は年齢確認をされる26歳エンジニアの村上(pipopotamasu (pipopotamasu) · GitHub)です。

今回はリクエストからレンダリング完了までの時間を減らした取り組みについて書こうと思います。

f:id:ec0156hx39:20180820162919p:plain

きっかけ

メドピアではメインプロダクトをRuby on Railsで開発しています。 Railsで開発を始めてから2年半、すくすくとアプリケーションが成長してきています。 しかし成長していくにつれ問題が出てくるのが世の常、以下のような問題が生じてきました。

「ページ読み込みが遅い...」 f:id:ec0156hx39:20180816143017p:plain

Webアプリケーション開発者は開発環境で1日に何十回とページ読み込みをさせると思いますが、Railsの成長と共にそれがめちゃくちゃ遅くなってきたのです。

環境

メドピアのフロントエンド環境はassets pipeline(browserify-railsを用いてビルドしている)で管理している部分と、Webpackerで管理している部分があります。 今回の取り組みは、JavaScriptをassets pipelineからWebpackerへ移行している過渡期に行いました。

問題は何か?

さて、ページの読み込みがめちゃくちゃ遅いという問題にぶちあたりましたが、これだと問題が大き過ぎるのでもうちょっと細分化してみましょう。 以下はリクエストからページレンダリング完了までの流れになります(超ざっくりです)。

f:id:ec0156hx39:20180817113222p:plain

①~⑤まで確認してみたところ、②と④、⑤に時間がかかっていることがわかりました。

まずdev toolの計測を見てみましょう。 f:id:ec0156hx39:20180817131401p:plain

青色が待機時間、サーバーから応答が返るまでの待ち時間です。最初のHTML取得で結構時間がかかっていることがわかります。 ピンク色はブロック時間、ネットワーク接続の待ち行列で費やした時間を表しています。

最初のHTML取得までの時間、つまり②の時間が遅いことについては、開発環境であるためRailsのconfig.cache_classesをfalseにしていてコードのキャッシュをさせないようにしているので、遅いのはある程度仕方がないのですが、それにしても遅い...。サーバ側でのViewのレンダリングにかなりの時間を費やしています。

# config/environments/development.rb

config.cache_classes = false

f:id:ec0156hx39:20180820005252p:plain

またdev toolの計測を見てみると、CSSやJavaScriptといったアセットを取得しに行く時間、つまり④、⑤も異常に遅いです、全リソースを読み込むのに15秒もかかってるorz。そもそも読み込もうとしているJavaScriptが多すぎますね。

原因は何か?

②が遅い原因

アプリケーションログを遡っていくと、あることに時間を費やしているのがわかりました、browserifyのビルドです。 JavaScriptに何かしらの変更を加えた時に、browserifyのビルドが走っていてそれが遅く感じる原因のようです。 f:id:ec0156hx39:20180820005453p:plain

どうやらbrowserifyはWebpackなどと違い、デフォルトで差分ビルドができず(watchifyというプラグインが必要)、JavaScriptを変更した後にエントリーポイントで読み込むファイルを全ビルドしていたためにかなり遅くなっていました。 読み込んでいるJavaScriptの種類にもよりますが、約4~6秒ほどかかっていました。

④と⑤が遅い原因

読み込もうとしているJavaScriptが多すぎて、ブラウザの同時接続数(Chromeは6)をオーバーしているためです。前述したdev toolのピンク色がブロック時間、つまり接続待ちを表しています。

そもそも何故こんなにも読み込むJavaScriptが多いのでしょうか?

どうやらdevelopment環境のデフォルトで、assets pipelineで本来一纏めにされるJavaScriptがデバック用にバラバラで読み込まれる設定がされているようです。

# config/environments/development.rb

config.assets.debug = true

これにより読み込むJavaScriptが多くなりすぎて、接続待ちに時間が多く費やされていました。

対策

browserifyのビルドが遅い問題

watchifyを導入して、差分ビルドをさせるようにするという案もありましたが、最終的にWebpackに置き換えるという形にしました。 これについては別に記事を書いているのでよろしければご覧ください。

tech.medpeer.co.jp

最終的にbrowserifyの全ビルドから、Webpackの差分ビルドに置き換えたことにより、 4~6秒 → 1.5秒 に短縮することができました。

f:id:ec0156hx39:20180820010534p:plain

読み込むJavaScriptが多すぎる問題

デバックモードもそれはそれでデバックしたい時には便利なので、単純にconfigの値をfalseにするだけだと切り替えが面倒です。

# config/environments/development.rb

config.assets.debug = false # デバッグしたい時にtrueに書き換えてappサーバを再起動する必要がある

しかしconfig.assets.debugがfalseの時、Railsのjavascript_include_tagヘルパーのdebugオプションをtrueにすることで、デバックモードに切り替えることができます。

railsguides.jp

このオプションを利用して、debugしたい時だけURLに特定のパラメータを付与するとdebugモードに切り替わるヘルパーを作成しました。

  def javascript_include_tag_with_debug_option(js, options = {})
    javascript_include_tag js, options.merge(debug: params[:js_debug].present?)
  end

このヘルパーを利用することで、通常時のリクエストはdebugモードはOffですが、以下のように特定のパラメータを付与するとOnにできるようになりました。 https://hostname.jp/articles?js_debug=on

終わりに

今回は開発環境の話でしたが、本番環境でもリソース取得時間の改善の余地がまだまだあります。 「サイトの読み込み時間を爆速にする」ことに興味がある方は是非一度メドピアに遊びに来てください。

また9月12日にメドピアでRailsの技術的負債をテーマにした「MedBeer」というイベントを開催します。

medpeer.connpass.com

負債の予防・返却に興味がある人、ビールが飲みたい人は是非参加してください。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

開発合宿@伊豆大島に行ってきました!

こんにちは。メドピアのサーバサイドエンジニアの小林です。

まずはじめに、この度の西日本豪雨で被災された方々にお見舞い申し上げますとともに、被災地の一日も早い復興を心よりお祈り申し上げます。

今回のブログでは、メドピアでは恒例となっているエンジニア・デザイナー開発合宿に参加してきましたので、レポートさせていただきます。

メドピアでは、これまでにも年2〜3回ぐらいのペースで開発合宿を開催しています。

この夏は、東京の離島、伊豆大島へ…!メドピアとしても合宿で島へ行くのは初めてのことでした。

1日目

東京港竹芝ターミナルから船に乗っていざ、出発! 船は予想通り(予想以上に?)揺れるので酔い止め薬必須です。

船に揺られること2時間、着いたのは伊豆大島の北の玄関口、岡田港です。

f:id:marikokobayashi:20180711194653j:plain

ここから更に三原山に登り、車で30分程経った頃、標高500mほどに位置する大島温泉ホテルさんに到着。

【公式サイト】伊豆大島・三原山温泉/大島温泉ホテル

f:id:marikokobayashi:20180711195031j:plain

到着…したは良いものの…あたりは一面の霧でした…!肌寒い…!

私はあたり一面を山に囲まれた田舎出身者ですが、今回は事前にちゃんと標高を調べていなかった中、実家の標高より高い場所に来てしまい *1、軽装で来た(薄手のカーディガンは持っていましたが…)ことを若干後悔しました。

しかし、今回は観光に来たのではなく合宿。山を降りようにもこの霧の中ではいまいち降りる気も湧いてきません。 合宿所にこもるにはうってつけではないでしょうか…!?!?

f:id:marikokobayashi:20180711210044j:plain
名物の椿フォンデュの天ぷらが美味しかったです。ところてんが食後のデザートのあとに出てきました

今回は、ホテル内に全員が集まれる会議室や広間のような場所は借りていないため、借りた客室の中で一番広い部屋にいったん集まって、それぞれが何をやるか発表しあって合宿スタートです!

f:id:marikokobayashi:20180711210732j:plainf:id:marikokobayashi:20180711210218j:plain
1部屋に集まってやること発表会

2日目

2日目も、特に皆出かける予定もなく、宿でもくもく…。

f:id:marikokobayashi:20180711211007j:plainf:id:marikokobayashi:20180712153054j:plain
もくもくしている様子
ちなみに温泉は昼は3時から夜の12時まで入れるようになっていましたので、3時になるとすぐにお風呂に入り出す人もいました。
f:id:marikokobayashi:20180711210927j:plainf:id:marikokobayashi:20180711211022j:plain
2日目のご飯の様子

最終日

最終日はすぐにやってきます。3日間あるとはいえ、1日目の夕方に宿に到着して3日目の朝には宿を出発。 ご飯・睡眠・お風呂の時間も考えると、開発にあてられた時間は実質1日強といったところです。

最終日の朝、宿を出発したあと、帰りの船に乗るまで時間がありましたが、あいにくの雨でしたので、一旦皆で元町港の近くにある御神火温泉さんに立ち寄り、休憩所で開発をしたりそれぞれ好きなように過ごしていました。

f:id:marikokobayashi:20180712201548j:plainf:id:marikokobayashi:20180711211250j:plain

その後、出発1時間ほど前になってやっと雨が止んできたので、岡田港にバスで向かい、近辺でお土産を購入したり、アイスを食べたりと束の間の夏気分を味わいました。

f:id:marikokobayashi:20180711211439j:plainf:id:marikokobayashi:20180711211411j:plain
べっこうずしが美味しかったそうです(私は食べれなかった)

成果発表会

成果発表会は後日会社で行いました!FirebaseやGraphQL、Google Cloud Speech APIなどのライブラリを試した人、資格取得に向けた資料作りをした人、パフォーマンスチューニングに挑んだ人などがいて様々な種類の発表がありました。エンジニア・デザイナー以外のメンバーにも聞いていただけました!

f:id:marikokobayashi:20180711212134j:plainf:id:marikokobayashi:20180711212238j:plainf:id:marikokobayashi:20180712152154j:plainf:id:marikokobayashi:20180711212207j:plain

まとめ・感想

今回は伊豆大島ということで、最初は「夏の海!観光!」といった期待をしていた人が少なくなかったようですが、実際は天気もあまり優れず山の上にこもりきりということになりました…!! そのぶん開発自体に集中できたという人も多いのではないかと思います。

私個人としては、今回は3人1チームで1つ新サービスを立ち上げるということにチャレンジしました。

私は合宿に参加するのが1年前から数えて3回目になりました。 これまでにも合宿中に新サービス作りにチャレンジしようとしたこともありましたが、今まではRails開発経験の浅さもあって(と言ってしまうと言い訳じみていますが…)人に見せられるような形での新サービスを3日で作り上げるということはできなかったのですが、今回は3人のチームで、各々の分担を決めて限られた日程の中でスケジュール立てして作っていったので、発表のときには「新サービスこんな感じで作ったよ!」と紹介することができ、達成感がありました。

合宿に参加する意義としては、新しいライブラリを試したり、普段業務で後回しになって手を出せずにいるようなリファクタリング等に取り組める場として使えるというのももちろんですが、「短期間でどうやって人に見せられるような成果を出せるか?」の経験を積むことが出来るという側面もあるように感じました。

今後も開発合宿を定期的に開催していただける予定のようなので、次回あたりには是非私の地元の県での合宿の開催を検討してもらえないかな〜と密かに狙っています。

以上、メドピア開発合宿@伊豆大島の様子をお伝えしました!

f:id:marikokobayashi:20180711213126j:plain


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*2

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:長野県民は実家の標高を言えるらしい(詳しくはググってください)(私も急に聞かれたけどおおよそ正確な値を言えました)

*2:リードエンジニアも募集中です!

poltergeistからheadless chromeへ移行する時に気をつけること

こんにちは。メドピアのRuby(Rails)化をお手伝いしている@willnetです。最近は子育てに忙しくしています 👶

先日、メドピアで利用しているcapybaraのjavascript driverをpoltergeistからheadless chrome(selenium-webdriver)に移行しました。driverを変更するにあたって既存のテストコードをいくつか修正する必要があったので、そこで得た学びを共有したいと思います。

なぜ移行したのか

ここ数年、Railsでエンドツーエンドのテストを書くときにはpoltergeistを使う、というのがデファクトスタンダードだったはずです。それ以前はみんなcapybara-webkitを使っていましたが、poltergeistはバックエンドにPhantomJSを使っており、Qtに依存しているcapybara-webkitと比べてビルドが簡単だったことから徐々にシェアを増やしていったように思います*1

そんな中、だいたい1年ほど前にChromeのバージョン59からヘッドレスモードが実行できるようになりました。これをうけて、PhantomJSの開発が停止されました。PhantomJSをメンテするのはだいぶ大変だったみたいです。

これは当然、依存しているpoltergeistにも影響します。PhantomJSの依存をやめる構想もあったようですが、リポジトリを眺めている限りではあまり進捗しておらず、さらにpoltergeistの開発自体あまりアクティブではないように見えます。

ここまでの経緯を考えると、headless chromeに切り替えるのは無難な選択と思われるのですが、移行時期には考慮が必要でした。headlessモードが使えるchromeがリリースされた当初は、chromedriver(chromeを別プロセスから動かすのに必要なもの)に大きめのバグがあり、すぐに移行するには不安な状況でした。しかしそれから1年ほど経過した今なら問題なく移行できるのではないか?となったのでした。

そんなわけで、フィーチャスペックのときに使うドライバをpoltergeistからheadless chromeに変更しました。

f:id:willnet:20180619182309p:plain

移行に必要なこと

リリースから1年経ってこなれたと言っても、poltergeistからheadless chromeへの変更は単純に入れ替えただけでは完了しません。テストコードの変更も必要です。

僕たちはcapybaraのインターフェースを経由してheadless chromeやpoltergeistを利用しているので、移行してもテストの書き方は基本的には変わりません。ただ、ドライバによって対応しているメソッドとしていないメソッドがあるため、poltergeistのときは動いていたコードがheadless chromeでは動かないケースがあるのでした。そういうところは動くように書き換えてあげる必要があります。

大抵の箇所は機械的に置換していくようなやり方でOKなのですが、テストの内容によってはもっと頑張らなければならないところがあるのでそれを紹介します。

ファイルダウンロードのテストを変更する

例えばリンクをクリックするとcsvがダウンロードできるようなページがあったとします。poltergeistを利用している場合、次のようにレスポンスヘッダを見て、Content-Typeが text/csv であることをもってテストを成功をみなすという方法があります。

context 'CSVダウンロード用のページに遷移したとき' do
  before { visit csv_download_path }

  it 'かつ"CSVダウンロード"をクリックしたらCSVファイルがダウンロードできること' do
    click_on 'CSVダウンロード'
    expect(page.response_headers['Content-Disposition']).to eq("attachment; filename=\"download.csv\"")
    expect(page.response_headers['Content-Type']).to eq("text/csv")
  end
end

上記のコードは、headless chromeに切り替えるとうまく動きません。どうやらheadless chrome(正確にはselenium webdriver)はレスポンスヘッダを見るAPIを提供していないようです。バグとかではなくそういう設計方針の模様

レスポンスヘッダを見れないとしたら、実際にダウンロードしたファイルの内容を確認するしかありません。こちらのエントリを参考にしつつ、実際にダウンロードしたファイルの中身をチェックする方法に変更しました。

まず、次のようなヘルパーメソッド群をモジュールとして定義し、フィーチャスペックで使えるようにします。

module DownloadHelper
  TIMEOUT = 10
  PATH    = Rails.root.join('tmp/downloads')

  module_function

  def downloads
    Dir[PATH.join('*')]
  end

  def download
    downloads.first
  end

  def download_content
    wait_for_download
    File.read(download)
  end

  def wait_for_download
    Timeout.timeout(TIMEOUT) do
      sleep 0.1 until downloaded?
    end
  end

  def downloaded?
    !downloading? && downloads.any?
  end

  def downloading?
    downloads.grep(/\.crdownload$/).any?
  end

  def clear_downloads
    FileUtils.rm_f(downloads)
  end
end

RSpec.configure do |config|
  config.include DownloadHelper, type: :feature
  config.before(:suite) { Dir.mkdir(DownloadHelper::PATH) unless Dir.exist?(DownloadHelper::PATH) }
  config.after(:example, type: :feature) { clear_downloads }
end

これにより、先程のテストを次のように変更することができます。

context 'CSVダウンロード用のページに遷移したとき' do
  before { visit csv_download_path }

  it 'かつ"CSVダウンロード"をクリックしたらCSVファイルがダウンロードできること' do
    click_on 'CSVダウンロード'
    expect(download_content).to eq "row1,row2\n"
  end
end

さてこれで解決…と思いきや、もうひとつ対処が必要だったりします。どうやら、 headless chromeはデフォルトでファイルダウンロードをしないようです。そこでstackoverflowの回答を参考に、ファイルダウンロードを許可するようにしました。具体的には次のようにconfig/rails_helper.rbに記述しています(bridge変数以下がファイルダウンロードを許可しているコード)。

Capybara.register_driver :headless_chrome do |app|
  driver = Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
      login_prefs: { browser: 'ALL' },
      chrome_options: {
        args: %w(headless disable-gpu window-size=1900,1200 lang=ja no-sandbox disable-dev-shm-usage),
      }
    )
  )
  bridge = driver.browser.send(:bridge)
  path = "session/#{bridge.session_id}/chromium/send_command"
  bridge.http.call(
    :post, path,
    cmd: 'Page.setDownloadBehavior',
    params: {
      behavior: 'allow',
      downloadPath: DownloadHelper::PATH.to_s,
    }
  )
  driver
end

Capybara.javascript_driver = :headless_chrome

これでファイルダウンロードをheadless chromeでテストできるようになりました。

追記

最近(2019年9月)のchrome77からはこの方法だとテストが失敗するようになっています。詳細について新しい記事にしたので参考にしてください。

最近のheadless chromeを利用したファイルダウンロードのテスト方法について - メドピア開発者ブログ

ブラウザ上で見えない要素に対応する

headless chromeは、poltergeistと比べて「ブラウザ上で見える要素であるか否か」にシビアなようです。例えば、position: fixed; left: 1000px としているDOM要素があるとします。このとき、ブラウザのウィンドウサイズが(800, 600)のように小さく要素が画面外になる場合は、その要素は見えないという扱いになります。

この場合は単にウィンドウサイズを大きくしてあげればよいのですが、それでは対応できない場合は次のようにvisible: falseをつけて、見えない要素の中で指定のDOMが存在するかを確認する必要があります。

expect(page).to have_css('.super-right-dom', visible: false)

このように「画面外にあるので見えない」というのはわかりやすいのですが、なにをもって「見える」「見えない」と判断しているのか難しいなと感じるケースがあります。例えばあるページではbxSliderを利用して画像をカルーセル表示しているのですが、スクリーンショットで見えていることが確認できるDOM要素もheadless chrome的には見えない扱いをされていました。

やむを得ずこれもvisible: falseとして対応しました。しかしこのあたりはまだ深く調査できてないので、詳しい方いたら教えていただけると嬉しいです。

細かい変更点

次のような細かい点にも対応しました。このへんは単に置換するだけなので軽く箇条書きで済ませます。

  • triggerメソッドがない
    • trigger('click')を使わず、clickメソッドを使うようにした
  • ウィンドウのリサイズのお作法が違う
    • before: Capybara.page.driver.browser.resize(320, 580)
    • after: Capybara.current_session.current_window.resize_to(320, 580)
  • ブラウザのダイアログが表示される部分は、polgergeistでは自動でacceptされるが、headless chrome(というかselenium webdriver)ではどのように対応するかをaccept_alert {} みたいに書く必要がある

まとめ

polgergeistからheadless chromeへの移行において、気をつける点と変更が必要な点について紹介しました。headless chromeに移行することで、今後メンテナンスが続いていくであろうという安心感が得られます。しかし、セットアップには今回紹介したようにコツをつかむ必要があります。

この記事がこれからheadless chromeへ移行する人や、移行に苦労している人にとって参考になれば幸いです。


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*2

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:PhantomJSもQtに依存しているけど、PhantomJSはバイナリが配布されているのでビルドせずにインストールできるというのが大きかった

*2:リードエンジニアも募集中です!

Rails × ECS でオートスケーリング&検証環境の自動構築

マリオカートでカーブを曲がるときに体を傾斜させてしまうCTO室 kenzo0107 です。

今回は 2018/04/02 にリニューアルしたイシコメの Rails × ECS についてです。

イシコメとは?

「イシコメ」は、医師10万人の声でつくるヘルスケアメディアです。
医師と一般の方々をつなげることで、医療情報格差を埋めることを目指しています。

MedPeerの10万人の医師会員に協力いただいたアンケート結果をもとに編集部で記事を執筆し、医師監修の上で配信。多くの医師の声を反映することで、より正しい情報を提供しています

https://ishicome.medpeer.jp/

リニューアル経緯

リニューアル前は以下のような構成でした。

  • フロントに Laravel 5
  • バックに Drupal
  • Docker on EC2
  • コンテナイメージの S3 でのプライベート管理

Docker がまだ出てきて間もない頃、当然、AWS ECS がリリースされておらず
果敢に技術的にチャレンジした痕跡が多々ありました。

ですが、 当時の構成では以下の問題がありました。

  • イメージの管理やデプロイがし辛い
  • スケーリングが考慮されてない

また、
Google 検索結果の医療や健康に関する検索結果の改善 が追い風となりトラフィックが伸び、スケーリングへの配慮が重要になりました。

webmaster-ja.googleblog.com

上記の問題を AWS ECS・ECR の恩恵を受けることで解決しようと考えました。

また、
開発促進をすべく、弊社で開発の知見のより多い Rails へのリプレイスを行う運びとなりました。

まず結論

※ 左側がユーザサイド、右側が管理画面になります。

  • イメージ管理は ECR
  • デプロイは ecs-cli
  • CloudFront > ALB > Nginx > Rails というルーティング*1
  • Tasks, EC2 をオートスケーリング
  • RDS Aurora MySQL へリプレイスし Read Replica オートスケーリング

メディアサイトというサイト特性もあり、アクセスが集中しスパイクすることを鑑みて CloudFront でのキャッシュを有効にし、オートスケーリングできるところはする様にしました。

リニューアル直後の 2018年4月頃、麻疹が流行した際に麻疹関連の記事へのアクセスが急増した際は、本当にこの構成にしておいてよかったと思いました。

https://ishicome.medpeer.jp/entry/767ishicome.medpeer.jp

続いて ECS でサービス構成するに当たって考慮したことをまとめました。

ECS への準備で考慮したこと

  • コンテナ設計
  • デプロイ
  • ロギング
  • オートスケール
  • バッチ処理
  • 検証環境

コンテナ設計

Nginx

Nginx をルーティングに挟んだのは以下の理由からです。

  • Rate limit の設定を細かくコントロールしたかった。*2
    • AWS WAF では最低でも 5分間に 2000 リクエスト以上でアクセス制限可能
  • IP 直指定回避

Rails

Rails コンテナについて以下の点を設計考慮しました。

secrets.yml

yaml_vault で暗号化・復号するようにしました。*3

  • KMS のエイリアスキーを作成し
  • そのキーでの暗・復号権限を IAM Group で管理する

こうすることで
権限の付与時には IAM Group に含める、
権限の剥奪時には IAM Group から除外する、
と管理が楽になりました。

Task Definition には基本秘密情報を載せない様にしました。*4

  • 実行コマンド
// secrets.yml の暗号化 (env: production)
yaml_vault encrypt \
  config/secrets.yml \
  -o config/encrypted_secrets.yml.production \
  --cryptor=aws-kms \
  --aws-region=ap-northeast-1 \
  --aws-kms-key-id=<kms alias> \
  --aws-profile <profile>

// secrets.yml の復号 (env: production)
yaml_vault decrypt \
  config/encrypted_secrets.yml.production \
  -o config/secrets.yml \
  --cryptor=aws-kms \
  --aws-region=ap-northeast-1 \
  --aws-kms-key-id=<kms alias> \
  --aws-profile <profile>
Dockerfile (Rails)

RAILS_ENV を渡してビルドし各環境毎に処理分けさせてます。

FROM ruby:2.5-alpine

RUN apk update \
  && apk upgrade \
  && apk add --update build-base mysql-dev nodejs tzdata git \
  && rm -rf /var/cache/apk/*

# TZ JST
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ENV app /work/app
WORKDIR $app

COPY . $app
RUN bundle install -j4 --retry 6 --without test development --no-cache \
  && npm install --production --no-progress \
  && mkdir -p tmp/sockets \
  && mkdir -p tmp/pids

ARG RAILS_ENV

# docker build 時に db 接続しようとする為、接続しない様、nulldb 指定
RUN chmod +x docker/on_build.sh \
  && sync \
  && docker/on_build.sh

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

docker/on_build.sh

  • assets:precompile, assets:sync 実行時に S3 にアップロード, CloudFrontで配信 by asset_sync, fog-aws
#!/bin/sh

set -e

# docker build 時に db 接続しようとする為、接続しない様、nulldb 指定
DB_ADAPTER=nulldb bundle exec rake assets:precompile assets:sync RAILS_ENV=${RAILS_ENV}

デプロイ

ecs-cli を採用しました!

採用理由は以下の通りです。

  • ECS 向けの AWS オフィシャルのツール
  • 既存の設定した AWS credential 利用可
  • Task 定義が docker-compose.yml 形式
  • Fargate 対応可

Fargate Tokyo region が待ち遠しいです♪*5

デプロイフロー

  1. master, develop, qa/* に merge をトリガーに CircleCI ビルド
  2. テストがパスしたら CodePipeline 開始
  3. CodePipeline で cap (production|staging) deploy 実行

CircleCI には CodePipeline の開始・更新権限のみの AWS Access Key ID, Secret Key を設定しています。

CircleCI 上からデプロイしないの?

はじめに検討したのですが以下理由により上記構成を選択しました。

  • CircleCI とビルドサーバを分けることで CircleCI はテストに専念できる。
    → デプロイが終わるまでリソースを手放さず他のビルドが遅延するのを避ける。

ゆくゆくは CodePipeline でテストも実行し全てを完結させてみてコスト比較する検証をしたいと思っています。
既にあったら教えてください

ロギング

  1. コンテナから awslogs で CloudWatch に出力
  2. Lambda で日次で CloudWatch のロググループを S3 に保存

Rails コンテナでは環境変数に RAILS_LOG_TO_STDOUT: 1 を設定することで Rails のログを標準出力し、CloudWatch に流す様にしています。

また、お好みですが
lograge で CloudWatch 上のログの視認性が高まりました。

CloudWatch のイベントフィルターで

{$.db >= 1000}

とすることで DBで 1000 msec 以上時間を要したイベントを抽出することができます。

オートスケール

スケールアウト時に考慮したことと

  • まず EC2 インスタンスを増やして、その後、Task を増やす
    → Task が一方に偏ってしまう等の事象が発生してしまう為です。

CloudWatch の監視設定で evaluation_periods を EC2 < Task の様に設定しました。

  • インスタンス単体の CPU 使用率等のメトリクスでなく、Service でのメトリクスで考えること
    → 一時的な偏りでなく、全体的に負荷が高い場合にスケールする、という様にする為です。

  • RDS Aurora MySQL のオートスケールは Read/Write を switch_point で参照先を切り替える様にしました。

バッチ処理

クラスタを指定し ECS Scheduled Tasks で one-off container で定期実行します。

以下 Sitemap 定期更新タスク例です。

検証環境の自動構築

qa/* というブランチ名で push するとそのブランチに紐づくポートで検証環境を自動で構築する様にしました。

これによりエンジニア・ディレクターの検証回数が飛躍的に増え、画面を通じてコミュニケーションすることで認識の齟齬が減りプロジェクトがより円滑に進む様になりました。

現状、インスタンスタイプ m5.large の EC2 インスタンスをスポットで利用することで価格を抑えつつ 3~5 程度の検証環境が動作しています。

構築手順

  1. ALB の空いている Listener Port 取得
  2. 空き Port を元に target group 作成
  3. target group に branch 名をタグ付け
  4. インスタンス登録
  5. ALB Listener 作成
  6. ECS Service 作成
  7. service 名を branch 名がわかるようにラベリング
  8. QA 環境へ deploy

現状不要となった QA は cap で削除用コマンドを用意してますが ブランチ削除時に自動削除される等、検討中です。

ちなみに、どのブランチがどのポートを使っているかは Slack bot が教えてくれます。

まとめ

Rails × ECS へのリプレイスにより自動化できた部分が多く本当に運用が楽になったと感じます。

特に検証環境の自動構築は他プロジェクトでも望まれ導入を進めています。

また、Fargate の検証を実施しておりますので、その折には本ブログにまとめていきたいと思います。

以上です。
参考になれば幸いです。


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

インフラ構築・運用や開発プロセスの改善に携わっていただける SRE 募集中です!

https://medpeer.co.jp/recruit/entry/entry09.html

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*6

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:Nginx を挟んだ理由はコンテナ設計で!

*2:時折、 /phpmyadmin のようなパスでアクセスしてくるような攻撃が1分間に 200 リクエスト程きますが、 AWS WAF の based rule では防げません。

*3:イシコメ リニューアル直後に Rails 5.2 credentials が出た為、secrets.yml での秘密情報管理をしています。

*4:EC2 上で ssm で値を取得し環境変数に設定する方法もありますが Fargate の時どうするのか検証仕切れず、一旦 secrets.yml に寄せる様にしました。

*5:本記事執筆時に Fargate Tokyo Region の 2018年7月 予定がアナウンスされる神タイミングでしたので、執筆を急ぎました

*6:リードエンジニアも募集中です!