Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Railsアプリ開発の事例紹介 / A case study for a Rails App

Railsアプリ開発の事例紹介 / A case study for a Rails App

リードエンジニアから学ぶMedPeerのプロダクト開発
https://medpeer.connpass.com/event/181835/

Takumi Shotoku

July 30, 2020
Tweet

More Decks by Takumi Shotoku

Other Decks in Technology

Transcript

  1. 自己紹介 • 名前: 正徳 巧 • aka: 神速 • 社歴:

    2019年12月入社 • 11月から仕事はしてた • GitHub: @sinsoku (画像右上) • Twitter: @sinsoku_listy (画像右下) 2
  2. 5

  3. 7

  4. 話すこと / 話さないこと • ✅ AWSのインフラ構成 • ✅ Railsの設定の紹介 •

    ✅ gemのTipsの紹介 • ❌ リモートワークについて • ❌ プロジェクトの進め方 • ❌ Dockerに関する話 8
  5. CloudFrontを置く理由 • 静的ファイルのキャッシュ • 将来のパフォーマンス改善のため • セキュリティ対策(DDoSなど) • デプロイ時にassetsが404になる問題の回避1 1

    ECSのデプロイ時に一定確率で静的ファイルが404になる問題を回避する https://qiita.com/hareku/items/6be1b71e58033b9739fd 11
  6. プロジェクト初期のDockerfile 一番最初のDockerfileは以下の通り。 # NOTE: prd/stgͰڞ௨Ͱར༻͢Δrails༻ͷDockerfileͷμϛʔ࣮૷Ͱ͢ # railsଆͷ࣮૷͕ಈ͘Α͏ʹͳ͖ͬͯͨΒɺదٓमਖ਼͠ɺ࡞੒͍ͯͩ͘͠͞ FROM nginx:latest #

    ALB͔Βͷhealthcheck༻ͷμϛʔϑΝΠϧ RUN touch /usr/share/nginx/html/healthcheck # μϛʔΠϝʔδͰ͋ΔnginxͷϙʔτΛ࠷ऴతͳRailsͱಉ͡3000ʹมߋ͓ͯ͘͠ RUN sed -i -e 's/80;/3000;/g' /etc/nginx/conf.d/default.conf 14
  7. assets:precompileでsasscが死ぬ問題 sasscでsegfaultが起きたので、並列コンパイルの設定を無効にし ました。 # config/initializers/assets.rb # NOTE: assets:precompileͰsegfault͕ى͖ΔͨΊฒྻίϯύΠϧΛແޮʹ͢Δɻ # #

    ref: https://github.com/rails/sprockets/pull/469 # ref: https://github.com/rails/sprockets/issues/581 Rails.application.config.assets.configure do |env| env.export_concurrent = false end 19
  8. 複数DBの対応 DBは1つだけど複数DBの設定をしておく。 default: &default adapter: postgresql encoding: unicode # For

    details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> url: <%= ENV['DATABASE_URL'] %> production: primary: <<: *default database: foo_production primary_replica: <<: *default database: foo_production replica: true 20
  9. v6.0.4 が出るまでの回避策 RSpec.configure do |config| config.before(:suite) do # NOTE: Rails

    6.0.3ͷόάΛճආ͢ΔͨΊɺ࠷ॳʹwritingͷ઀ଓΛ࢖͏ɻ # ࢀߟ: https://github.com/rails/rails/issues/39205 ActiveRecord::Base.connected_to(role: :writing) { User.count } end end 23
  10. 機微情報のフィルタ rails/rails#342184を参考に設定した。 # config/initializers/filter_parameter_logging.rb Rails.application.config.filter_parameters += [ :password, :secret, :token,

    :_key, :auth, :crypt, :salt, :certificate, :otp, :access, :private, :protected, :ssn ] # config/initializers/rollbar.rb Rollbar.configure do |config| config.scrub_fields |= Rails.application.config.filter_parameters end 4 https://github.com/rails/rails/pull/34218 26
  11. おまけ: ログレベルの変更 productionのデフォルトは :debug なので注意。 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@

    -45,7 +45,7 @@ Rails.application.configure do # Use the lowest log level to ensure availability of diagnostic information # when problems arise. - config.log_level = :debug + config.log_level = :info # Prepend all log lines with the following tags. config.log_tags = [:request_id] 27
  12. rspec 推奨設定があるので有効化し ておく。 • 遅いテストを表示 • テスト順序のランダム化 • Kernel.srandにseedを使う •

    ...など --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -46,7 +46,6 @@ RSpec.configure do |config| -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin # This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides @@ -92,5 +91,4 @@ RSpec.configure do |config| # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed -=end end 33
  13. rails_admin 管理画面のルートにはマウントしない。 Rails.application.routes.draw do mount RailsAdmin::Engine => "/tables", as: 'rails_admin'

    end 複雑な要望は素直にRailsで機能を作る。 カスタマイズし始めると学習コストが高く、Railsのアップグレー ドが辛くなりやすい。 35
  14. rails_admin 最低限のテストを書いておく。 RSpec.describe 'RailsAdmin::Engine', type: :system do include_context 'login_as_admin' before

    { visit rails_admin_path } it 'μογϡϘʔυͱ֤ϞσϧͷҰཡϖʔδΛදࣔͰ͖Δ' do expect(page).to have_title 'αΠτ؅ཧ' link_texts = all('.sidebar-nav a').map(&:text) link_texts.each do |link_text| within('.sidebar-nav') { click_on link_text } within('.page-header') { expect(page).to have_text "#{link_text}ͷҰཡ" } end end end 36
  15. pundit, paper_trail • rails_adminのために追加 • 基本は閲覧のみを許可 • 更新は必ず履歴を残す • 誤操作を戻せるように

    • 削除は許可しない class ApplicationPolicy def index? true end def create? true end def show? true end def update? history? end def destroy? false end def history? record.respond_to?(:paper_trail_options) end end 37
  16. okcomputer ヘルスチェックを簡単に実装できる。 $ curl https://telemed.medpeer.jp/healthcheck default: PASSED Application is running

    (0.000s) /healthcheck/all でDB, Redisなどの疎通も確認できる。9 $ curl https://telemed.medpeer.jp/healthcheck/all Default Collection cache: PASSED Able to read and write via Redis cache store (0.002s) database: PASSED Schema version: 20200630095822 (0.009s) default: PASSED Application is running (0.000s) redis: PASSED Connected to redis, 9.99M used memory, uptime 6221501 secs, 35 connected client(s) (0.002s) sendgrid: PASSED none: All Systems Operational on 2020-07-29 17:20:18 +0900 (0.456s) version: PASSED Version: d01fd95a61df65252cb98c102001bf73282cf427 (0.000s) 9 sendgridは独自のチェックです。 42
  17. simplecov テストの並列実行を使うとカバレッジが壊れるので直す。11 # .circleci/config.yml - run: mv coverage/.resultset.json \ coverage/.resultset-${CIRCLE_JOB}_${CIRCLE_NODE_INDEX}.json

    - persist_to_workspace: root: . paths: - coverage/ 11 https://gist.github.com/trev/9cc964a54c8d5b62f4def891eba6b976 を参考に実装 46
  18. simplecov CircleCIでRakeタスクをを実行してカバレッジを集計する。 namespace :coverage do task :report do require 'simplecov'

    SimpleCov.start('rails') resultsets = Dir["#{SimpleCov.coverage_path}/.resultset-*.json"] results = resultsets.map { |file| SimpleCov::Result.from_hash(JSON.parse(File.read(file))) } SimpleCov::ResultMerger.merge_results(*results).tap do |result| SimpleCov::ResultMerger.store_result(result) end end end 47