読書メーターのインフラ構成について

こんにちは。

メディアサービス開発部バックエンド開発グループのフサギコ(髙﨑)です。

Ruby on Railsによるバックエンドの実装運用と、AWSによるサービスインフラの設計構築を中心とした、いわゆるテックリードのような立ち位置で働いています。

本記事では前記事のニコニコ漫画のインフラ構成についてに続いて、読書メーターのサービスインフラの概要について軽くご紹介したいと思います。

読書メーターについて

読書メーターは、2008年5月に開設された日本最大級の読書コミュニティサイトです。

単純な読書記録や感想投稿に留まらず、ユーザ間で交流ができること、出版各社さまからご提供いただき著者さまのサイン本や発売前に校閲校正をする際の仮刷りであるプルーフと呼ばれる段階の本が貰える献本プレゼント企画を行っていることも特徴的な点です。

読書メーターの構成

読書メーターは下記の6つのコンポーネントから構成されています。

  • フロントエンドサーバ群
    • Webフロントエンド(Ruby on Rails)
    • スマートフォンアプリ向けAPI(Ruby on Rails)
    • 管理画面(Ruby on Rails)
  • バックエンドサーバ群
    • バックエンド(Scala, Play framework)
    • サブバックエンド(Ruby, Grape)
    • 旧環境(PHP)

これらの関係を概要図に表すと下記のようになっています。

f:id:bookwalker_writer:20220201194406p:plain
読書メーターの構成概要図

これらのサービスインフラを主にTerraformを用いて構成管理しています。

フロントエンドサーバ群

インターネットから直接アクセスして使用する、フロントエンドの立ち位置にいるサーバ群です。

いずれもフレームワークこそRailsではありますがActiveRecordを使っておらず、バックエンドサーバ群のAPIへActiveRecordライクな書き方でリクエストできるgemを内製し、それを使っています。

Webフロントエンド(Ruby on Rails)

読書メーターへブラウザでアクセスした際のPCWeb, SPWebを配信するコンポーネントです。

一部、スマートフォンアプリにおいてWebViewで表示される画面もここから配信しています。

古来からあるビューがslim + sprockets + Coffee Script + Vue 1.x + sassc-railsというお世辞にもモダンとは言えない組み合わせで動いているため変更難易度が高くなってしまっているのが大きな課題です。

これを解消するため、React + Typescript + CSS module + Webpackのマルチエントリポイントという構成で段階的な書き換えに取り組んでいます。しかしながら

  • CSS moduleが新規機能の追加を終了してメンテナンスのみに移行
  • 依存npmパッケージのバージョンアップによって見た目が崩れていないことの担保
  • 過剰な共通化の結果Reactでの再現が困難なslimテンプレート

などの課題に直面しており、悪戦苦闘しています。

一方で、デプロイにecspressoを用いてECS(Fargate)で動作するように移設できた結果デプロイ頻度を増やせるようになったなど、改善が見られる点もあります。

スマートフォンアプリ向けAPI(Ruby on Rails)

読書メーターのスマートフォンアプリ向けのjson APIです。 機能としてはかなり安定しており、良くも悪くも改修される頻度は低いです。

あまりにも安定しすぎていて未だにElastic Beanstalkで動いているのですが、他コンポーネントのECS(Fargate)移設でノウハウが確立できてきたため、これも近いうちに移設する予定です。

管理画面(Ruby on Rails)

主に運営が使用する、読書メーターの管理画面です。 管理画面ですから見た目は最低限で、ある意味もっともRailsらしい見た目かもしれません。

これもecspressoを用いてECS(Fargate)で実行しています。

バックエンドサーバ群

バックエンド

ScalaとPlay frameworkで書かれたバックエンドサーバです。

2016年11月ごろに行われた全面リニューアルに向けて書かれたもので、二つ存在するバックエンドのうちリニューアル前から存在するMySQLのデータベースを引き継いでいるのはこちらです。

当初、DDD1やDI2などを用いてレイヤをかなりしっかり分けて書こうと指向していたと聞きますが、その堅牢さと引き換えの重厚さが直接のクライアントであるフロントエンドサーバ群の要求の変化に対して追いつけていない問題があります。 そのため、変更しやすさと堅牢さのバランスの妥協をどこで行うかを模索しています。

また、メディアサービス開発部が開発運用しているプロダクト、コンポーネントの中では数少ない、現在もElastic Beanstalkで実行されているソフトウェアです。

Elastic Beanstalkは手軽であった反面デプロイの所要時間が長いため一日に可能なリリース回数が少なくなってしまう問題があり、その解決に向けてECS(Fargate)への移設に取り組んでいます。

サブバックエンド

Rubyで書かれていますがRailsではなく、GrapeというAPI向けフレームワークを使用しています。ORMはActiveRecordです。

当初はバックエンドがScalaとPlay frameworkかつDDDという比較的重厚な構成をしていたため、バックエンドよりもデータモデルを追加変更しやすいことを意図して立ち上げられたと伝え聞いています。

しかし、結局バックエンドが持つデータとjoinしたくなったり、Railsでなく不慣れなGrapeを採用したことでノウハウが共有できないなどの問題に直面しており、現在はいくつかの機能をバックエンドに再実装するなど縮小、最終的には削除に向けて動いています。

これも積極的な機能追加はされなくなっていますが、運用の手間とコストの削減のため、ecspressoを用いてECS(Fargate)で実行するようにはなっています。

旧環境

読書メーターが現在の構成になる前に存在した、PHPで書かれた旧環境です。

cloudfrontの裏にoriginとして存在する画像配信サーバのみが表向きには生き残っています。 この画像配信サーバは画像のオンデマンドリサイズ機能を持っているため、単純にorigin access identityを使ってcloudfrontからs3にあるファイルを直接配信するような構成にはできません。

このような構成ではつい新しく書き直したくなってしまいますが、料金的にも可用性的にも現状で十分安く安定しており、再構築するのにかかるであろうエンジニアの工数コストと照らし合わせると優先度がなかなか上がりづらいのが悩ましい点です。

まとめ

本記事では、読書メーターを構成するいくつかのコンポーネントとその関係について概観しました。

いずれも課題を抱えており、それぞれに人の手が必要となっています。それぞれのコンポーネントが抱える課題とそれに対する取り組みについてはまた別の記事にて、いま読書メーターを担当している開発者や私からお話する機会が来るかもしれません。

ブックウォーカーでは物理・電子・Web連載問わず漫画や本が好き、あるいは長年運用されてきたWebサービスを紐解き、より良い形に作り替えていくことに興味があるWebアプリケーションエンジニアを募集しています。

もし興味がありましたらぜひ、ブックウォーカーの採用情報ページからご応募ください。


  1. Domain-Driven Design、ドメイン駆動設計

  2. Dependency Injection、依存性の注入