昨日はckoshienさんの野球リーグスコア管理システムに用いている技術についてという記事でした。IIDXというゲームの成長支援サイトです。2014年11月に開発を開始し、運用を始めて4年ほど経過しました。現時点で登録者数は9200人ほどで、GAのマンスリーアクティブユーザは11k人ほどです。IIDXは音ゲーと呼ばれるジャンルのゲームで、楽曲に難易度が設定されています。楽曲の難易度はおおまかに言うと☆1から☆12までに分類されます。この分類はシングルプレイ(SP)、ダブルプレイ(DP)といったモードごとに存在しているのですが、本サイトではその中でもSPの☆12についてだけ取り扱ってます。repositoryとして公開しています。参考になったらスターをいただけるとモチベーションになります。SP☆12だけなのかここではゲーム性の説明を行います。技術的な話を知りたい方はスキップしてもらって構いません。SP☆12は現時点で(未解禁のものも含めると)300曲を超えています。他の難易度帯と比較すると☆12は易しい曲と難しい曲の難易度差が最も激しくなっています。☆12に挑戦し始めの人は噂程度に「この曲は☆12の中でも難しいと言われている気がする...」という選曲判断が必要になってきます。公式にも指標に近いものがあるのですが、割とあてにならないという背景もあります。人によって得意不得意分野があるため参考表も万人に当てになるわけではないですまた、☆12の攻略には長い時間が必要になります。自身の例ですと、1つのゴールであろう皆伝という最上位段位の取得に☆12触り始めから1年以上の時間が必要でした。皆伝という段位は☆12の中でも上位の難しさに位置している楽曲で構成されるコースのため、簡単な☆12からスタートし、段階を踏んで難しい☆12もクリアできるようになる必要があります。私の元々の開発モチベーションとしては、このステップをモチベを維持しつつ「続けていける」ようにすることでした。そのため自分がプレイしているSPであり、皆伝を取得するのに避けられない☆12を対象としたサイトを作り始めました。スタート地点としてはPHPで簡易的な☆12参考表サイトを作成していました。このサイトは知人等にインターナルに利用してもらっていたのですが、需要がありそうということで公開を前提に作り直し始めたのが2014年11月です。50GB)(ローカル環境のみ)k8s対応nginxgodmackerelBigQuery(アクセスログ解析)といった形になっています。AWS/GCP等も検討したことはあるのですが、VPSと比較すると月額コストが跳ね上がるため見送っています。技術的な解説はじめてRailsを使って作成したアプリだったことによるイマイチコードがいたるところに点在しています。合間を見て少しずつ改善はしているものの既にユーザが利用していると変更しにくい部分があり、手がつけにくいところもあります。Railsのroutes.rbの記法の修正は既にブックマークをしているユーザへの影響を考えると変更しにくいです。linkを参照してしまう方は想像より多かったのを記憶しています。技術的な要素でいうと、「いたって普通なRailsアプリ」をメインポリシーとして作成しています。Wayに乗っておこうという精神です。メインで開発し続けているサービスなら多少の獣道も選択としてはありですが、社会人が片手間に行う分には大衆に乗っかるほうが楽でした。最近まではフロントエンドとの連携に自作のgem使ったりしながら特殊なことをしていたのですが、そのあたりはwebpackerに乗り換え、スタンダードRailsへの変身しました。(この変身コストはとても大きかったです。releaseとしては3系ですが、このプロジェクトでは4系を利用しています。理由としては最新のts-loaderを使いたかったためです。releaseのwebpackerではwebpack3系までしか対応していなかったためです。webpackerを採用した理由としては「現在はRailsの中でそれが主流」という理由以上のことはありません。疎なwebpackインテグレーションを自作しても良かったのですが、メンテナンスを続けるモチベが持てなさそうなので敬遠しました。実際使ってみると細かいところで不満はあるものの、トータル的には体感は悪くないという印象を受けました。webpackerでは下記のように少しだけ設定を変えています。置き換えていることです。source-map自体を切る修正を入れる人が多そうな印象ですが、自分の環境ではあえて設定しています。理由はsentryでのエラートラッキングが快適になるためです。このような形でエラー部分の元ソースコードを表示することができます。いうnpmを利用することでそのときのactionやstateの状態までわかります。そのエラーになるまでのactionの履歴そのエラーが出たときのクライアント側のstateOSSなので特にこのサービスに関しては隠蔽する必要はないのですが、mapファイルはsentryサーバ以外からアクセスできないようにしています。自前でsentryを立てているので特定IPからのアクセスは弾くようになっています。}}typescriptに置き換えてからまだ一度もエラーが出ていなかったため、この画面は意図的にエラーを仕込んで発生させました...w今のところ、webpacker4系に対する不具合は踏んでおらず問題なく運用できています。react-railsReactとのつなぎ込みにはreact-railsというgemを利用しています。react_on_railsというgemのほうが多機能なのですが、相容れないところがありreact-railsを採用することにしました。react_on_railsのほうが優れている点としてはrailsContextというオブジェクトが付与されており、この中にtimezone/localeなどを始めとした開発者がほしいであろうメタ情報が入っています。Railsにxhrリクエストをする際にcsrf-tokenをheaderにセットするAPIも用意されています。利用しており、このhookではページがチャタるケースがあったため、採用しませんでした。react-railsはreact_on_railsと比べると非常にシンプルで、コンポーネントをrenderする以上のサポートはあまりないようです。それを行ってくれるだけで自分の要件的には十分だったため、採用しています。チャタるとは何かをもう少しわかりやすく言うと、一瞬古いページが表示された後に新しいページが表示される現象のことを指しています。コンテンツが一瞬表示される->白くなる->再度表示されるというループになっていることがわかると思います。一瞬表示された後と再度表示された後でAPIリクエストが2回走ってしまうという問題やUXの観点から許容できない、という判断になりました。上記はturbolinks的には「仕様」の挙動です。抜粋すると以下のように書かれています。version.react_on_railsのgem自体にはissueを投げてみたのですが、少し私の想像を超えた範疇の問題があるらしく、今回はreact-railsを採用することにしました。いうgemを利用させていただきました。似たようなgemにjs-routesと呼ばれるものがあり、そちらを採用していたのですがts化にあたって載せ替えを行っています。パラメタ不足などの不正なルーティングに関して検知しやすくなるため、tsとrailsを組み合わせるならお薦めです。Table)からのデータ取り込みを始めとした外部サービスの連携や定期処理(sidekiq-cron)などに利用しています。先述した通り☆12の譜面はかなり数が多く、1年を通して「解禁」され数が増え続けていきます。これらを全て人手によって捕捉し続けるのはコストが高い、と感じ1日に1回IST側のデータを利用して新規の☆12が解禁されたら自動追加するようにしています。☆12の中での難易度自体は議論によって決められるもののため、難易度が決定した後の反映処理は人手で行っています。当サイトはVPS1台で運用しているので、バックアップ等の日次処理も必要になってきます。これらの処理もSidekiq側に任せています。いうlibを作り、DBのdump(pg_dump)carrierwaveで保存された画像等の収集を行った後にtar.gzに固めてS3に送る処理です。これらの処理をjobにして朝の5時過ぎに行うようにしています。手元でproduction環境の再現を行いたいときにもS3からデータを取ってきています。運用しているいくつかのサービスは全てこのような形でバックアップを取っており、いつVPSが死んでしまってもすぐにサービスを復旧できるようにしています。CICircleCIを使ってテストを行っています。テストフレームワークはrspecでjs部分のテストはjest等では書いていません。specとしてchromedriverを利用したE2Eで担保しています。specではjsを使うところ使わないところで以下のように条件分岐を書いています。いう環境変数を定義して実行するとchromeが実際に立ち上がるようにしています。いったコマンドで実行します。仕込むことでElementも触れるのでspecがうまくいかないときの不具合調査などに利用しています。運用サポート死活監視にはmackerelとgodを利用しています。SaaS型のサービスでプロセスが死んでいるか生きているかの監視もできます。sidekiq/puma/redis/postgresql/nginxなどのサービス継続に必要なプロセスは全てgodに監視させています。再起動時などにもgodに任せて起動させているため、再起動するとサービスが落ちたままになっているということもないです。sidekiqは知らない間にメモリの使いすぎでプロセスが落ちていることもあったため、godに監視させて死んでいたら起こすようにするのは精神的安定感が増し増しでした。採用していますが、こいつはsidekiqのgod監視とやや相性が悪いです。restart時にプロセスがいない瞬間ができてしまうため、capistranoがsidekiqを起こすのとgodがsidekiqを起こすのが衝突してしまい、二重でsidekiqが起動してしまうケースが有るためです。capistranoと併用する場合は素直にcapではsidekiq:stopだけして起こすのはgodに任せたほうが良いという感覚を持っています。GitHubk8sk8sもローカル環境での動作自体はサポートしています。VPSを2台所持しているため、2台でなんとか運用できないか検討していたのですが、結局2台では割と厳しいということと、capistranoに代わるデプロイツールを自作し、メンテし続けるコストを払える自信がなかったため見送りました。一通りproductionとしてローカル環境で動作させるところまでは確認しているのでrailsでのk8sに参考にしたい方はどうぞ。GitHubただ運用もしていないコードなので穴はたくさんあると思います...BigQuerynginxのアクセスログをfluentd経由でBigQueryに投げるようにしています。SSDで容量が厳しく、割と細かめにログを削除しているため後でまとめて見るためにBigQueryを利用しています。元々はS3に置いてAthenaで集計していたのですが、BigQueryのほうがいろいろと捗りそうなので移管しました。いうプラグインを利用させていただき、bqにログを送っています。poolがタイムアウトになっていたり、急激な負荷によるアラートの調査などに使っていたりします。追加しておくことで、調査が捗ることがあるためおすすめします。nginxのログから問題のrequest_idがわかればproduction.logをrequest_idでgrepすることで該当行のログに辿り着きやすくなります。最近webpackerベースのreactにリプレースを行っていたのですが、その際に困った問題と解決した手法についても記載しておきます。targetをes5としていましたが、babel-polyfillを仕込み忘れていたことが1点目です。IEにはないため正常に動作しません。追加するのがおすすめです。ブラウザのUAを見て必要なpolyfillを行ってくれます。試しにIEで見ると結構な量をpolyfillしてくれますが、最新版のchromeではほぼ空であることが確認できると思います。自分でbundleすると配布するjsファイルが巨大化するため、cdn経由で入れることにしました。2点目はes5にトランスパイルされていないライブラリを利用してしまっていることでした。GitHubの行でアローファンクションを使っているのですが、これがIEだと解釈できないという問題です。指定されているため、使っているライブラリまではes5にトランスパイルされません。ドキュメントを見るとレガシーブラウザは5系を使え(最新は6系)ということが記載されていたので、5系を利用することで解決しました。当初は上記2点の問題がわからぬまま、IEでは見れないといった声が上がっていたため、最初の対応としてはelectronでサイトをラップしたexeを配布していました。applicationというライブラリを利用したのですが、幅広くの場面で役に立つので困ったら利用してみてください。支える技術をご紹介しました。他にもいくつかサービスがありご紹介したい技術もあったのですが、割と長くなってしまったので今回はこのような形で締めたいと思います。サービスを運用している方の参考になれば幸いです。作者は2018年5月に念願の冥ハードを成し遂げ、ゆるふわ離脱モードに突入しました。react/chef/rails/docker/aws/gcphttps://8398a7.github.ioCrieitは個人で開発中です。どんな軽い内容でも嬉しいです。